All notable changes to Cloud Health Office will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Cloud Health Office can now be deployed behind an externally managed OAuth/OIDC/ SMART authorization server without source changes or insecure issuer shortcuts.
- Explicit identity mode.
SmartAuth:ModeisDemoorExternalIssuer, stated rather than inferred. A Demo deployment on a non-development host fails startup, so production cannot silently fall back to the bundled issuer. - Multi-issuer trust registry. The issuer is resolved from the token's exact
issfirst and then supplies its own keys, audiences, algorithms, claim mapping and permitted tenants — so one trusted IdP's material never authorizes another's token. - Validated OIDC discovery and JWKS. The document's
issuermust match exactly; fetch targets are confined to configured origins (SSRF boundary), with loopback, link-local and RFC1918 addresses barred outside development. - Key rotation. Unknown
kidtriggers a rate-limited, single-flighted refresh; cached keys survive an IdP outage within a bounded staleness window and fail closed outside it. - Algorithm policy. Asymmetric only — no
alg=none, and no HMAC (a symmetric verifier accepts a token signed with the issuer's public key). - Caller identity model. One resolution per request into
AuthenticatedCaller; provider NPI is established only from a claim a trusted issuer was configured to assert. - Readiness. A
smart-identity-trusthealth check reports operational trust state without exposing keys, discovery payloads or IdP error text.
- Tenant could be named by an unauthenticated header.
X-Tenant-IDwas consulted whenever a token carried no tenant claim, so any authenticated caller whose issuer did not map a tenant could select any tenant. The header may now fill a vacuum but never contradict a token — a mismatch is a 403.X-Dev-Tenant-IDis honoured on development hosts only. - CDex submitter binding strengthened. Where a trusted issuer asserts the
caller's NPI,
$submit-attachmentcompares the token's identity against the provider the request was addressed to, closing the substitution a public-NPI corroborating key cannot detect. Deployments without a provider claim are unchanged.
Docs: docs/architecture/smart-oauth-trust.md,
docs/architecture/idp-integration-contract.md.
Cloud Health Office could receive a prior payer's Condition, Observation,
Procedure and the rest — the Payer-to-Payer pipeline validated, counted, named
and archived them — but it had nowhere to put them and no way to serve them.
They were classified Unsupported, because claiming to ingest a resource type
with no read path behind it would have been a false claim. That gap is now
closed at its root.
Twelve types, one table. AllergyIntolerance, CarePlan, CareTeam,
Condition, Device, DiagnosticReport, Goal, Immunization,
MedicationDispense, MedicationRequest, Observation, Procedure — the
USCDI clinical data classes this repository already documents as CMS-0057-F
obligations, minus the ones CHO discharges elsewhere (Patient demographics,
Coverage, CARIN EOB, DocumentReference clinical notes, Provenance as metadata).
ClinicalResourceInventory is the single source of truth, and the FHIR routes,
the SMART scope layer, the Provider Access authorization filter, the
Payer-to-Payer import classification and the CapabilityStatement all read it.
That is not tidiness: a clinical type reachable through SMART but missing from
the governed set would be readable by any provider with a scope, attributed or
not, consented or not. Structural tests pin each consumer to the table, and a
test checks every entry against the Firely search-parameter registry so the
table cannot claim a parameter FHIR R4 does not have.
The import store was promoted, not copied. The rows Patient and Provider Access read are the rows a Payer-to-Payer exchange committed — one object in DI behind two interfaces. There is no projection to fall behind, no dual write to reconcile, and no second place a resource could be stale in. Clinical data therefore stays out of CHO's authoritative member, enrollment, coverage and claim stores by construction: a prior payer's Condition cannot be read as a CHO-owned record because it does not live where CHO-owned records live. Reads return the version from the most recently committed exchange, so a staged or failed ingestion is invisible and never displaces a committed one.
Identity is derived, not allocated. A resource is served under the
deterministic import identity — SHA-256 of tenant + member + source payer +
resource type + source id. A replay updates in place instead of appearing at a
new URL; two payers' identically-numbered records are two resources; another
tenant's id resolves to nothing; and the URL leaks no member, payer or clinical
detail. Serving the payer's own Observation/123 would have collided on all
three counts.
Imported subject is data, never authorization authority. Tenant and member
come from the exchange context CHO drove, so a package whose Observation names
another member is filed under the member CHO resolved — and served that way
too, with the subject element rewritten to the trusted binding on the way out.
Both authorization boundaries, unchanged in shape. Patient Access requires a
patient-context token whose binding is now enforced against both
member-naming search parameters: patient and subject. Checking only the
first would have left the second as an unguarded way to ask for someone else's
record, and subject is exactly what the clinical types added. Provider Access
keeps all four CONSENT-01 controls — authentication, SMART scope, attribution,
active ProviderAccess-purpose consent — because the clinical types are in the
same globally registered filter's governed set and there is exactly one clinical
controller. A Payer-to-Payer consent still opens nothing here.
Knowing an id is not authority to read it. Every store call is keyed on the tenant and the authorized member, so another member's resource is never selected rather than filtered out afterwards. A provider reading by id must name the member, because Provider Access authorizes a member and not an id. "Not yours", "not there" and "another tenant's" return one identical 404 — telling them apart is what enumeration needs — with the category kept in a PHI-free audit line carrying ids, an outcome and a count, and no field a value, diagnosis, medication name or narrative could live in.
Ingestion gains a payload gate and keeps its honesty. Clinical resources pass a type / source-id / size / nesting check before they become readable PHI, using the parser this service already owns rather than a second one. A refusal is per resource, counted and named by reason — one oversized Observation does not cost the member the rest of their history — and the package stays archived verbatim. Types still outside the inventory are still named, counted and archived, never dropped.
Existing history becomes visible without re-running an exchange. Clinical
data CHO already holds from exchanges committed before this change lives only in
the archived package. ClinicalBackfillService re-projects it under exactly the
identities a real import would have produced: deterministic, replay-safe,
tenant-safe (binding from the ledger entry, never the Bundle), committed-only,
non-destructive, gated by the same validator, disabled by default with a dry-run
mode. Asking a prior payer again for data CHO already has would be impossible
once that relationship has ended.
Stated, not glossed. No US Core profile is claimed: no meta.profile, no
supportedProfile. CHO serves these as valid FHIR R4 and does not re-shape a
prior payer's content to satisfy US Core invariants, so a profile URL would be a
label rather than conformance. Search is read plus _id, patient and
subject where R4 defines it — no category, code, status or date search.
No CHO component authors native clinical data yet, so every served resource is
imported; the source axis exists so native data will coexist rather than
overwrite, but it is an unexercised seam.
meta.source names the originating payer and the source resource id,
meta.versionId is a content hash, so imported data is never indistinguishable
from CHO-authored data.
PAT-02 moves PARTIAL → PASSABLE, and the CMS-0057-F evidence generator computes CHO Replace at 20 PASSABLE / 1 PARTIAL / 0 GAP from the manifest — nothing is hard-coded. The one remaining PARTIAL is SEC-01, where the identity provider is configured per engagement.
Design: docs/architecture/clinical-fhir.md.
A prior authorization pended for additional information stopped at an A4 status. Cloud Health Office could say that more information was needed and could not say what, could not hand the request to the provider, and had no way to take the answer back. That round trip now exists, end to end, on one durable record.
Which CDex interaction. CHO is the payer, so the exchange it needs is the
solicited one: the CDex Task Attachment Request profile for the request half
(GET fhir/r4/Task/{id}, or by identifier / focus=Claim/{authNumber}) and
the $submit-attachment operation for the response half
(POST fhir/r4/$submit-attachment). CDex's Task Data Request profile — a payer
querying a provider's clinical record — is a different transaction and is
deliberately not what a pended prior authorization uses.
One record, extended rather than duplicated. rfai-service already held
RfaiCase, so that is the additional-information aggregate: no second store, and
no second lifecycle. The FHIR Task is a projection computed on read, and
authorization-service keeps only the handle (Authorization.RFAIReference). The
correlation chain is tenant → authorization → request → response → stored
artifact.
An A4 status alone does not raise a request. PendedAuthorizationRfaiCoordinator
raises one only when the review decision is A4 and the decision names what
documentation is wanted. A pend that asks the provider for nothing has not asked
them anything, and manufacturing a request from it would put a question to the
provider that no reviewer posed. A1/A2/A3 and a plain InReview state raise nothing
either — all asserted.
Structured, not prose. Each requested item carries the X12 PWK code and the
LOINC code (the same request has to be expressible on both wires), the service
line it is about, diagnosis context (on a CHO-owned Task.input type, since CDex
has no code for it and typing a diagnosis as attachment-code would make it read
as a document being requested), and whether it is required; the case carries
a due date and a coded reason. Free text supplements the codes and never replaces
them, and a request with no items is refused at creation.
Idempotency is in the primary key, not in a read-then-write. The case's document id is derived from tenant + authorization + a digest of the decision, so two workers racing on one A4 event address the same document and exactly one conditional insert wins (Cosmos 409 / Mongo duplicate key); the loser reads back the winner's case. A redelivered event replays onto the request the first delivery created whatever status it has since reached — otherwise a redelivery after the cycle closed would open a second one. At most one cycle is open per authorization, and a later cycle is a new record with the next sequence, so earlier request/response evidence is never overwritten.
A submission is bound to its request, not to an authorization number. Tenant
(from the authenticated context, never the payload), the payer-issued tracking id,
the authorization named in AttachTo, and — where the request records one — the
submitting provider's NPI must all agree. Knowing an authorization number attaches
nothing. Submission identity is content-derived (tenant + request + tracking id +
SHA-256 of the bytes), so a retry records nothing twice while a materially
different document is appended as an additional response rather than
overwriting the first.
Payload policy is enforced before anything is stored, all-or-nothing per call:
a content-type allow-list (PDF, images, plain/RTF text, XML, C-CDA, FHIR JSON),
20 MB per attachment, 50 MB and 10 attachments per call, 25 artifacts per request
enforced by the aggregate itself, caller-supplied Attachment.url refused rather
than fetched, and titles kept as sanitised metadata. Storage keys are derived
entirely from server-side values through the platform's existing
IClaimAttachmentContentStore; the case record keeps a pointer and a hash, never
content, and no clinical payload reaches a log or an audit event.
Receiving documents returns the authorization to review — never approves it.
The resume-review announcement is raised only on the transition into
DocsReceived, so a replay cannot restart the decision clock twice; a partial
delivery records the arrival but leaves the status pended; and a decided
authorization is not reopened by documents arriving late. An acceptance test
asserts explicitly that documentation submission does not imply approval.
Claim/$inquire follows the lifecycle — pended-additional-information with the
X12 A4 reviewAction before, plain pending after — so it is never left
permanently reporting A4 once the data is in.
A security hole this surfaced and closed — read scopes no longer authorize
writes. SmartScopeEnforcementMiddleware derived the required scope from the
resource-type path segment and always demanded .read. Two consequences: a
system-level operation path — /fhir/r4/$submit-attachment — names no resource
type and fell through the "unknown path" branch entirely unenforced; and
every write reachable under /fhir/r4, POST Claim/$submit included, was
authorized by a read scope.
A request is now resolved into the interaction it actually is — which resource's
scope governs it, whether it reads or writes, and which scope contexts may invoke
it — from the path and the method. FHIR operations are classified explicitly,
because an operation's HTTP method says nothing about its effect ($inquire and
$member-match are POSTs that read); plain REST follows the method. Nothing
falls through: an operation nobody classified is still governed by a scope named
for the operation, with the access its method implies. $submit-attachment
additionally requires a user/ or system/ context — a patient-context token is
not an acceptable caller for a provider/payer transaction.
Behaviour change: Claim/$submit, $cho-appeal-submit,
PayerToPayer/$initiate and DTR questionnaire authoring now require a write
scope where a read scope previously sufficed. smart-auth-service therefore issues
write scopes (user/system × *, Claim, Task, Questionnaire,
QuestionnaireResponse) and .well-known/smart-configuration advertises them —
without that the CDex response half would have been ungrantable, since CHO issued
no write scope at all. There is deliberately no patient-context write scope. rfai-service's legacy by-auth/{tenantId}/… route now honours a path
tenant only when it matches the authenticated one, instead of selecting on it.
Anti-enumeration. Unknown tracking id, other tenant, other authorization and other provider all return one identical 404, with the distinguishing category kept in a PHI-free audit line. Request-shape defects are described plainly as 400 because they say nothing about what exists, payload defects as 422, and a fully correlated but closed request as 409 — the caller has already proven it is theirs.
Deliberately not Provider Access consent. That gate governs a provider reading a member's clinical record; this is a payer/provider transaction about the submitter's own prior-authorization request. The separation introduced with the shared consent registry is preserved rather than borrowed from.
Known limitations, named rather than left absent. The submitter is bound by the
tracking id and the corroborating provider NPI rather than by the caller's own
identity, because this repository has no token-subject-to-NPI mapping (the same
documented limitation as $inquire). The payer makes the request available for
retrieval rather than pushing a Task to a provider endpoint — there is no provider
FHIR endpoint registry here. fhir-service registers the in-process attachment
content store by default and says so at startup, so a deployment must bind a
durable implementation before submitted documentation survives a restart. Malware
scanning is a registered seam with no scanner behind it; unscanned content is
recorded with scan status Unknown, never Safe. Expiry is derived from the due
date rather than swept. And there is no outbox: a failure between recording the
decision and raising the request leaves the authorization pended with
RFAIIssued = false — the recoverable state, because the retry carries the same
correlation key and cannot duplicate.
One existing test changed, with cause. RfaiDocsReceivedConsumerTests
asserted that a partial delivery wrote nothing at all. Documents reaching the
payer is a fact about the authorization whether or not the request is complete, so
the first arrival is now stamped on RFAIResponseDate; the assertion that matters
— the status stays pended and the decision clock stays stopped — is unchanged and
sharpened. rfai-service and a new RfaiService.Tests were also added to the
solution, which did not previously build them.
Design: docs/architecture/cdex-additional-information.md.
Acceptance 317 passed (+63), rfai-service 37 (new), fhir-service 458, SmartAuth 59, authorization-service 26 + 38, evidence 32; all 0 failed. PAS-07 PARTIAL → PASSABLE on 41 supporting tests, so CHO Replace is 19 PASSABLE / 2 PARTIAL / 0 GAP (generator-computed). Remaining PARTIAL: PAT-02, SEC-01.
Prior-authorization state was persisted and queryable but had no retention lifecycle: nothing said how long a record must be kept, nothing expired one, and nothing could prove a record had not been deleted early. That is now an explicit, testable rule with a production sweeper applying it.
The rule, in one place. IPriorAuthorizationRetentionPolicy is pure and
deterministic — it reads an authorization and a clock and answers three
questions. RetentionUntil = last status change + retention period, and a record
is purgeable only when it is both operationally terminal and past that
boundary.
The period is floored, not merely configured. CMS-0057-F states a minimum — retain for at least one year after the last status change — not a maximum. One year is enforced as a floor configuration cannot go under, and the default is six years, matching the HIPAA posture already used for member documents. Defaulting a destructive job to the bare regulatory minimum would quietly make prior-auth the shortest-retained regulated data in the platform.
The anchor is a lifecycle fact, never a read. StatusHistory.ChangedAt
(max), falling back to ReviewedDate then SubmittedDate — deliberately not
LastUpdatedDate, which every write touches, so an unrelated edit cannot move
the boundary. An inquiry cannot extend a record's regulatory retention, and a
test drives five reads then asserts the purge still happens exactly when
originally due. A record with no establishable anchor is kept, not purged.
Open authorizations are never purged, however old their timestamps: a pended
decision may still be waiting on information no matter how long it has waited.
The open/terminal split was previously spelled out separately in the SLA watchdog,
the RFAI consumer and both repositories; it is now defined once as
AuthorizationStatus.IsOpen()/IsTerminal() and is total over the enum.
The sweeper is modelled on IntegrityProjectionWorker, deliberately not on
this service's SlaWatchdogService — which captures a scoped repository for
the process lifetime and calls a tenant-less repository method from a background
thread, so it throws on every tick. PriorAuthorizationRetentionWorker takes a
scope per tenant from IServiceScopeFactory, passes the tenant explicitly to
every repository call (no ambient HttpContext), bounds work per tenant per
sweep, observes cancellation between every record, is disabled by default, and
supports a dry run.
Purge is a conditional delete, so the check and the delete are one operation:
Mongo filters on id + tenant + expected status in a single DeleteOne; Cosmos
re-reads inside the purge and carries that read's ETag into DeleteItemAsync as
IfMatchEtag. A record that reopens between being listed and being purged
survives — the predicate no longer matches. Repeated sweeps are no-ops, an
already-purged record is not an error, and a failed batch simply retries.
Deletion is hard, and coherent. The Authorization document embeds its
diagnoses, requested service lines, attachment metadata and status history, so
removing it removes them; the PAS ClaimResponse is projected on read rather
than stored, so no FHIR artifact survives. Nothing is kept behind a soft-delete
flag while claiming to be purged. Two dependencies are named as out of scope
rather than half-handled: blobs behind ClinicalAttachment.FileUrl, and
rfai-service RfaiCase records, whose repository has no delete path at all.
Tenant-safe throughout. New repository methods (ListTenantIdsAsync,
FindRetentionCandidatesAsync, PurgeIfStillEligibleAsync) take the tenant as
an argument and a CancellationToken — the first on this interface to do either.
A purge naming the wrong tenant refuses outright.
Audit is aggregate. Sweeps report counts; per-record purge lines carry
tenant, opaque authorization id, policy version, retention boundary and status —
never a member, payload, narrative or credential, with CR/LF stripped (CWE-117).
cho.authorization.retention.outcomes.total is dimensioned by outcome, dry-run
and tenant.
The freshness half of PAT-03 needed no job. "Update PA data within 1 business day" bounds how stale a copy may be; CHO keeps no copy. PA state is projected from the authoritative record at read time, so the interval between a status change and its visibility is zero. That is structural rather than incidental because the read seam exposes a single lookup and no write, so no cached or replicated projection can exist behind it to drift — asserted by test. The absence of a freshness job is the design, not a gap.
Acceptance: PAT-03 moves PARTIAL → PASSABLE, so CHO Replace declares 18 PASSABLE / 3 PARTIAL / 0 GAP — computed by the evidence generator, not hard-coded. Remaining PARTIAL: PAS-07, PAT-02, SEC-01. PAT-02 and PAS-07 are untouched by this work. Zero GAPs is still not complete CMS-0057-F compliance.
Documentation: a Data retention section in
docs/architecture/prior-authorization.md; updated
docs/compliance/CMS0057-ACCEPTANCE-INVENTORY.md.
Prior-authorization state was persisted and retrievable, but only through
authorization-service's own REST surface. The standards-facing FHIR operation did
not exist, which is what kept PAS-04 PARTIAL. POST fhir/r4/Claim/$inquire now
serves it.
One record, projected — not a second model. The inquiry reads the same
authorization record $submit writes and the rest of the platform updates, over
the read endpoint authorization-service already exposes. There is no
inquiry-specific store and no second status field. PriorAuthorizationRecord is
a deliberately narrow read projection: the fields an inquiry does not need —
patient name, date of birth, clinical attachments, reviewer, notes — have no
property to land in, so they cannot leak into a response or a log.
Standards shape, not a CHO wire format. The request is a PAS Bundle carrying
a Claim with use = preauthorization, whose identifier (or
insurance.preAuthRef) names the authorization. The response is a Bundle
carrying a ClaimResponse on the Da Vinci PAS profile, built by the same
PasResponseBuilder that serves $submit.
The status mapping is deterministic and total over CHO's authorization
states: Submitted/InReview → queued pending; Pended/A4 → queued
pended-additional-information with the X12 306 reviewAction; Approved/A1 →
complete approved; Modified/A2 → partial modified; Denied/A3 → complete
denied with the coded reason; Expired → complete expired; Cancelled →
cancelled. outcome carries the coarse answer and disposition the specific
one, so a caller can distinguish pending from pended-for-information from
approved from denied. An unrecognised status reads as still in progress rather
than as an approval CHO cannot vouch for.
Read-only by contract. IPriorAuthorizationStore exposes one lookup method
and no write method at all — asserted structurally, so an inquiry cannot create a
record, move a status, restart a decision clock, or trigger a payer submission
however often it is repeated. Status comes from the stored adjudication record,
so an inquiry never becomes an outbound X12 transaction.
Freshness. Every inquiry reads live committed state, so a status changed after submission is the status returned; tests drive pended → approved and assert the second inquiry reflects it.
Lookup cannot be guessed into. An authorization number alone is never sufficient — a corroborating member or provider key must accompany it and match the record, and a supplied key that does not match refuses even when another does. Tenant comes from the authenticated context and is re-checked on the record itself, so it holds even if header propagation is lost.
Anti-enumeration, without swallowing honest errors. Request-shape defects —
no authorization identifier, no corroborating key — return 400 naming what is
missing, because they say nothing about what exists; telling a caller who forgot
an identifier that their authorization "does not exist" would be both wrong and
unhelpful. Every refusal about a record — unknown, wrong tenant, not the
caller's — returns one identical 404 OperationOutcome, and a structural test
pins that classification so a newly added outcome cannot quietly become
distinguishable. The category is kept in a PHI-free audit line carrying tenant,
caller, authorization number, outcome and status only.
Thin controller. The action routes and maps to HTTP; lifting the lookup keys out of the inquiry Claim belongs to the service, since which element carries which key is a property of the PAS request shape rather than of HTTP.
Authorization controls. Authentication ([Authorize]), the SMART
*/Claim.read scope, and tenant from context — the same controls $submit has.
Deliberately not routed through the Provider Access consent gate: that gate
governs a provider reading a member's clinical record, whereas PAS is a
system-to-system transaction between the submitter and the payer about the
submitter's own request. The corroborating key, not a member consent, is what
binds an inquiry to its authorization.
Making status inquirable required fixing the write side. preAuthRef was set
only on approvals, and a pended submission persisted an authorization number that
was never returned to the caller — so the outcome that most needs following up
was un-inquirable. Approved, denied and pended responses now all carry the
number that was persisted. The denial code and reason, the approved period, and
the service lines from the submitted Claim are now persisted too, so an inquiry
can answer why and for what rather than just "denied". The authorization HTTP
client now propagates the tenant header — without it authorization-service falls
back to its default partition, and reads and writes would have crossed tenants.
CapabilityStatement. Claim now advertises both submit and inquire with
their Da Vinci PAS OperationDefinition canonicals, pinned by test to the routes
actually served. $submit had never been advertised.
Acceptance: PAS-04 moves PARTIAL → PASSABLE, so CHO Replace declares
17 PASSABLE / 4 PARTIAL / 0 GAP — computed by the evidence generator, not
hard-coded. PAS-07 stays PARTIAL: $inquire reports a pended-for-
information decision, which CHO already knows from the A4 review decision, but it
neither requests documentation nor accepts it — that round-trip is what CDex is,
and it is not implemented. Remaining PARTIAL: PAS-07, PAT-02, PAT-03, SEC-01.
PAS-01/02/03/05/06/08 are unchanged and green. Zero GAPs is still not complete
CMS-0057-F compliance.
New documentation: docs/architecture/prior-authorization.md. Updated:
docs/compliance/CMS0057-ACCEPTANCE-INVENTORY.md, docs/diligence/ADAPTER-STATUS.md.
Payer-to-Payer already ran on a purpose-scoped consent decision; Provider Access
did not. A provider-shaped token (user/… or system/…) that passed the SMART
scope check could read any member's record — the acceptance suite even
asserted that as expected behaviour. Provider Access now composes four
independent, mandatory controls, and the consent one runs on the same registry
and the same policy as Payer-to-Payer.
Four controls, none implying another. Authentication and SMART scope stay
where they are (middleware — token validation is not moved or re-implemented).
On top of them ProviderAccessAuthorizationService adds provider/member
attribution and an active ConsentPurposeOfUse.ProviderAccess consent. A correct
scope implies neither attribution nor consent; attribution does not imply
consent; consent does not imply attribution; and a Payer-to-Payer consent
authorizes nothing here. The composed decision fails closed — any one refusal
denies, and so do a missing tenant, a missing member context, an unidentified
caller, and an unreadable registry.
One registry, one policy, now shared by construction. A new
IConsentEvaluator owns the fail-closed registry read and delegates every
lifecycle and purpose rule to the existing pure ConsentAuthorizationPolicy.
Payer-to-Payer's gate was refactored onto it rather than keeping its own copy, so
the two capabilities cannot drift: they differ only in the purpose they ask for.
IConsentSource generalises the registry seam that #1152 introduced — the same
HttpConsentRegistryConsentSource against consent-service serves both, and no
second consent store exists.
Enforced at one shared boundary, before any PHI is read.
ProviderAccessAuthorizationFilter is a global MVC action filter, not a
per-controller attribute, so a new member-scoped controller is governed the
moment it exists. A filter rather than middleware because Provider Access needs
the tenant and TenantMiddleware runs after SmartScopeEnforcementMiddleware;
a filter runs after the whole pipeline yet still before any action body. It
governs every member-scoped resource the SMART layer serves — Patient,
Coverage, ExplanationOfBenefit, Encounter, Claim, Task,
Communication, DocumentReference, ClaimResponse — and a structural test
pins that inventory to the SMART layer's own list so a resource cannot escape one
by being forgotten in the other. FHIR operations are deliberately excluded:
$member-match and $member-data-export have their own Payer-to-Payer
authorization, and an operation name is not a member id.
Provider Access is a caller shape, not a route name. A user/- or
system/-scoped token is a provider reading someone else's record. A
patient-scoped token is Patient Access — the member reading their own data —
which a Provider Access consent does not govern and is not required for. The
distinction comes from the token, so it cannot be lost by adding an endpoint.
Member context is required, not guessed. Patient/{id} names the member;
otherwise it comes from ?patient= or the SMART binding. A resource id is never
resolved to a member, because resolving it means reading the resource being
authorized. No member context denies — which is why a provider-shaped search
across the whole membership is now refused rather than returning every member.
Refusals cannot be used to enumerate. "Not attributed", "no consent" and "no
such member" return one identical 403 FHIR OperationOutcome; a test asserts
the bodies are byte-identical. The structured category lives in the audit record
instead. Decisions are audited with PHI-free identifiers only — tenant, member
id, caller id, resource type, consent id, category, instant — never
demographics, clinical payloads, consent narrative, or credentials, with CR/LF
stripped from ids (CWE-117).
Attribution, stated honestly. The repository had no attribution code at
all: PROV-02's "attribution enforcement" test asserted a dictionary miss on an
unknown id, and the capability text describing Provider Access as "governed by
attribution plus SMART scopes" was aspirational. Attribution is now a real,
enforced control backed by a configured panel catalog
(Cms0057:ProviderAttribution) that fails closed on an empty catalog — but no
live roster feed from a payer source system is wired up, and nothing claims one
is. That remains engagement integration behind IProviderAttributionSource.
Two existing tests asserted the hole and were rewritten rather than deleted:
EobSearch_UserToken_CanSearchAnyPatient (a provider token reading any patient)
and PatientSearch_SystemToken_NoPatientBinding (a backend token listing the
whole membership) now assert the refusals, alongside new tests for the authorized
paths.
Acceptance: CONSENT-01 moves PARTIAL → PASSABLE, so CHO Replace declares 16 PASSABLE / 5 PARTIAL / 0 GAP — computed by the evidence generator from the manifest, not hard-coded. Payer-to-Payer behaviour is unchanged and its suite re-runs green: a Provider Access consent still authorizes no exchange. Remaining PARTIAL: PAS-04, PAS-07, PAT-02, PAT-03, SEC-01. Zero GAPs is still not complete CMS-0057-F compliance; this is implementation evidence, not certification, and the QNXT/external-core column is unchanged.
New documentation: docs/architecture/provider-access.md. Updated:
docs/architecture/consent.md, docs/compliance/CMS0057-ACCEPTANCE-INVENTORY.md,
docs/diligence/ADAPTER-STATUS.md.
Payer-to-Payer authorization was a generic Active consent: any active record for the member let an exchange proceed. It is now a first-class, purpose-scoped decision on the same registry.
A purpose axis, not a new consent type. ConsentPurposeOfUse
(Unspecified / PayerToPayerExchange / ProviderAccess) says what a consent
authorizes the plan to do, orthogonal to ConsentType, which says what
regulatory instrument the record is. A §164.508 authorization for
Payer-to-Payer and one for Provider Access are the same instrument and different
permissions, and a sensitive-category authorization can be purpose-scoped too.
The axis follows FHIR Consent.provision.purpose (HL7 v3 PurposeOfUse), so the
record projects onto FHIR Consent when that projection lands.
One registry, one policy. consent-service remains the authoritative store —
no second consent collection was added. The new
CloudHealthOffice.Consent.Contracts project carries a PHI-free
ConsentAuthorizationSnapshot (tenant, member, id, purpose, status, period,
version — no narrative fields, and no field to put them in) and
ConsentAuthorizationPolicy.Evaluate, a pure function that is the single place
any purpose-scoped authorization is decided. A snapshot must match the tenant
and the member, carry the requested purpose, be Active, and be in force at
the evaluation instant — the effective period is applied by the policy rather
than trusted from the stored status, so a record persisted as Active past its
ExpiresAt still denies. Ties resolve deterministically (latest-expiring,
unbounded first, then highest version), so two evaluations of the same registry
state name the same consent.
Refusals say which refusal. NoConsentOnRecord, NoConsentForPurpose,
NotActivated, Revoked, Expired, NotYetEffective — reported
most-specific-first, because "they revoked it", "it lapsed", and "they never
granted this purpose" are different operational facts.
Enforced server-side, identically in both directions. Inbound
(PayerToPayerExchangeService) evaluates before any member data is assembled.
Outbound (PayerToPayerOutboundService) evaluates twice: before the remote
$member-match, so an unauthorized member's identity never leaves CHO at all,
and again immediately before the export, so a revocation landing while the
match is in flight stops the data request. Both call one
IPayerToPayerConsentGate over one policy against one registry, so responding
cannot drift more permissive than initiating. No Payer-to-Payer request type has
a consent field in either direction — an acceptance test asserts that by
reflection over all four request types, so neither a peer payer nor an internal
caller can self-attest.
Provider Access separation is structural. A member with an Active
ProviderAccess consent and nothing else is denied for Payer-to-Payer with
NoConsentForPurpose. The purposes are compared as data inside the policy;
nothing about the calling controller or route participates.
Exchanges record what authorized them. AuthorizingConsentId,
ConsentDecisionReason, and ConsentEvaluatedAtUtc are written on the outbound
exchange, and the consent id and reason on both audit entries. A retry clears
them so it re-asks rather than reusing an earlier answer.
Fail-closed at every edge. Blank tenant or member, an unreadable or
unreachable registry, a source that throws, an empty catalog, and Unspecified
purpose all deny. Consent-lookup failures log a category only, never registry
detail.
New consent-service surface: PurposeOfUse on Consent (optional on create) and
GET api/v1/members/{memberId}/consents/authorization-snapshots?purposeOfUse=,
a PHI-free projection so another service can authorize without reading consent
narrative. fhir-service reads it through HttpConsentRegistryConsentSource
when Services:ConsentServiceUrl is configured; the configuration-backed source
remains the Demo/test fallback in the same shape (purpose, status, period), so
Demo exercises the real policy instead of a boolean allow-list.
Migration is explicit and fails closed. Records written before PurposeOfUse
existed deserialize to Unspecified and authorize nothing purpose-specific.
There is no backfill, no inference from ConsentType, and no "treat Active as
P2P" fallback. The practical consequence is deliberate: a deployment upgrading
to this code authorizes zero Payer-to-Payer exchanges until members' consents are
recorded with PurposeOfUse = PayerToPayerExchange. Unchanged: ConsentType,
the state machine and its transitions, the encrypted narrative fields, and every
existing endpoint's contract (the consent event payload gains a purposeOfUse
field).
Acceptance: P2P-03 moves PARTIAL → PASSABLE, so CHO Replace declares 15 PASSABLE / 6 PARTIAL / 0 GAP — computed by the evidence generator from the manifest, not hard-coded. CONSENT-01 stays PARTIAL with a new GAP test naming the reason: the registry can now express a Provider Access purpose, but the Provider Access read path does not consult it (attribution plus SMART scopes govern that path), so CONSENT-01 does not ride on the Payer-to-Payer work. The QNXT/external-core column is unchanged — P2P-03 augment stays GAP. Zero GAPs is still not complete CMS-0057-F compliance; this is implementation evidence, not certification or attestation.
New documentation: docs/architecture/consent.md. Updated:
docs/architecture/payer-to-payer.md (Consent section, order of operations),
docs/compliance/CMS0057-ACCEPTANCE-INVENTORY.md.
The outbound Payer-to-Payer exchange previously stopped at a validated Bundle: CHO retrieved another payer's member-scoped package, checked it, stamped provenance, audited it — and kept nothing. A successful exchange now produces a durable, tenant-safe, member-scoped, provenance-preserving CHO record.
New fhir-service code: PayerToPayerPackageIngestionService (application
service) receives an ALREADY VALIDATED package from PayerToPayerOutboundService
and never contacts a payer itself — the orchestration and transport added for
P2P-02 are unchanged, and no second Payer-to-Payer wire format exists. It
classifies each resource, normalizes intra-package references, stages every row
under a deterministic import key, and commits.
Imported data is kept apart from CHO-authoritative data.
IPayerToPayerImportRepository (MongoDB when MongoDb:ConnectionString is set,
in-process otherwise — the same fallback DtrService uses) is a separate store
from CHO's member, enrollment, claim, and provider records. Source ownership is
structural rather than conventional: an imported row cannot be read as a CHO-owned
record, so a remote Patient never replaces CHO's member identity and a prior
payer's Coverage never touches current enrollment. Both are stored as
reference-only administrative context, keeping the peer's resource id while filed
under CHO's own member.
Supported types are the ones CHO actually serves, not the CMS wish list:
ExplanationOfBenefit, Claim, ClaimResponse, Encounter, and
DocumentReference are ingested as member history; Patient, Coverage,
Organization, Practitioner, PractitionerRole, and Provenance are stored as
reference-only. Everything else — Condition, Observation, and the rest of the
USCDI clinical set — is named and counted on the exchange and preserved in an
archived copy of the package, never silently dropped and never claimed as
ingested.
Replay-safe by construction. The import key is a hash of
tenant + local member + source payer + resource type + source resource id, joined
with a separator that cannot occur in an identifier. Replaying a package lands on
the same rows instead of doubling a member's history; the same source id from a
different payer is a different key, so two payers' records are never merged; a
content hash tells "same again" from "changed"; and each exchange's own
Provenance stamp stays its own record so it remains clear which exchange
delivered what.
Atomic enough to be safe. Rows are versioned by exchange — identified by
(tenant, exchange, import key) — and reads return the version from the most
recently committed exchange. Staging writes only that exchange's own rows and
committing is a single-document ledger write, so a failed ingestion both adds
nothing visible AND takes nothing away: it cannot overwrite or hide the version an
earlier exchange committed, and an updated resource supersedes the older one only
once the exchange carrying it commits. A retry re-stages the same deterministic
keys and commits; an exchange abandoned in a non-terminal state is taken over
once stale, so a process that dies mid-ingestion cannot strand a coverage
transition. The
exchange gained DataReceived and Ingesting states plus structured ingestion
fields (status, failure category, persisted / duplicate / administrative /
unsupported counts with the unsupported types named, and start/finish timestamps);
Completed is now reachable only after the commit lands, so a package that
was retrieved but not stored is never reported as success.
References are rewritten only when they resolve to another resource in the
same package — relative, absolute, and versioned forms all resolve to CHO's
imported identity, while references to resources the peer did not send, contained
(#…) references, and urn:uuid forms are left exactly as they arrived. CHO does
not invent links the source payer never asserted, and an absolute URL does not
survive as a live pointer at the peer.
Tenant, member, and source payer on every stored row come from the validated
exchange context, never from the peer's Bundle: a package whose resources name
another tenant or member changes nothing about where its data is filed. Logs and
audit carry ids, categories, and counts only — no Bundle bodies, demographics,
clinical payloads, or endpoint URLs. Real tests
(PayerToPayerIngestionTests, [Trait("Backend","Replace")], 15 scenarios, plus
PayerToPayerImportPolicyTests / PayerToPayerReferenceNormalizerTests, 36
cases, plus PayerToPayerOutboundControllerTests) drive the production path:
durable persistence with correct binding, provenance retention, administrative
ownership, unsupported-type handling, replay and cross-payer non-merging, staging
and commit failure, a failed later exchange not hiding committed history, retry
and stale-exchange takeover, tenant and member safety, reference resolution, and
per-failure HTTP mapping.
No acceptance scenario status changed. P2P-02 was already PASSABLE and its
rationale is updated; P2P-03 stays PARTIAL (no dedicated Payer-to-Payer
ConsentType), PAT-02 stays PARTIAL (USCDI clinical types are archived, not
served), and PAT-03 stays PARTIAL (no retention job). CHO Replace remains
14 PASSABLE / 7 PARTIAL / 0 GAP, which is not full CMS-0057-F compliance,
completeness, or certification. Imported data is durable but not yet projected
into CHO's FHIR read APIs; payer onboarding (SMART Backend Services / UDAP,
mTLS) remains deployment integration; and QNXT/external-core Payer-to-Payer
integration remains GAP. New architecture documentation:
docs/architecture/payer-to-payer.md.
Closed CMS-0057-F acceptance gap P2P-02 (Payer-to-Payer outbound initiation) as real Cloud Health Office Replace-mode capability: on an authorized coverage transition, CHO — the member's new payer — initiates the exchange against the member's prior payer, rather than only answering other payers' requests. It orchestrates the existing P2P primitives instead of duplicating them (P2P-01 respond semantics for the data request, P2P-04 coverage selection for the local prior-coverage context) and adds no second wire format.
New fhir-service code: PayerToPayerOutboundService (application service) drives
the workflow fail-closed and in order — tenant scope, member + prior-payer
coverage context from CHO-owned data, target-payer endpoint resolution,
server-side opt-in, remote Patient/$member-match, member-data export, response
validation, provenance, audit, exchange state. A thin
PayerToPayerOutboundController (POST fhir/r4/PayerToPayer/$initiate, under
the SMART-enforced surface, tenant from the authenticated context) only routes;
no outbound logic lives in it.
Endpoint resolution is the SSRF boundary. IPayerToPayerEndpointResolver
resolves a payer id — never a caller-supplied URL — against a tenant-scoped
configuration directory (Cms0057:PayerToPayerOutbound), and fails closed: an
unknown payer, a duplicate entry, a non-absolute or non-HTTPS base URL (plain
HTTP only under an explicit development flag, with a warning), or a URL carrying
user info, a query, or a fragment resolves to nothing. The outbound request and
its DTO carry no URL/endpoint field at all. HttpPayerToPayerRemoteClient uses a
named HttpClient with redirects disabled (a peer cannot bounce CHO onto another
host), a response-size cap, unchanged TLS validation, and no logging of payloads,
demographics, credentials, or endpoint URLs — log lines identify a peer by its
opaque directory key.
Authorization is server-side and enforced before anything leaves CHO. The
existing IPayerToPayerConsentGate decides the member's opt-in; there is no
caller-supplied consent field, and an unauthorized member's identity is never
disclosed to a remote payer (not even in a member-match). The remote match sends
only what the operation needs — the member's identifier with that payer (from
CHO's own coverage record) plus family name and birth date; no SSN, address,
phone, or email. Export is requested only after the peer resolves exactly one
member, and the returned FHIR Bundle is parsed and checked for member consistency
(single matched Patient, no foreign Patient/… reference) before acceptance;
anything unparseable, empty, or inconsistent is rejected whole. Accepted packages
are stamped with a Provenance naming the source payer, so another payer's data
is never mistaken for CHO-originated.
Outcomes are structured, not free text (TargetPayerNotConfigured,
NotAuthorized, LocalCoverageAmbiguous, MemberNoMatch, MemberAmbiguous,
RemoteUnauthorized, RemoteUnavailable, InvalidRemoteResponse) and are
recorded on a PayerToPayerOutboundExchange with an idempotency key
(tenant | member | target payer | transition), so a repeated initiation replays
one exchange and a retry after a failure resumes it. Audit carries tenant,
member, target payer, endpoint key, exchange id, outcome, and resource count —
no demographics, payload, URL, or credential. Real acceptance tests
(PayerToPayerOutboundTests, [Trait("Backend","Replace")], 24 scenarios) drive
the production orchestration with only the far side of the wire faked, asserting
call ordering and request content, missing consent, unconfigured/non-HTTPS payer,
no-match, ambiguous match, remote auth/transport failure, malformed and
cross-member packages, cross-tenant refusal, overlapping local coverage, and
idempotent retry. HttpPayerToPayerRemoteClientTests pins the transport seam's
own contract: peer status → structured outcome (422 is the anti-enumeration
no-match signal; a 404 is a route/configuration error, not a member no-match),
calls only to the resolved endpoint URIs, no fabricated Authorization header, and
an oversized or empty body refused rather than buffered.
Scope, stated plainly. P2P-02 CHO Replace moves GAP → PASSABLE and CHO
Replace now declares 14 PASSABLE / 7 PARTIAL / 0 GAP — which is not full
CMS-0057-F compliance, completeness, or certification. P2P-03 remains
PARTIAL: opt-in is still a generic Active consent with no dedicated
Payer-to-Payer ConsentType. Received packages are retrieved, validated, and
audited but not ingested into the CHO member record; exchange state lives in
an in-process store; and connecting to any named payer needs that payer's
onboarding — a directory entry plus transport credentials (SMART Backend
Services / UDAP client registration, mTLS) behind
IPayerToPayerCredentialProvider, which supplies none by default rather than
fabricating one. QNXT/external-core P2P integration (including outbound
initiation from a QNXT-backed deployment) remains GAP.
Closed CMS-0057-F acceptance gap P2P-04 (Payer-to-Payer member-match /
concurrent coverage) with the FHIR Patient/$member-match operation as real
Cloud Health Office Replace-mode capability — cross-payer identity resolution
over CHO-owned data, distinct from the P2P-01 known-member respond. New
fhir-service code: PayerToPayerMemberMatchService (application service) resolves
the transitioning member within the tenant from normalized identity attributes
and returns the relevant member + coverage context; a thin
PayerToPayerMemberMatchController (POST fhir/r4/Patient/$member-match, under
the SMART-enforced surface, tenant from the authenticated context) only routes.
Matching is deterministic and fail-safe (MemberMatchPolicy): a positive
assertion needs a strong identifier (member/subscriber id or SSN) or the
family-name + birth-date pair, and any contradicting attribute — wrong DOB, wrong
member id, different sex — fails the candidate closed. Zero candidates → no match,
more than one → ambiguous, cross-tenant → never visible, and a weak single
attribute is refused before any search (anti-enumeration). MemberIdentityNormalizer
makes equivalent formatting (casing, whitespace, accents, phone/ZIP punctuation,
identifier hyphens) compare equal without merging distinct people.
PayerToPayerCoverageSelector picks the relevant concurrent/prior/current
coverage by requested payer/subscriber context and effective date; genuinely
overlapping coverages without a discriminator return an ambiguity rather than a
guess. The match reuses the same CHO member/coverage store via a new
IChoMemberDirectory on MockPatientAccessDataProvider (no duplicate store; the
Patient Access contract is unchanged), and its resolved member id feeds the P2P-01
export path directly (proven by an acceptance test). Member-match is identity only
and does not gate on or introduce consent. Real acceptance tests
(MemberMatchTests, [Trait("Backend","Replace")]) exercise the production
service/policy/selector/normalizer/source: exact strong-id and demographic
matches, prior-payer subscriber id, no-match, ambiguous identity narrowed by given
name/gender, conflicting id/DOB/given name, cross-tenant, insufficient criteria,
normalization, concurrent-coverage selection (prior/current/overlapping), and the
P2P-01 hand-off. The prior P2P-04 GAP-assertion test is replaced by this behavioral
coverage; the adapter-status report's PayerToPayer source now names $member-match
(mode stays Demo). The scenario manifest moves P2P-04 replace GAP → PASSABLE.
P2P-02 (outbound initiation, stays GAP), P2P-03 (dedicated P2P ConsentType,
stays PARTIAL), and QNXT Augment are unchanged and independently truthful. The
CI evidence pipeline derives the new result automatically — CHO Replace counts move
PASSABLE 12 / PARTIAL 7 / GAP 2 → PASSABLE 13 / PARTIAL 7 / GAP 1 with no
evidence-tooling change and no manual edit of generated evidence. Synthetic data
only; no PHI or payer configuration. Probabilistic matching, member enumeration,
and production P2P transport security (mTLS/UDAP) remain engagement work.
Closed CMS-0057-F acceptance gap P2P-01 (Payer-to-Payer inbound respond) with
the first production-shaped Payer-to-Payer vertical slice, Cloud Health Office
acting as the authoritative prior payer in Replace mode. New fhir-service
PayerToPayer domain: PayerToPayerExchangeService (application service) resolves
the transitioning member via a tenant-scoped, deterministic resolver
(PayerToPayerMemberResolver over PatientAccessPayerToPayerMemberSource, which
reuses the existing IPatientAccessDataProvider — no duplicate store), enforces
the member's opt-in authorization (fail-closed), and assembles a member-scoped
FHIR export with PayerToPayerExportBuilder reusing the existing CARIN/US Core
PatientAccessMapper (Patient + Coverage + ExplanationOfBenefit). A
PayerToPayerExportPolicy applies the locked 5-year date-of-service lookback (the
remittance/cost-sharing/drug exclusions are represented as predicates, gated on
data-model markers that do not exist yet). A thin PayerToPayerController
(POST fhir/r4/PayerToPayer/$member-data-export, under the SMART-enforced FHIR
surface, tenant taken from the authenticated context) routes to the service; all
logic lives in the service, not the controller. Member matching is safe:
insufficient criteria, no candidate, more than one candidate, a demographic
mismatch, a cross-tenant request, or a missing opt-in each fail explicitly and
never return another member's or another tenant's data. Every exchange yields an
audit entry. Real acceptance tests (PayerToPayerExportTests,
[Trait("Backend","Replace")]) exercise the production service/resolver/source/
builder/mapper: happy path, wrong member, no/ambiguous match, tenant boundary,
missing consent, empty-but-valid member, and the 5-year lookback. The prior P2P-01
GAP-assertion test is replaced by this behavioral coverage; the adapter-status
report moves PayerToPayer OutOfScope → Demo (inbound respond). The scenario
manifest moves P2P-01 replace GAP → PASSABLE. P2P-02 (outbound initiation),
P2P-03 (dedicated P2P ConsentType — stays PARTIAL), P2P-04 ($member-match
/ concurrent coverage), and QNXT Augment are unchanged and independently
truthful. The CI evidence pipeline derives the new result automatically — CHO
Replace counts move PASSABLE 11 / GAP 3 → PASSABLE 12 / GAP 2 with no
evidence-tooling change and no manual edit of generated evidence. Synthetic data
only; no PHI or payer configuration. Production P2P transport security (mTLS/UDAP)
remains engagement work.
Closed CMS-0057-F acceptance gap PAS-08 by implementing benefit drug/service
exclusion as real Cloud Health Office Replace-mode product capability, in the
authorization/benefit decision path (not a FHIR-controller or test-only check). New
authorization-service benefit-exclusion domain — BenefitExclusion model, a
configuration-driven, tenant-scoped IBenefitExclusionCatalog (no hard-coded
codes), a DrugServiceCodeNormalizer (NDC/RxNorm/HCPCS/CPT/service-type),
a pure DrugExclusionEvaluator, and an AuthorizationExclusionService.
ChoAuthorizationBackend.CreateAsync now consults it before the ordinary path: a
request for a drug/service the member's applicable plan excludes (or the pharmacy
service type, out of the CMS-0057-F medical scope) is recorded as a coded denial
(278 A3, structured DenialReasonCode) and persisted in the authoritative CHO
record with an auditable status history — never auto-approved by a generic rule.
A non-excluded request is unaffected. RequestedService gained an optional
ProductOrServiceSystem so a drug identity can be normalized. Real acceptance
tests (DrugExclusionTests, [Trait("Backend","Replace")]) exercise the
production backend + catalog + evaluator over a repository fixture — excluded drug,
non-excluded comparator, no-catalog, coverage scoping, code normalization, unknown
code, multiple exclusions, pharmacy service type, and PAS denied-response mapping
via PasResponseBuilder.BuildDeniedResponse. The prior PAS-08 GAP-assertion test is
replaced by this behavioral coverage. The scenario manifest moves PAS-08 replace
GAP → PASSABLE (rationale updated); QNXT Augment stays N/A (no external-core
drug-exclusion integration is claimed). The CI evidence pipeline derives the new
result automatically — CHO Replace counts move PASSABLE 10 / GAP 4 →
PASSABLE 11 / GAP 3 with no change to the evidence tooling and no manual edit of
generated evidence. Synthetic data only; no PHI, formulary, or payer configuration.
Made the CMS-0057-F evidence pipeline fresh, traceable, and externally
understandable without exposing CI internals. The evidence workflow now also runs
when runtime/domain code that can affect CMS-0057-F behavior changes (the FHIR,
authorization, member, provider, claims, benefit-plan, consent, and smart-auth
services, plus the operating-mode and prior-auth-rule engines), not only when the
acceptance suite or evidence tooling changes. A new allow-list projection in
tools/Cms0057Evidence (--public-output) emits a sanitized
cms0057-public-evidence.json — built field by field, so it carries only schema
version, evidence status, commit SHA/short/URL, timestamp, synthetic
classification, framework, FHIR version, scenario count, a test-execution summary,
independent Replace (product) and per-backend Augment (integration) declared-status
counts, a per-scenario declared-status matrix, and disclaimers — never test names,
rationales, run identity, PHI, secrets, tenant data, or QNXT field mappings. The
projector refuses to publish a run with any failed test. Declared capability status
stays separate from execution: a passing GAP-assertion test remains GAP in the
public snapshot, never a pass. The workflow now splits into an evidence job
(PR + main; validates and uploads artifacts only) and a main-only publish job
(narrow contents: write) that commits the sanitized snapshot to the site tree; the
acceptance-scenarios page renders it under Latest published evidence with the
tested source revision and generation date, Replace shown as product capability and
each external core as separate integration capability. Public reporting avoids any
CMS-certification or universal-production-readiness claim, and known gaps stay
visible. New projector unit tests cover allow-list sanitization, GAP-stays-GAP,
independent Replace/Augment counts, deterministic ordering, fail-safe on missing or
failed input, and unknown future backends. No runtime service behavior changed.
Turned the CMS-0057-F acceptance suite into auditable, reproducible evidence
tied to a source revision. tests/Cms0057Acceptance.Tests/scenarios.json
(schemaVersion: 1) is now the machine-readable source of truth for scenario
status (PASSABLE | PARTIAL | GAP | N/A), scored on two independent axes —
Cloud Health Office Replace (product capability) and external-core
Augment (integration capability). New ScenarioManifestTests reconcile the
suite's [Trait]s against the manifest (unknown/duplicate ids, invalid statuses,
a scenario silently losing all its tests, or a PASSABLE-for-a-backend scenario
backed only by GAP-assertion tests all fail the build). New generator
tools/Cms0057Evidence reads the manifest, the acceptance TRX, and the suite's
traits and emits deterministic cms0057-evidence.json/.md/.html bound to the
full tested commit SHA, keeping declared capability status separate from
test execution status (a passing GAP-assertion test confirms the gap and is
never promoted to PASSABLE). New CMS-0057-F Acceptance Evidence workflow runs
the suite, generates the evidence, writes a job summary, and uploads the
cms0057-acceptance-evidence-<sha> artifact; it fails on test failure or
manifest/test drift. Evidence contains only synthetic identifiers, repository
metadata, and test results — no PHI, tenant data, secrets, or QNXT field
mappings. Generator has its own unit tests (tools/Cms0057Evidence.Tests). No
runtime service behavior changed.
Made Cloud Health Office the authoritative (Replace-mode) backend for the
prior-authorization CMS-0057-F vertical slice, distinct from external-core
(Augment-mode) integration. New authorization-service backend seam:
Backends/IAuthorizationBackend selected by operating mode via
AuthorizationBackendSelector (Cms0057:Authorization:OperatingMode,
default Replace). ChoAuthorizationBackend (Replace) is the CHO-native system
of record — a thin application layer over the existing IAuthorizationRepository
(Cosmos/Mongo) that persists submission, retrieval, status/decision lifecycle,
stable id, and an append-only Authorization.StatusHistory.
QnxtAuthorizationBackend (Augment) is a documented stub (throws; no fake SOAP)
selected only when configured — never a silent fallback to CHO; selection fails
loudly if the configured external backend is unregistered. Replaces PR #1143's
flat IAuthorizationAdapter. AuthorizationsController routes create through
the selected backend and exposes GET /api/authorizations/backend-status
(active mode/backend, no sensitive config). Reuses the OperatingMode engine's
EngineOperatingMode. The FHIR/PAS layer is unchanged and depends on no
vendor-specific abstraction.
The acceptance suite now distinguishes product capability (CHO Replace) from
integration capability (QNXT Augment): PAS-03 is product PASSABLE on
ChoAuthorizationBackend (exercised via an in-memory repository fixture so the
real production backend, not an acceptance-only path, is proven) and integration
GAP on QNXT Augment. Scenarios carry [Trait("Backend","Replace"|"Augment")].
METRICS-01 product moved PARTIAL → PASSABLE (metrics derive from the persisted
CHO record). Inventory and the public acceptance page now score the two
dimensions separately and clarify Demo (synthetic) vs Replace (CHO authoritative)
vs Augment (external core). No runtime behavior change to shipped services beyond
the additive backend routing (Replace is the default and preserves prior
behavior).
Executable acceptance harness (tests/Cms0057Acceptance.Tests/, in
cloudhealthoffice-main.sln) proving the CMS-0057-F scenario set against the
real C# services in Demo/Cho mode. Scenarios are tagged
[Trait("Scenario","…")] (PAS-01..08, PROV-01..03, P2P-01..04, PAT-01..03,
SEC-01, CONSENT-01, METRICS-01), each with a happy path and, for prior auth, a
negative path. GAP scenarios are tests that assert the still-unimplemented seam
(the QNXT source-system adapters, the out-of-scope Payer-to-Payer surface, the
absent drug-exclusion and retention paths) rather than papering over stubs.
Added IAuthorizationAdapter + QnxtAuthorizationAdapter documented stub in
authorization-service (mirrors the existing Qnxt*Adapter pattern) so the
PAS-03 QNXT create-auth seam is explicit and testable; it throws
NotImplementedException and is not wired into DI. Honest PASSABLE / PARTIAL /
GAP inventory and traceability table at
docs/compliance/CMS0057-ACCEPTANCE-INVENTORY.md; public definition-of-done
guide at src/site/insights/cms-0057-f/acceptance-scenarios.html. No change to
runtime behavior of shipped services.
Founding-partner CMS-0057-F Compliance Accelerator offer ($90k / 6–8 weeks), CISO diligence binder (BAA template, security one-pager, adapter-status table, data-handling rules, 25-name target list), and a labeled synthetic demo tenant. fhir-service now exposes GET /fhir/r4/adapter-status and stamps X-CHO-Adapter-Mode / X-CHO-Data-Class / X-CHO-Adapter-Label on every response so mock adapters cannot look live.
IRemittancePoster posts a stored, matched 835 (AvailableForPosting) onto
claim financials and member benefit accumulators and marks the receipt
Posted. Source of the ERA is the remittance store — this does not invent
835s, change 277CA or 276/277, or reconcile EFT. Tenant comes from the
matched transmission. Duplicate posts replay. Failed claim or accumulator
writes abort without marking Posted. Gateway-only claims (no domain
claim) skip the claim sink. Accumulators use 835 PR deductible/copay/
coinsurance deltas with AdjustmentId 835|{remittanceId}|{claimId}, not
claims.finalized.v1. Development: POST /api/dev/gateway/remittance/{receiptId}/post.
Vendor-neutral IClaimIntelligenceComposer composes 837 submission, 277CA
acknowledgment, 276/277 claim status, 275 attachments, and 835 remittance
into a tenant-scoped read model. Lifecycle status is derived without letting
one transaction overwrite another (277CA accepted is not paid; 276/277 paid
does not invent an 835). Financial and attachment summaries are
informational. Timeline event ids are stable, so duplicate deliveries do not
duplicate history. GET /api/claims/{claimId}/intelligence. The view is not
the system of record and does not post payment.
Vendor-neutral IRemittanceGateway.RetrieveRemittanceAsync with canonical
GatewayRemittance. Stedi transport is the 835 ERA Report
GET https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/reports/v2/{transactionId}/835
after webhook or poll discovery. IRemittanceProcessor matches claims
deterministically (payer claim control number, then patient control number),
persists receipts, and emits identifier-only events. It does not post
payment, change 277CA, or overwrite 276/277 status. Development:
POST /api/dev/gateway/remittance.
Contract-tested against Stedi's documented 835 API; live ERA retrieve pending production/test capability.
Vendor-neutral IClaimStatusGateway.CheckClaimStatusAsync with canonical
ClaimStatusRequest / ClaimStatusResponse. Callers pass ClaimId or
TransmissionId; the coordinator derives payer, provider, subscriber, dates,
and control numbers from the original 837 snapshot and from a matched 277CA
payer claim control number when present. Stedi transport is Real-Time Claim
Status JSON POST https://healthcare.us.stedi.com/2024-04-01/change/medicalnetwork/claimstatus/v2.
276/277 status is a separate dimension from 277CA acknowledgment,
adjudication, and 835 payment. HTTP 200 with no matching claim is a business
NoRecordFound, not a transport failure. Mock returns deterministic
statuses for tests. Development:
POST /api/dev/gateway/claims/{transmissionId}/status.
Stedi test keys are not supported for this endpoint. Contract-tested against the documented API; live inquiry pending production/test capability.
Vendor-neutral IClaimAttachmentReceiver so Cloud Health Office can receive
a 275-equivalent attachment as the payer. Distinct from outbound
IClaimAttachmentGateway. Canonical InboundClaimAttachment, deterministic
claim/service-line matching, SHA-256 content store, durable receipts with
outbox, quarantine for unmatched attachments. Development:
POST /api/dev/payer/claims/{claimId}/attachments.
Stedi inbound payer-side 275 is adapter-ready / pending Stedi payer connectivity, not implemented. Raw X12 275 ingress is deferred. Receipt does not adjudicate or pay the claim.
Vendor-neutral IClaimAttachmentGateway.SubmitAttachmentAsync with canonical
ClaimAttachmentSubmissionRequest. Bytes live in IClaimAttachmentContentStore
(existing IDocumentStore / Azure Blob when configured) as a content
reference plus SHA-256 — never on the claim aggregate. Attachments associate
deterministically to an existing 837 transmission (optional service line).
Stedi transport is Create Claim Attachment JSON
POST https://claims.us.stedi.com/2025-03-07/claim-attachments/file plus PUT
to the pre-signed URL. Unsolicited 275 only. MIME/size validated before
send. Attachment lifecycle is independent of 837 / 277CA / adjudication /
payment. Idempotency is
tenant+transmission+attachment+checksum+type+line+version.
Synchronous gateway acceptance is not payer review or claim payment. Live
275 is not claimed for sandbox accounts. Development:
POST /api/dev/gateway/claims/{transmissionId}/attachments.
Durable Mongo persistence for transmissions, 277CA acknowledgments, outbox,
and poll cursors. Non-Development hosts fail closed unless Mongo is
configured. TryCreateAsync is unique-index atomic. Outbox publication is
retried by a hosted dispatcher. Transmission state transitions are guarded
so malformed/duplicate events cannot rewind or overwrite a completed 277CA
outcome. Same-key 837 submit after 277CA is a replay.
Vendor-neutral GatewayClaimAcknowledgment plus IClaimAcknowledgmentGateway
retrieve and IClaimAcknowledgmentProcessor. Stedi discovers 277CAs via
transaction.processed.v2 webhooks or Poll Transactions
(core.us.stedi.com/2023-08-01) and retrieves JSON from
GET /2024-04-01/change/medicalnetwork/reports/v2/{transactionId}/277.
Acknowledgments match deterministically to #1111 transmission records.
Tenant comes from the matched transmission. 277CA accepted/rejected stays
separate from adjudication and payment. Duplicate webhooks are idempotent.
Stedi does not HMAC-sign claim-response webhooks; CHO authenticates the configured credential-set header. Live 277CA testing is not claimed for sandbox accounts.
Development: POST /api/dev/gateway/claims/{transmissionId}/277ca.
Production webhook: POST /api/integrations/stedi/claim-responses.
IClaimSubmissionGateway.SubmitClaimAsync is a real capability. Mock and
Stedi implement 837P / 837I / 837D against Stedi's documented JSON APIs
(professionalclaims/v3, institutionalclaims/v1, dental-claims).
Canonical GatewayClaimSubmissionRequest stays vendor-neutral. Payer
readiness reuses IPayerReferenceService. Durable
IClaimTransmissionStore records transmission state separately from
adjudication/payment. Idempotency is tenant+claim+version+type+frequency.
Synchronous gateway acceptance is not 277CA, adjudication, or payment.
Live 837 calls are not claimed for sandbox accounts (Stedi test claims
require a production-account test key). Development:
POST /api/dev/gateway/claims.
Vendor-neutral IEligibilityResponder so Cloud Health Office can act as the
payer/information source for an inbound eligibility inquiry. Canonical
PayerEligibilityInquiry / PayerEligibilityResponse, exact-match member
and dependent resolution, read-only coverage / benefit / accumulator access,
and a Development-only POST /api/dev/payer/eligibility ingress.
Stedi does not currently document a self-service inbound 270 payer-hosting
API. The Stedi inbound adapter is adapter-ready / pending Stedi payer-side
connectivity, not implemented. Existing outbound IEligibilityGateway /
Stedi eligibility is unchanged.
Canonical GatewayEligibilityPerson subscriber/patient model. Dependent
inquiries emit Stedi dependents[]; subscriber-only requests do not. Opt-in
live sandbox smoke covers the documented UHC 87726 John/Jane Doe Active
Coverage path.
Canonical, vendor-neutral payer identity for Cloud Health Office. Stedi List
Payers JSON (GET https://payers.us.stedi.com/2024-04-01/payers) synchronizes
into IPayerReferenceService. StediHealthcareGateway resolves eligibility
payers through that service; PayerMap/TenantPayerMap are deprecated
fallbacks. Arbitrary payer ids are no longer passed through to Stedi.
Enhanced Provider Management & Multi-Market Expansion
- Enhanced practice management features for small-to-medium practices
- Advanced provider network analytics and reporting
- Expanded core system integrations (Epic Tapestry, additional CAPS)
- Mobile provider app (React Native) for iOS and Android
- Provider-facing scheduling and patient communication tools
- Enhanced claims scrubbing with AI-powered validation
- Multi-location practice support with centralized billing
Closes the Cloud Health Office Claims-domain Phase 1 effort spanning 14 capabilities (5.1a–5.12b). Phase 1 delivers a full claim lifecycle end-to-end: submit → adjudicate (7-stage pipeline: Scrubbing 100 / Network 200 / BenefitCalculation 300 / NCCI 400 / CoB 500 / AI Examination 600 / Persistence 999) → pay (operator-initiated batched 835) → adjust (re-adjudication via predecessor chain) → reverse (operator-initiated batched negative 835).
No new functionality. This release is documentation-driven: capability matrix, end-to-end narrative, architectural-pattern index, Phase 2 backlog catalog, CMS-0057-F readiness assessment, canonical V1 API surface reference, and a portfolio module-status register.
- 5.1a (#725) — Claim Identity & Versioning (versioning fields + Mongo event chain)
- 5.1b (#743) — Cosmos partition-key migration to
/tenantId - 5.2 (#728) — Adapter pattern foundation
- 5.3 (#729) — Claim Submission API (canonical V1 surface)
- 5.4 (#734) — Pre-adjudication scrubbing + claims-scrubbing-service decommission
- 5.5 (#731, #732) — Adjudication pipeline foundation
- 5.6 (#733) — Network & credentialing enforcement
- 5.7 (#736) — NCCI / MUE edits enforcement + projection bypass extension
- 5.8 (#737) — Coordination of Benefits + Phase 2 hook stub
- 5.9 (#738) — AI-Backed Examination pipeline stage
- 5.10 (#740) — Operator-initiated batched 835 remittance + cross-service finalize
- 5.11 (#739) — FHIR ExplanationOfBenefit projection
- 5.12a (#741) — Adjustment Workflow chain + re-adjudication
- 5.12b (#742) — ReversalRun batched 835 reversal + lifecycle wiring
docs/architecture/claims-phase-1-closer.md— Phase 1 closer narrative (capability matrix, end-to-end lifecycle, 14-pattern architectural index, diligence-readiness checklist)docs/roadmap/claims-phase-2-backlog.md— Phase 2 backlog (48 items across 10 categories: inbound EDI, FHIR completeness, CMS-0057-F, COB priorEob, AI examiner, cross-service event-stream depth, operational, trading-partner transmission, reference-data workflows, infrastructure follow-ups)docs/compliance/claims-cms-0057-f-readiness.md— CMS-0057-F readiness posture (Phase 1 shipped vs Phase 2 required vs January 2027 mandate)docs/api/claims-v1-surface.md— canonical V1 API surface (8 controllers / 47 verbs across claims-service + payment-service customer-facing surfaces)docs/status/MODULE-STATUS.md— portfolio module-status register (initialized at Claims Phase 1 close; format mirrorable for future service-level closures)
Claims 5.13 establishes the closer pattern for service-level / domain-level Phase 1 / Phase 2 closures across Cloud Health Office. Future Provider Phase 2, BenefitPlan Phase 2, and other domain closures can mirror the structure: capability matrix → operational narrative → pattern index → phase boundary → diligence-readiness posture, with separate registries for backlog, compliance posture, and API surface.
OpenAPI / Swagger surfaces continue to be served by both claims-service (via shared AddChoInfrastructure) and payment-service (via direct AddSwaggerGen) in development environments. payment-service Swagger pattern parity migration and XML-doc-driven Swagger surface enrichment are tracked as Phase 2 follow-ups.
- Legacy
ClaimsCosmos container deletion (~30-day retention window from 5.1b cutover; Bicep PR) - Phase 2 sequencing per
docs/roadmap/claims-phase-2-backlog.md. Primary near-term driver: CMS-0057-F unauthenticated patient access (January 2027 mandate).
New microservice enabling per-member-per-month (PMPM) capitation payments from health plans to capitated providers. Structurally mirrors premium-billing-service (which collects premiums FROM sponsors) but pays TO providers.
New Service: capitation-service
- CapitationContract — provider agreements with 12-tier age-sex rate schedules, risk adjustment (HCC/RAF), quality withhold percentages, incentive pools, per-member and aggregate stop-loss thresholds
- CapitationRunService — monthly batch orchestration that fetches PCP panel rosters from coverage-service, risk scores from risk-adjustment-service, calculates proration for mid-month adds/terms, applies withholds, and generates provider payment statements
- CapitationStatement — provider-facing payment detail with member-level line items (base PMPM, risk score, adjusted PMPM, proration factor, gross/withhold/net), retroactive adjustments, and RecalculateTotals()
- CapitationDisbursementService — EFT payment lifecycle supporting NACHA ACH credits, Stripe Connect transfers, and paper checks, with ACH return handling (R01-R29 codes) and auto-retry logic
- CapitationEraService — X12 005010X221A1 835 ERA generation for capitation payments (CLP02=22, CLP06=CP, no SVC service lines, CAS CO-45 for withholds, PLB with WO/72/L6/FB adjustment codes)
- NachaCreditFileService — NACHA credit file generation (transaction codes 22/32 for checking/savings credits, SEC code CCD, service class 220, entry description CAPITATION)
- StripeConnectService — Stripe Transfer API integration for Connected Account payouts with webhook processing (transfer.created, transfer.reversed, payout.paid, payout.failed)
- Dual Cosmos DB / MongoDB repositories (8 files) with tenant isolation
- Kubernetes deployment manifest, Dockerfile, docker-compose entry (port 5012)
Supporting Service Changes:
- coverage-service — PcpNpi, PcpName, PcpAssignmentDate, PcpAssignmentMethod, PreviousPcpNpi fields on Coverage model; new PcpAssignmentMethod enum (AutoAssigned, MemberSelected, PlanDefault);
GET /api/v1/coverage/by-pcp/{npi}endpoint for panel roster queries; compound indexes on (TenantId, PcpNpi, Status) - provider-service — ProviderBankAccount model with EFT/Stripe Connect/check disbursement support, W-9/1099 compliance fields; DisbursementMethod, BankAccountType, TaxIdType enums;
GET/PUT /api/providers/npi/{npi}/bank-accountendpoints
Portal — Capitation Management (3 new pages):
- Capitation Contracts — data grid with contract#/provider/type/LOB/status/tiers/withhold, inline rate tier editor, activate/terminate actions
- Capitation Runs — create/execute runs with period selector, run list with provider count/member-months/net payable/duration, drill into statements
- Capitation Statements — filterable list, member-level breakdown with age/gender/PMPM/risk score/proration/withhold, approve/hold/void workflows, batch "Pay Approved" disbursement
- ICapitationService API client (16 methods), Capitation navigation group in sidebar
Seed Data:
seed-capitation.sh— 3 demo contracts, 20 member PCP assignments, completed capitation runseed-capitation-pcp-assignments.js— mongosh script for Coverage.PcpNpi updates
Tests: 176 new (163 unit + 13 smoke)
- CapitationRunService, CapitationDisbursementService, CapitationEraService (28 X12 835 tests), NachaCreditFileService, StripeConnectService, all 4 controllers, TenantMiddleware, CosmosSerializer
- WebApplicationFactory smoke tests for full HTTP pipeline
| Metric | Previous | Current |
|---|---|---|
| Portal pages | 47 | 50 |
| Microservices | 23 | 24 |
| Service interfaces | 20 | 21 |
| C# application lines | ~74,800 | ~86,800 |
| Total code lines | ~192,000 | ~204,000 |
| Automated tests | 797 | 973 |
4.2.0 - March 2026
New Pages:
- Work Queues — claims examiner workflow with pend queue management by reason (NCCI, missing auth, provider not contracted, COB, medical review), priority tracking, and examiner assignment
- Appeals — search-first appeal tracking with regulatory deadline monitoring (MA 30-day standard, 72-hour expedited), appeal detail dialog with full lifecycle review
- Correspondence — outbound letter queue management (adverse determinations, EOBs, RFAIs, welcome letters) with RFAI response tracking and deadline monitoring
- Enrollment Operations — daily 834 file processing dashboard with transaction counts, adds/terms, and rejection detail
Enhanced Pages:
- Dashboard — added operational alerts (work queue count, pending RFAIs, appeals due), EDI transaction volume summary, and system health indicators
- Member Detail — added Accumulators tab with plan year deductible and OOP max progress bars, service-specific accumulator tracking, and recent claim activity affecting accumulators
- Claims — consolidated ClaimsNew into primary Claims page with advanced search (Claim ID, Member ID, Provider, status, date range)
- Settings — added Operating Mode tab showing per-engine Augment/Replace configuration with mode descriptions
Portal Architecture:
- Navigation reorganized into 6 collapsible groups (Operations, Members & Providers, Configuration, Finance, Monitoring, Admin)
- All PHI pages changed from [AllowAnonymous] to [Authorize]
- Search-first pattern enforced on all pages displaying member, claim, or authorization data (HIPAA minimum necessary)
- 5 new service interfaces and implementations (WorkQueue, Appeals, Correspondence, EnrollmentOperations, OperatingMode)
- Dashboard metrics corrected (approval rate and claims trend math)
- ClaimsScrubEngine — C# port of TypeScript validation rules with 20+ rules across 6 categories, wired into AdjudicationController
- OperatingMode engine — per-engine, per-tenant Augment/Replace toggle with AugmentResult and discrepancy logging
- Seed scripts corrected to lowercase database name (cloudhealthoffice)
- Tenant onboarding checklist (TENANT_ONBOARDING_CHECKLIST.md) with Azure AD multi-tenant admin consent flow documentation
- Parameterized seed-demo-data.js (1,345 lines) for any-tenant seeding
- Parameterized seed-tenant.js for tenant provisioning
- README updated with accurate platform metrics and new sections
- Architecture diagram (SVG, Sentinel theme) replacing ASCII art
- Adjudication pipeline diagram showing 8-stage processing with latency
- Operating mode diagram illustrating Augment/Replace architecture
- Channel Partners section for implementation firm distribution model
- Adoption path rewritten to reference Operating Mode by name
- Codebase Scale section with line-count breakdown by language
| Metric | Previous | Current |
|---|---|---|
| Portal pages | 43 | 47 |
| Calculation engines | 7 | 9 |
| Service interfaces | 15 | 20 |
| Portal Razor lines | 14,622 | 16,279 |
| C# application lines | ~72,900 | ~74,800 |
| Total code lines | ~160,000 | ~192,000 |
| Total lines (w/ docs) | ~240,000 | ~303,000 |
| Automated tests | 1,018 | 1,295 |
4.1.0 - February 16, 2026
Commercial positioning and developer discoverability improvements
Based on comprehensive repository assessment, this release transforms FHIR APIs from buried technical features into prominently featured commercial products.
- Patient Access API: Full OpenAPI 3.1 spec for CMS-9115-F Patient Access API (FHIR R4, US Core 3.1.1+, CARIN BB)
- Claims Scrubbing API: Commercial pre-validation API with ROI metrics (95%+ first-pass rates)
- Provider Access API: Fixed filename typo (provider-accerss-api.yaml → provider-access-api.yaml)
- Interactive Documentation: Swagger UI embedded viewers for all APIs
- CMS-0057-F Compliance (15 min): Deploy → Test → Verify compliance before Jan 1, 2027 deadline
- Patient Access API (30 min): OAuth setup → Authentication → Build member portal (JS/Python/C# examples)
- Claims Scrubbing API (20 min): EHR integration patterns, batch validation, ROI calculator
- Homepage: CMS-0057-F deadline banner (Jan 1, 2027 urgency messaging)
- Featured Section: FHIR APIs now 2x grid space in prime dashboard position
- New Page: Dedicated API documentation hub (
api-docs.html) with interactive viewers - Navigation: Added "FHIR APIs" to main menu
- Commercial Card: Claims Scrubbing positioned alongside core compliance APIs
- Before: No OpenAPI specs, no quickstarts, APIs buried in src/fhir/
- After: 3 OpenAPI specs, 3 quickstart guides, prominent portal presence, 15-min onboarding
- Files Changed: 16 files, 2,895 insertions
- Time to First API Call: Reduced from hours to < 30 minutes
4.0.0 - February 11, 2026
100% vulnerability elimination from 86 high-severity issues to absolute zero.
- CVE-2024-43485: Fixed System.Formats.Asn1 RCE (8.0.0 → 8.0.1)
- CVE-2024-21907: Fixed Newtonsoft.Json deserialization attack (10.0.2 → 13.0.3)
- Directory.Build.props: Global transitive dependency enforcement
- 59 package updates: Azure.Identity, Azure.Core, Microsoft.Azure.Cosmos, MudBlazor, Stripe.net, Swashbuckle, and 50+ more
- TenantContextService: Maps Azure AD tenant → CHO tenant via subscription lookup
- TenantHttpMessageHandler: Injects
X-Tenant-IDheader on all backend API calls - Portal Isolation: Prevents cross-tenant data leakage (CRITICAL security fix)
- Dynamic UI: Shows actual tenant name with demo/production badges
- Logout Functionality: Proper Microsoft Identity sign-out flow
- CloudHealthOffice.Infrastructure Package: Cloud-agnostic
IDocumentStore<T>interface - Azure Implementation:
CosmosDocumentStore<T>(current production) - DigitalOcean Implementation:
MongoDocumentStore<T>(65% cost savings) - Reference Implementation: member-service compiles with multi-cloud support
- GitHub Actions Workflow: 3-click toggles for Azure/DigitalOcean deployment
- Status: Available in
feature/multi-cloud-infrastructurebranch for testing
- New Tenant: clouddentaloffice (dental claims EDI)
- Endpoint: 20.115.193.245:22 (pending DNS: sftp.cloudhealthoffice.com)
- Folder Structure: /dental-claims/inbound/837/, /outbound/835/, /outbound/277/
- Credentials: Stored in Azure Key Vault
- Azure Permissions: Added Application Administrator & User Access Administrator roles
- Deployment Gates: Pre-approval checks in GitHub Actions
- PII/PHI Scanner: Configured to allow test data patterns
- Logic Apps Migration: Disabled deployment (moved to Argo workflows)
- Azure.Identity: 1.12.1 → 1.13.1
- Azure.Core: 1.42.0 → 1.44.1
- Microsoft.Azure.Cosmos: 3.42.0 → 3.45.0
- MudBlazor: 7.20.0 → 8.4.0
- Stripe.net: 46.4.0 → 47.0.0
- Swashbuckle.AspNetCore: 6.5.0 → 10.1.2
- Logic Apps Deployment: Disabled in deploy.yml (use Argo workflows)
- Multi-Tenant Headers: Portal now sends
X-Tenant-IDon all API calls (all services already compliant)
- DNS Configuration: sftp.cloudhealthoffice.com not yet pointed to 20.115.193.245
- Mock Data Fallback: Portal shows mock data when backend unavailable (configurable via
Portal.UseMockDataFallback) - Stripe.net Warning: NU1603 - Package 46.4.0 not found, resolved to 47.0.0 (non-breaking)
Production Readiness: ✅ Multi-Tenant Isolation | ✅ Security Hardening | ✅ HIPAA Controls | ✅ Zero Vulnerabilities
Documentation: RELEASE-v4.0.0.md, MULTI-CLOUD-SETUP.md, MULTI-CLOUD-DEPLOYMENT-GUIDE.md
3.0.0 - February 2026
Cloud Health Office v3.0.0 is production-ready for both health payers (legacy system augmentation) and healthcare providers (practice management with direct EDI). This release delivers multi-cloud independence, CMS-0057-F compliance, and commercial launch readiness.
- Kubernetes/Helm Deployment: Deploy Cloud Health Office to AKS, EKS, GKE, or any Kubernetes cluster
- Argo Workflows Migration: Cloud-native workflow orchestration replacing Azure Logic Apps
- Apache Kafka Integration: Cloud-agnostic messaging replacing Azure Service Bus
- HashiCorp Vault Support: Open-source secrets management as alternative to Azure Key Vault
- Multi-Cloud Deployment Guide: Comprehensive documentation for deploying across cloud providers
Documentation: MULTI-CLOUD-DEPLOYMENT.md, ARGO-MIGRATION-GUIDE.md
- X12 275 Attachment Ingest Workflow: Kubernetes-native SFTP polling and processing
- X12 278 Authorization Request Workflow: Cloud-agnostic prior auth handling
- X12 277 RFAI Response Workflow: Event-driven response generation via Kafka
- X12 278 Replay Workflow: Deterministic replay from Kafka offsets
- Container Images: X12 parser, encoder, SFTP fetcher, metadata extractor, Kafka publisher
- Argo Events Configuration: SFTP polling and Kafka event sources with sensors
Documentation: ARGO-OPERATIONS.md
- Managed Application Plan: ARM template deploying full Cloud Health Office stack
- SaaS Plan with Meter-Based Billing: Per-transaction pricing (837, 278, 275, FHIR API calls)
- 3-Tier Pricing: Starter, Professional, Enterprise — Contact sales for pricing
- Partner Center Metadata: Complete offer listing and marketing assets
- Legal Documents: Privacy policy, SLA (99.5%-99.95% uptime), support terms
- Marketplace Icons: Sentinel-branded SVG assets for all required sizes
Documentation: marketplace/README.md
- Sales Product Overview: 2-page executive summary with competitive positioning
- ROI Calculator: TCO analysis and 5-year savings projections
- Case Study Template: Reusable template for pilot customer success stories
- Financial Model: 3-year projections with unit economics
- Pitch Deck Content: 15-slide framework for investor/customer presentations
- Pilot Program: 60-day structured pilot with success criteria
- Sales Email Templates: 5 targeted outreach templates
- Marketing Landing Page Copy: Conversion-optimized content
Documentation: sales-materials/README.md
- VC Target List: 12+ prioritized healthcare and SaaS VCs with investment thesis fit
- Investor One-Pager: Single-page investment summary
- Due Diligence Checklist: Legal, financial, technical, commercial preparation
- Strategic Partner Targets: 50+ partners including Microsoft, SIs, technology vendors
- Investor Meeting Script: 30-minute pitch framework
- Warm Intro Templates: 4 introduction request templates
- Alternative Funding: Grants (SBIR), RBF, venture debt, strategic investors
- PR Strategy: Thought leadership, podcasts, conferences, LinkedIn
Documentation: fundraising/README.md
- Eligibility Service: Azure Container Apps + Dapr with dual X12 270/271 and FHIR interface
- ClaimRiskScorer Azure Function: ML-powered fraud/abuse scoring (0-100) with PyTorch
- Provider Directory API Logic App: FHIR endpoints with NPPES NPI integration
- Prior Auth API Logic App: Da Vinci PAS CDex flow with 72-hour SLA tracking
- Cosmos DB Integration: PriorAuthorizations and ProviderDirectory containers
Documentation: services/eligibility-service/README.md
- Azure Monitor Workbook: Real-time compliance metrics visualization
- Patient Access API Tracking: Enablement percentage with daily trends
- Prior Auth SLA Monitoring: 72-hour urgent and 7-day standard response tracking
- Error Rate Analysis: Transaction-level error tracking for 270/271, 278, 837
- PHI Audit Trail: Security operations monitoring via Application Insights
Documentation: docs/AZURE-MONITOR-DASHBOARDS.md
- Blazor Web App:
/tools/migration-wizardfor legacy system migration - Claims Backend SOAP Integration: Paginated export via Open Access APIs
- Cosmos DB Export: Batch upsert for Members, ProviderDirectory, BenefitPlans
- Mapping Report Generator: 95%+ auto-match with field-level validation
- One-Click API Cutover: Routing key flip via Azure API Management
- Azure Key Vault Integration: Secure credential management
Documentation: tools/migration-wizard/README.md
- Quarterly Milestones: Q1-Q4 2026 with CMS compliance timeline
- Microservice Releases: eligibility-service v2.0, prior-auth-service v2.0, claims-service v1.0, remittance-service v1.0
- Community Targets: 500→7,500 GitHub stars, 15→150 contributors
- OKRs: Measurable success criteria for compliance, adoption, community, and AI
Documentation: ROADMAP-2026.md
- Executive Whitepaper: 7-page document for payer CIOs/CTOs
- ROI Analysis: 522% Year 1 ROI, 4.2-month payback period
- TCO Comparison: $16.7M legacy vs $2.6M Cloud Health Office (5-year)
- Implementation Roadmap: 12-16 week phased timeline
- Mermaid Visualizations: Gantt charts, TCO comparison, cost breakdown
Documentation: docs/WHITEPAPER-CMS-0057-F-COMPLIANCE.md
- CONTRIBUTING.md: Enhanced with DCO and CLA instructions
- CODE_OF_CONDUCT.md: Contributor Covenant 2.1
- GOVERNANCE.md: Steering committee election process
- Issue Templates: Feature request and bug report YAML forms
- PR Automation: Auto-labeling and reviewer assignment workflows
- Vendor-Agnostic Refactoring: Removed 1,295 vendor-specific references across 185 files
- Container Build Workflow Fix: Corrected image tags for vulnerability scanning
- patient_access_api Workflow Fix: Added missing
kindandparameterskeys
- Updated README.md with Kubernetes deployment badge and dual architecture options
- Updated ARCHITECTURE.md with deployment options section
- Updated ROADMAP.md to reflect multi-cloud strategy progress (40% complete)
- Helm charts updated with HashiCorp Vault integration settings
- Container build workflow image tag mismatch for Trivy scanner
- patient_access_api workflow.json missing required keys
- PHI compliance issues with HTTPS enforcement for Vault URLs
- Storage Account networkAcls defaultAction set to "Deny" for HIPAA compliance
- Key Vault networkAcls defaultAction set to "Deny" for HIPAA compliance
- Managed Identity exclusively used for Cosmos DB/Event Grid access (no keys)
- All 424 tests pass with zero security vulnerabilities
| PR | Title | Category |
|---|---|---|
| #116 | Remove vendor-specific references | Platform |
| #115 | Add multi-cloud deployment documentation and HashiCorp Vault integration | Multi-Cloud |
| #114 | Fix image tag mismatch in container build workflow | CI/CD |
| #113 | Migrate X12 EDI processing to Argo Workflows and Kafka | Multi-Cloud |
| #112 | Add VC fundraising strategy and materials | Commercial |
| #111 | Add comprehensive commercial launch materials | Commercial |
| #110 | Enhance CMS-0057-F whitepaper with ROI analysis and visualizations | Documentation |
| #109 | Add CMS-0057-F compliance whitepaper for payer executives | Documentation |
| #108 | Add 2026 product roadmap with CMS compliance milestones | Roadmap |
| #107 | Add community governance files, issue templates, and PR automation | Governance |
| #106 | Add Blazor migration wizard for legacy platforms to Cloud Health Office | Tools |
| #105 | Add Azure Marketplace offer structure with managed app and SaaS plans | Marketplace |
| #104 | Add ClaimRiskScorer Azure Function for 837 fraud/abuse risk scoring | Microservices |
| #103 | Add eligibility-service with dual X12 270/271 and FHIR interface | Microservices |
| #102 | Add CMS-0057-F Compliance Dashboard workbook for Azure Monitor | Compliance |
| #101 | Fix patient_access_api workflow missing required keys | Bug Fix |
| #100 | Add ProviderDirectoryApi and PriorAuthApi Logic Apps with NPPES integration | Microservices |
2.0.0 - 2025-11-28
Complete CMS-0057-F compliance with production-ready FHIR R4 APIs, delivered 18 months ahead of the January 1, 2027 deadline.
- Release Notes Portal: New
site/release-notes.htmlwith delivered features, sandbox testing, and early adopter signup - Documentation Updates: Enhanced CMS-0057-F compliance documentation with post-FHIR implementation status
- Site Navigation: Added release notes links across all platform landing pages
- V2 Announcements: Updated site/index.html with v2 banners and CMS-0057-F/FHIR API announcements
Documentation: Release Notes, CMS-0057-F Compliance
- X12 837 → FHIR Claim: Professional, Institutional, and Dental claims with Da Vinci PDex profiles
- X12 278 → FHIR ServiceRequest: Prior authorization with Da Vinci PAS/CRD compliance
- X12 835 → FHIR ExplanationOfBenefit: Remittance advice with complete adjudication details
- X12 275 → FHIR DocumentReference: Clinical attachments and supporting documentation
- CMS-0057-F Compliance Checker: Automated validation of data classes and timeline requirements
- Azure FHIR Validator: Profile validation integration with Azure API for FHIR
- US Core + Da Vinci IGs: Full PDex, PAS, CRD, DTR implementation guide conformance
- 45 Comprehensive Tests: All FHIR mappers validated with 100% pass rate
- Zero External Dependencies: Secure core mappers with no runtime vulnerabilities
Compliance Status: Ready for January 1, 2027 CMS-0057-F deadline
Documentation: FHIR-INTEGRATION.md, CMS-0057-F-COMPLIANCE.md
- Real-Time Patient Data Access: FHIR R4 API for providers with patient authorization
- SMART on FHIR Scopes:
user/*.read,system/*.readfor provider/system access - NPI-Based Authorization: Provider identity verification and access control
- Consent Management: Patient authorization tracking and revocation support
Documentation: FHIR-INTEGRATION.md
- Bulk FHIR Export:
$exportoperation for efficient data exchange - 5-Year Historical Data: Configurable retention via Azure Data Lake lifecycle policies
- Enrollment-Triggered Transfers: Automated data exchange on member transitions
- USCDI v1/v2 Coverage: Complete data class support for interoperability
Documentation: CMS-0057-F-COMPLIANCE.md
- Zero-Code Payer Onboarding System: Transform JSON configuration into complete deployment artifacts
- Interactive Configuration Wizard: Guided setup experience completing in <5 minutes
- TypeScript-Based Generator: 700+ lines of automation code with comprehensive validation
- 30+ Handlebars Template Helpers: String, array, conditional, JSON, math, date, type checking utilities
- Workflow Templates: Automatic generation of Logic App workflow.json files
- Infrastructure Templates: Bicep templates with parameters and deployment scripts
- Documentation Generation: Payer-specific DEPLOYMENT.md, CONFIGURATION.md, TESTING.md
- Example Configurations: Medicaid MCO and Regional Blues templates included
- 23-Test Comprehensive Suite: All passing with 100% validation coverage
- CLI Tool: Command-line interface with generate, validate, template, list commands
Documentation: CONFIG-TO-WORKFLOW-GENERATOR.md, IMPLEMENTATION-SUMMARY.md
- X12 270 → FHIR R4 Mapping: Transform eligibility inquiries to Patient & CoverageEligibilityRequest
- CMS Patient Access API Compliance: Ready for CMS-9115-F requirements (14 months ahead of roadmap)
- US Core Implementation: US Core Patient profile v3.1.1 compliant
- Standards Support: HIPAA X12 270 (005010X279A1), HL7 FHIR R4 (v4.0.1)
- Zero External Dependencies: Core mapper with no runtime vulnerabilities
- 19 Comprehensive Tests: 100% pass rate, covers all mapping scenarios
- Production-Ready Security: Secure examples using native fetch and Azure Managed Identity
- Service Type Mapping: 100+ X12 service type codes supported
- Subscriber & Dependent Support: Complete demographics handling
Documentation: FHIR-INTEGRATION.md, FHIR-SECURITY-NOTES.md, FHIR-IMPLEMENTATION-SUMMARY.md
- 60+ Enhanced Response Fields: Comprehensive claim intelligence beyond basic status
- Financial Fields (8): BILLED, ALLOWED, PAID, COPAY, COINSURANCE, DEDUCTIBLE, DISCOUNT, PATIENT_RESPONSIBILITY
- Clinical Fields (4): Diagnosis codes, procedure codes, service dates, place of service
- Demographics (4 objects): Patient, subscriber, billing provider, rendering provider details
- Remittance Fields (4): Check/EFT details, payment date, trace numbers
- Service Line Details: 10+ fields per service line with configurable granularity
- Integration Flags (6): Cross-module workflows for Appeals, Attachments, Corrections, Messaging, Chat, Remittance
- Unified Configuration: Complete valueAdds277 configuration in payer config schema
- Premium Product Capability: $10k/year additional revenue per payer
- Provider ROI: 7-21 minutes saved per claim lookup ($69,600/year for 1,000 lookups/month)
Documentation: VALUEADDS277-IMPLEMENTATION-COMPLETE.md, ECS-INTEGRATION.md
- Premium Key Vault Infrastructure: HSM-backed keys with FIPS 140-2 Level 2 compliance
- Private Endpoints: Complete network isolation for Storage, Service Bus, Key Vault
- VNet Integration: Logic Apps deployed in private virtual network
- PHI Masking: DCR-based transformation rules for Application Insights
- Customer-Managed Keys: Optional BYOK for regulatory requirements
- Data Lifecycle Management: 7-year retention with automated tier transitions (Hot → Cool → Archive)
- Storage Cost Optimization: 94% reduction ($463/mo → $29/mo) with lifecycle policies
- HTTP Endpoint Authentication: Azure AD Easy Auth for replay278 endpoint
- Audit Logging: 365-day retention with compliance queries
- 4 Bicep Modules: keyvault.bicep, networking.bicep, private-endpoints.bicep, cmk.bicep (649 lines)
- HIPAA Compliance: 100% technical safeguards (§ 164.312) documented and implemented
Security Score: 9/10 (Target achieved)
Documentation: SECURITY-HARDENING.md, HIPAA-COMPLIANCE-MATRIX.md, SECURITY-IMPLEMENTATION-SUMMARY.md
- Pre-Approval Security Validation: TruffleHog secret detection, PII/PHI scanning, artifact validation
- UAT Approval Workflow: 1-2 required approvers, triggers on
release/*branches - PROD Approval Workflow: 2-3 required approvers, manual dispatch from
mainonly - Security Context for Approvers: Scan results visible before approval decision
- Automated Audit Logging: Complete deployment history with compliance queries
- Communication Strategy: Stakeholder notification matrix with pre/post-deployment templates
- Emergency Procedures: Hotfix approval process with 30-minute SLA
- Rollback Automation: Automatic rollback-on-failure for UAT, documented procedures for PROD
- Health Checks: Post-deployment validation of Logic Apps, Storage, Service Bus, Application Insights
- Metrics & Reporting: Deployment success rate, approval times, rollback incidents
Documentation: DEPLOYMENT-GATES-GUIDE.md, GATED-RELEASE-IMPLEMENTATION-SUMMARY.md
- Interactive Configuration Wizard: Step-by-step guided configuration with validation (scripts/cli/interactive-wizard.ts)
- Synthetic 837 Claim Generator: PHI-safe test data for 837P and 837I claims (scripts/utils/generate-837-claims.ts)
- Azure Deploy Button Template: One-click sandbox deployment via azuredeploy.json
- E2E Test Suite: Comprehensive health checks with JSON reporting (scripts/test-e2e.ps1)
- CI/CD PHI Validation: 18 automated tests prevent PHI exposure (.github/workflows/phi-validation.yml)
- Troubleshooting FAQ: 60+ solutions across 9 categories (TROUBLESHOOTING-FAQ.md)
- Documentation Suite: QUICKSTART.md, enhanced ONBOARDING.md with 3 deployment options
Onboarding Time Reduction: 96% (2-4 hours → <5 minutes) Configuration Error Reduction: 87.5% (40% error rate → <5%) Test Coverage Increase: 41% (44 tests → 62 tests)
Documentation: QUICKSTART.md, ONBOARDING.md, ONBOARDING-ENHANCEMENTS.md
- Complete Visual Identity: Sentinel logo with holographic/neon circuit veins aesthetic
- Branding Guidelines: Comprehensive standards document (BRANDING-GUIDELINES.md)
- Absolute Black Design: Primary color palette with neon cyan (#00ffff) and green (#00ff88)
- Segoe UI Bold Typography: Consistent font usage across all materials
- Landing Page Transformation: Complete redesign with Sentinel aesthetic
- Repository-Wide Enforcement: Updated all references and documentation
Documentation: BRANDING-GUIDELINES.md, BRANDING-IMPLEMENTATION-SUMMARY.md
- Enhanced README.md with comprehensive features section and new capabilities
- Expanded QUICKSTART.md with post-v1.0.0 feature details
- Updated DEPLOYMENT.md with security hardening deployment section
- Enhanced DEPLOYMENT-SECRETS-SETUP.md with Key Vault migration procedures
- Null safety improvements in configuration validator
- JSON validation for all generated artifacts
- Workflow structure validation for Logic Apps Standard requirements
1.0.0 - 2025-11-21
This is the first production release of Cloud Health Office — the source-available, Azure-native, HIPAA-engineered platform that ends decades of payer EDI pain.
- Multi-Tenant SaaS Architecture: Configuration-driven platform supporting unlimited health plans
- CLI Onboarding Wizard: Complete deployment from worksheet to production in <45 minutes
- Zero-Code Payer Onboarding: Add new payers via JSON configuration without custom development
- Backend-Agnostic Design: Works with any claims system (core admin systems, custom platforms, modern cloud solutions)
- 275 Attachments: Clinical and administrative attachment processing with file validation
- 277 RFAI: Request for Additional Information outbound workflow
- 278 Authorizations: Prior authorization requests (inpatient, outpatient, referrals)
- 278 Authorization Inquiry (X215): Real-time status checks for existing authorizations
- 278 Replay Endpoint: HTTP endpoint for deterministic 278 transaction replay
- 837 Claims: Professional, Institutional, and Dental claims submission support
- 270/271 Eligibility: Real-time eligibility verification with 6 search methods
- 276/277 Claim Status: Claim status inquiries with date range filtering
- Appeals Processing: Appeals submission and tracking with 8 sub-statuses
- ECS (Enhanced Claim Status): Advanced claim status with extended data and 4 query methods
- Clearinghouse Integration: Native SFTP and API connectivity
- Change Healthcare Support: Ready for integration
- Optum 360 Support: Ready for integration
- Inovalon Support: Ready for integration
- Direct Payer Endpoints: Configuration-driven connectivity
- Zero-Trust Architecture: Private-endpoint-only, no public IPs
- Azure Key Vault Premium: HSM-backed keys (FIPS 140-2 Level 2)
- Private Endpoints: VNet integration for Storage, Service Bus, Key Vault
- PHI Masking: DCR-based redaction in Application Insights
- HIPAA Compliance: 100% technical safeguards addressed
- Automated Secret Rotation: API keys and credentials rotate automatically
- 7-Year Data Retention: Automated lifecycle management with tier transitions
- Audit Logging: 365-day retention in Log Analytics
- Complete Bicep Templates: All Azure resources defined in source
- Logic Apps Standard Workflows: 15+ production-ready workflows
- Modular Security Components: Key Vault, networking, private endpoints, CMK
- Multi-Environment Support: DEV/UAT/PROD configurations
- GitHub Actions Pipelines: Automated deployment with approval gates
- Configuration Schema: JSON Schema Draft-07 with 200+ validation rules
- TypeScript Interfaces: Type-safe configuration handling
- OpenAPI Specifications: Complete API documentation
- Example Configurations: Medicaid MCO and Regional Blues templates
- Comprehensive Documentation: 20+ detailed guides
- Real-Time Correlation APIs: Link attachments to claims
- Appeals Registration: Direct integration with claims backend Appeals API
- Authorization Processing: Complete authorization lifecycle management
- Eligibility Verification: Member eligibility checks with retry logic
- Retry Logic: 4 retries @ 15-second intervals for API calls
- Application Insights Integration: Telemetry and distributed tracing
- PHI-Safe Logging: Automated masking of sensitive data
- Custom Metrics: Authorization decisions, claim status, appeal tracking
- Health Checks: Automated verification post-deployment
- Dead-Letter Queues: Failed message handling and replay
- Onboarding time reduction: 6–18 months (legacy) → <1 hour
- Professional services cost elimination: $500k–$2M → $0 (bring-your-own-subscription)
- First production-grade claims backend REST correlation in source-available healthcare IT
- Complete source code transparency: No black boxes, fully auditable
- Azure Marketplace ready: Prepared for Managed Application publishing
- Deployment Target: Azure (Logic Apps Standard, Data Lake Gen2, Service Bus)
- Runtime: Logic Apps Standard (WS1+ SKU)
- Storage: Azure Data Lake Storage Gen2 with hierarchical namespace
- Messaging: Service Bus Standard tier with topics
- Security: Premium Key Vault with HSM-backed keys
- Monitoring: Application Insights with PHI masking
- Language: TypeScript (generator), Bicep (infrastructure), JSON (workflows)
Complete documentation suite includes:
- CONTRIBUTING.md: Development workflow and setup
- ARCHITECTURE.md: System architecture and data flows
- DEPLOYMENT.md: Step-by-step deployment procedures
- SECURITY.md: HIPAA compliance and security practices
- TROUBLESHOOTING.md: Common issues and solutions
- BRANDING-GUIDELINES.md: Sentinel brand identity standards
N/A - First release
- All dependencies audited and up-to-date
- No known vulnerabilities in production dependencies
- HIPAA compliance validated for all PHI handling paths
- Security hardening guide included in SECURITY.md
- Azure-only deployment (AWS/GCP support planned for Q1 2025)
- Integration Account X12 schemas must be manually imported post-deployment
- API connections require manual authentication configuration
- Azure AD Easy Auth configuration required for replay endpoints
N/A - First release
Special thanks to all contributors who made this release possible.
BSL 1.1 - see LICENSE file
The monolith has landed.
Legacy EDI integration is now optional.
Just emerged from the void.
Star ★ the repo if you believe payers deserve better than 1990s technology in 2025.