Changelog
All notable changes to fi-fhir will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased
End-to-end demo guide (2026-09-28)
- Documentation —
docs/user-guide/demo-end-to-end.md: the operator's script for the LAN demo environment (platform/gitops/k3s/fi-fhir/demo/: SFTP drop, S3 bucket, single-node Kafka, HAPI "St. Elsewhere" hospital), with a synthetic ADT^A01 batch, the drop recipes, the Connections-page values that reproduce the mounted source digests, and the honest state of ingestion: the deployed API polls as definitionsftp-test-demo, which nothing outside tests can create yet. Records the two ways to close that (alifecycle seedcommand mirroringdeployBatchRevisionin the batch proof, or the definition editor).
IDE design uplift — a calm, dense, precise Mapping Studio (2026-09-26)
- Evidence and direction (spec
.loom/37, MR !232) — 1440×900 screenshots of every production route showed four navigation layers above the content (the credential strip, a five-stage journey band with a "NEXT UP" card, an H1 hero with subtitle, and numbered stepper pills), marketing copy ("Build the interface from source to destination", "Mission control", "Recommended move"), simulated alerts labelled "Demo data" on the home page, and gradients, glows, pill badges and large cards where records belonged. The direction: neutral dark greys and one accent, 13 px UI type, 28 px controls, 36 px toolbars, tables for records, a page is a toolbar and its content, no simulated data. - U-0 (MR !233) — design tokens rewritten dark-first with the legacy token names kept, a
[data-theme="light"]set, and primitives inui/src/lib/ui/primitives/(Button, IconButton, Tabs, Badge, Panel, Toolbar, Table/Th/Td/Tr, Field/Input/Select/Textarea, EmptyState, KeyValue, Popover, Icon) on@lucide/svelte1.48.0; a dev-only/designgallery andui/docs/DESIGN.md. - U-1 (MR !235) — the shell: a 40 px header with the five-stage
StageControl(one tab stop, arrow keys), a breadcrumb, Commands and the theme toggle; the status bar carries connection state, the access chip (Trusted network, Cloudflare Access · name, or Bearer, with "Clear access" for bearer sessions only) and "Next"; the signed-in credential strip is gone (the sign-in is still announced to assistive tech) and the bearer entry is a centred dialog;JourneyProgress,DocumentHostand the editor-less document types are deleted and saved layouts drop them; Home and Operator are named the same in every place; a layout saved on Operator restores (operatorwas missing from the valid views); the document region pads 12 px unless the route renders aToolbar. - U-2 (MR !236) — Home is Recent (open documents and the tenant's integration sessions), Integrations (deployments, behind the same operator pre-flight as
/operator), Health (backend, GraphQL and the shell poll as table rows) and Alerts;observabilityStorehas no generators, andalertSource(unconfigured,live,unavailable) says why a list is empty; Events and Operator are toolbars over tables with details panes; a slow message-trace answer can no longer overwrite a newer selection;StreamingUnavailablekeeps its gate contract under a new contract test. - U-3 (MR !234) — HL7 intake is editor-first (the editor takes most of the page) with pipeline chips that show only what a run used, one status line and dense results tables; workflows, profiles and terminology are toolbars over tables with details panes;
AuthoringFlowRailandLifecycleTraceare deleted. Fixed on the way: several reactivity bugs (workflow readiness and publish enablement, terminology and sample bulk selection), a terminology upload that never refreshed the list, Inspector field clicks that did nothing, a failed preview that showed no error while the session engine was on, and editor text overwritten by unrelated sample-list updates. - U-4 (MR !237) —
test:ui-e2egains avisualproject that captures 1440×900 PNGs of every route, the bottom panel and the command palette as job artifacts, and fails if any retired marketing phrase reappears; no golden images are committed. - Production — UI
v0.1.29328verified from the LAN at 1440×900 on every route, including an HL7 Preview on the session engine.
IDE repair — honest surfaces, the operator bundle, production streaming (2026-09-25 → 2026-09-26)
- Root causes (spec
.loom/36, MRs !223 and !224) — measured from the LAN against the live deployment, none of them in the trusted-network path: the operator plane was forbidden for every identity because the deployment granted only the transport grantgraphql:operatorand never the service rolesintegration.operator,integration.delivery.operatorandintegration.deployment.operator(fixed in platform/gitops MR 805 with no code change; decision "Grant the operator bundle rather than alias the transport grant"); SSE streaming answered 404 because the Integration Session workspace was never enabled in production; the Copilot was gated on an unset loom platform endpoint; the Problems badge counted the diagnostics of the empty default draft;/api/auth/statussaid nothing about roles. - R-A (MR !225) —
/api/auth/statusreportsprincipal,roles, derivedcapabilities(operatorRead,operatorDelivery,operatorDeployment,clinicalRead,integrationSessions,streaming,subscriptions,llm.configured) andmissingRoles, evaluated through the same root-field role table the transport gate uses and checked in tests against the real operator service; the route and the probe share one authenticator, so a bearer presented from the LAN now reportsbearer;servelogs one WARN per configured identity that holds the grant withoutintegration.operator;scripts/check-runtime-config.sh,.env.exampleanddocker-compose.yamlrequire the bundle for the local IDE. - R-B (MR !226) — the IDE reads that contract: the operator page pre-flights on
capabilities.operatorReadand names the missing roles instead of issuing a query the service will refuse; every streaming surface is keyed per subscription root (streaming-unavailablewithdata-streamanddata-reason), and the four legacy panels (eventStream,workflowEvents,debugStepEvent, Runtime Output) render the honest state and never subscribe, because the SSE allowlist admits onlyintegrationSessionEventsandsessionRunEventsby design; the session engine runs only when the build flag is on andcapabilities.integrationSessionsis true; the Copilot runs on the backendllmCapability(copilot-llm-state) and the loom platform chrome is hidden unlessPUBLIC_LOOM_ENDPOINTis set; the Problems badge counts only a live draft; the/healthpoller keeps one request in flight and pauses while the tab is hidden. - R-C (MR !228) —
ui/Dockerfilebuilds withVITE_FI_FHIR_INTEGRATION_SESSION_ENABLED=true(the API decides at runtime);INTEGRATION-SESSIONS.mdgains the production section (the one env entry, the database it migrates, verification, rollback) andRUNBOOK.mdthe "Live streaming is unavailable" section; the SSE client keeps each in-stream error'sextensions.code(GraphQLStreamError.codes) and the classifier keys onFORBIDDEN, because the server's catalog-safe presenter rewrites every forbidden message to "GraphQL operation forbidden";buildOnApiOff.test.tsproves "build flag on, API sessions off" end to end through the real gate, store, pages and clients with onlyfetchfaked. - R-D (MR !227) — blocking
test:ui-e2e: Playwright (Chromium) against the built UI served by the production nginx template in front of three realfi-fhir servestacks on PostgreSQL 16 (the operator bundle with sessions on; the bundle minusintegration.operator; sessions off), asserting the status contract, the operator list with no pre-flight and no "forbidden", anintegrationSessionEventsstream answering 200text/event-streamwhile Live Stream shows the honest state, the Copilot's LLM state, the absent Problems badge and Platform indicator, and both negative controls;check-report.mjsfails the job if any check did not run and pass;make ui-e2emirrors it on the docker host. First CI run: 7/7 in 249 s. - Production (platform/gitops MRs 805 and 811) — every IDE identity (static bearer, trusted network, both Access principals) holds the operator bundle, and
FI_FHIR_INTEGRATION_SESSION_ENABLED=trueonfi-fhir-apiturned the Integration Session workspace on: the store migrated itself into the durable PostgreSQL already open for the operator control plane; no retention key is mounted, so raw retention stays refused; signed publication stays off. Verified from the LAN:integrationSessionEventsanswers200 text/event-stream, andeventStreamanswers theFORBIDDENevent by design. - R-E (MR !229, from R-D's finding) — HL7 intake sends CR-terminated segments on every path (stateless preview, Integration Session run, process/submit) whatever the editor holds: CodeMirror joins lines with LF and pasted files carry LF or CRLF, and the preview kernel's strict validation accepts only CR. The editor text is left as typed; the manual "Normalize newlines" action stays. The built-in sample was never executable — literal
\rcharacters for separators, a doubled backslash in the MSH-2 encoding characters, and no EVN segment — and is now a CR-terminated four-segment ADT^A01.internal/parser/hl7v2already split segments tolerantly; a table test now pins identical parses across CR, LF and CRLF so that stays true. The strict line-ending gate is deliberately unchanged. - Close-out — this roadmap and changelog, the decision entry, and
ci/test-ui-e2e.ymlnow runs when its own definition changes (test:binarywith it, through the runtime-verification anchor).
Sprint 7 — FHIR conformance, operator trace, harness truth (2026-09-24 → 2026-09-25)
- Slice 5.1c-α (MR !216) — the seven US Core cardinality violations recorded by the structural validator are closed in the mapper:
CareTeam.participantis projected from the event (no members → no CareTeam),Coverage.relationshipisselforunknown, aDocumentReferencewith no attachment is no longer emitted (andcontentcan no longer serialise as JSONnull),Encounter.identifier.systemis qualified under the deployment-ownedurn:fi-fhir:source:<source>via the newUSCoreMapper.Source,Encounter.typederives from PV1-2 through the pinned US Core binding (SNOMED CT for inpatient and emergency, HL7 table 0004 otherwise), and vital-signeffective[x]is the event timestamp. Missing data becomes US Core'sunknowndata-absent reason, never an invented value.recordedCardinalityGaps()is empty; all 25 fixtures validate clean; the negative control still turns red on exactlypatient.json. - Slice 5.1c-β (MR !219) — the HL7 official validator (
validator_cli.jar6.10.4, sha256-pinned, never vendored) runs offline in the new blockingtest:fhir-officialjob (withtest:fhir-official-captureemitting the 11 delivered Bundles as artifacts) against R4 4.0.1 and US Core 9.0.0. The validator demands 23 archives, not 2: its own terminology and extensions defaults plus US Core's transitive closure. All 21 additional archives are pinned undertestdata/fhir/packages/with digests reproduced against the registry; an online run with an empty cache produced the identical package summary and identical findings. Findings (157 over 36 inputs: 28 errors, 86 warnings, 43 information) are held by exact equality intestdata/fhir/official/findings.ledger.txt; a new or vanished row fails the build.make fhir-officialandmake fhir-official-negative-controlmirror the job. Matrix §5 row 2 is ratified in full;SUPPORTED-1.0.mdstates what the evidence establishes. - Slice 4.2c (MR !217) —
OperatorDeliveryAttempt.deliveries: [OperatorDestinationDelivery!]!projectsintegration_destination_deliveriesinto the operator control plane: transport, destination revision and class, verified digest, outcome, failure code, HTTP status class, endpoint and served-certificate advisories, completion time, and thefhirfacts (resource types, entry count, OperationOutcome issue codes). Newest first, tenant-scoped, bounded (5 per attempt on lists, 25 on a single attempt).operatorreads it through aDestinationDeliveryReaderseam;cmd/fi-fhirapplies the existing provenance ledger migrations; no new migration and no new root field, so the transport gate is unchanged. The message trace renders it per attempt and the delivery console exposes it on demand; both show ledger fields only. Day-1 gate proved main exposed nothing from the ledger; inverted at ship. - Performance harness (MR !215) —
scripts/performance-report.shsetcertified: truewheneverFI_FHIR_PERF_RUNNERwas1, comparing no measurement to any budget. It now requires budget 1's p95/p99 to be met on a runner reporting thefi-fhir-perftag in a non-regression build; the benchmarks report p50/p95/p99; a-tags perfregressnegative control (test files only) sleeps 300 ms per measured accept and must fail budget 1 (exit 3 otherwise); report schema 2 recordsnegative_controlandrunner_id; budget 3 readsnot_measured. A unit test runs the real script and fails against the old one. No budget is certified: runner 8's 11 GiB pod cannot schedule on its pinned node today. - CI (MR !218) — quay.io and Docker Hub withdrew
minio/minio;test:integrationandtest:batch-ingestionnow pull both pinned MinIO releases from the internal registry by digest, mirrored byte-for-byte. - Close-out —
events.EventMetagains a promotedMeta()accessor andfhirout.mapEventsetsUSCoreMapper.Sourcefrom it, so the legacy workflowfhiraction's raw Encounter identifier is qualified underurn:fi-fhir:source:<source>exactly as the durable path'sensureIdentifieralready made it; a typed-nil event still yields the invalid-payload error rather than a panic. Delivered Bundles and the official-validator ledger are unchanged.
FHIR delivery
- Deliver condition, procedure, immunization, vital sign, medication request, and allergy intolerance events through the shared workflow and durable FHIR projector.
- Honor workflow resource selection and resolve transaction references; unsupported JSON events now fail instead of silently producing only a Patient (issue #20).
- Preserve medication substitution
allowedBoolean: falsein JSON and require live HAPI FHIR read-back in the legacy E2E CI gate.
Security
- Add the
clinical:readGraphQL transport role for the ten PHI-reading event, patient, and projection queries while preserving thegraphql:operatorcompatibility grant.
Added
-
fi-fhir lifecycle seed(cmd/fi-fhir/lifecycle_seed.go): the supported way to create a batch integration definition in the PostgreSQL lifecycle catalog outside tests. It builds the revision from the mounted batch source revision (--source), the profile and workflow refs of one static-registry entry (--integration; the batch runner loads those bytes from the static registry, so the seed resolves them with the runtime's ownprocessor.RevisionResolverfirst), and destination revisions (--destination), then runs CreateDraft → ValidateConnection → Approve → Publish (--through deployedadds Deploy) at the expected snapshot version under--principaland--reason.--validate reallists one object of the input location through the providerservebuilds fromFI_FHIR_BATCH_*(codesSOURCE_REACHABLE,HOST_KEY_VERIFIED,AUTH_OK,INPUT_LISTED, orSOURCE_CONNECT_FAILED/INPUT_LIST_FAILED);--validate skiprecordsVALIDATION_SKIPPED. Re-runs resume from the stored state and refresh stale evidence; different content under the same revision ID is refused, never overwritten; a workflow that delivers to a destination the definition lacks is refused.--dry-runprints the revision and refs without a database;--destination-registry-outemits the delivery worker'sfi-fhir/destination-registry/v1document for the definition. Blocking CI prooftest:lifecycle-seed(ci/test-lifecycle-seed.yml,make lifecycle-seed): seeded through published, resumed to deployed, and ingested by the batch runtimeservebuilds, against PostgreSQL 16 and an in-process SFTP server.batch.SourceRevision.SecretBindingNamesis now exported. -
CI job
lint:edi-fixtures(ci/lint-edi-fixtures.yml) runs edilint, pinned tov0.1.0, overtestdata/edi/*.ediagainst the committedtestdata/edi/.edilint.ymlon every merge request, and publishesedilint-report.xmlas a JUnit report so envelope defects show up in the MR test panel. It runs beside the existinglint:edi, which is unchanged and still checks the same corpus on edilint's stock rules with no config file.testdata/edi/.edilint.ymlwaives EL3009 (duplicate interchange control number): the fixtures are independent interchanges and control-number uniqueness across the corpus is not an invariant the suite maintains.TestFixtureTrailerCountsMatchTheirTransactionSetspins every fixture's SE01 against the segments it actually contains ingo test ./internal/parser/edi/..., without needing the network — the three miscounts edilint originally found were already repaired onmainindf45f683, and nothing until now kept them repaired.make lint-edi-fixturesmirrors the job locally at the same pin and config, besidemake lint-edi, which still mirrorslint:edi. -
Kafka, Redis Streams, and Google Cloud Pub/Sub event backends with acknowledgment-aware consumers, workflow queue drivers,
workflow consume, shared handlers, and an event-sourcing outbox adapter. -
GraphQL callers can be authenticated by the identity Cloudflare Access verified at the edge: with
FI_FHIR_GRAPHQL_ACCESS_TEAM_DOMAIN,_AUDIENCE, and_PRINCIPALSset, the runtime verifies theCf-Access-Jwt-Assertiontoken (or theCF_Authorizationcookie) against the team domain's keys and exact application audience, and grants each listed email exactly the roles the deployment maps to it. Works beside either bearer mode; anAuthorizationheader keeps precedence./api/auth/statusreportsauthVia: "cloudflare-access"with the principal, and the IDE's credential gate steps aside for it, so a Google sign-in through Access carries straight into the IDE without a pasted token.
Integration Runtime Foundation
- Public
pkg/integrationcontracts for content-addressed integration revisions, tenant/actor identity, typed secret references, PHI/raw-retention policy, production/preview requests, and stable processing results - Golden Path 001 revision fixture with strict decoding, deterministic semantic digest validation, non-serializable raw payload bytes, and preview side-effect invariants
- Immutable Source Profile revision pointers plus exact profile/workflow artifact resolution with domain-separated content digests and single-deployment tenant enforcement
- Required PostgreSQL CI proof that pinned profile/workflow v1 artifacts survive v2 publication, process reconstruction, owner checks, and digest verification
- Internal preview-only
MessageProcessorthat resolves a server-owned integration revision and exact immutable artifacts, then produces deterministic, raw-free ADT A01 events, route plans, diagnostics, and suppressed deliveries - Strict published-workflow DSL v1 parser and pure CEL route planner with bounded YAML resources, closed action types, safe diagnostics, stable action identity, and no execution-capable dependencies
- Strict executable Source Profile compiler plus one-message HL7v2 validation, standards-correct DTM offsets and precision, source-time precedence, and deterministic event identity
- Blocking PostgreSQL preview-kernel proof that reconstructs fresh stores after v2 publication while preserving byte-identical v1 behavior and exact v2 semantics
- PostgreSQL-only production admission on the same
MessageProcessorsemantics, atomically committing the durable receipt, sanitized canonical event, exact lineage, initial delivery attempts, and pending transactional outbox rows - Deterministic tenant-scoped idempotency with explicit-key precedence, source/MSH-10/revision derivation, request-fingerprint conflicts, and commit-unknown recovery through the first durable result
- Blocking PostgreSQL 16 race gate that injects every transaction-boundary fault, restarts all handles, and collapses 64 concurrent submissions to one durable admission unit without persisting raw source bytes
- Authenticated, bounded
POST /v1/hl7v2production ingress with bearer or domain-separated HMAC-SHA256 credentials, service-principal attribution, server-owned integration/source identity, structured retry semantics, and a PHI-free receipt/event/warning/provenance/delivery response make golden-path-001Compose/CI gate with PostgreSQL 16 migrations, valid duplicate and idempotency-conflict probes, real process restart, strict versus tolerant profile proof, production/IDE semantic parity, durable cardinality, JUnit/JSON/SQL evidence, and raw/credential leakage scans- One typed
previewIntegrationMessageadapter backed by a strict server-owned integration registry and the canonicalMessageProcessor, plus a Mapping Studio credential gate that keeps the [REDACTED] raw samples in tab memory - Supported 1.0 target matrix with a pinned Kubernetes 1.36 minor and explicit phase release gates
- Backward-compatible integration deployment policy for connection-validation freshness, continuous/cron schedules, health thresholds, and capacity limits
- PostgreSQL versioned integration lifecycle with optimistic commands, auditable failed validation, immutable releases/history, pause/resume/retire, health projection, and exact revision resolution only while deployed
- Blocking PostgreSQL 16 lifecycle gate covering the full state journey, 32-caller concurrency, immutable-row rejection, restart reconstruction, and raw/secret leakage scans
- Content-addressed UTF-8 MLLP source revisions with bounded framing, timeouts, TLS 1.3 mutual authentication, canonical client CIDRs, capacity, and application/commit acknowledgement policy
- Optional
serveMLLP runtime that resolves only the lifecycle catalog's exact deployed release and serializes pause/retire authorization inside the durable PostgreSQL admission transaction before a positive ACK - PostgreSQL delivery leases, bounded exponential retry, destination-revision circuit state, durable DLQ, append-only audit, and idempotent replay/resubmit
- Optional
serveKafka outbox worker with stable attempt keys, sanitized canonical-event commands, all-ISR acknowledgement, TLS 1.3, and TLS-required SASL credentials - Authenticated PostgreSQL operator commands for audited
delivery replayanddelivery resubmit, plus a blocking PostgreSQL 16/Kafka failure-recovery gate - Content-addressed S3/SFTP batch sources with exact deployed-release binding, bounded concatenated-HL7v2 streaming, PostgreSQL leases/checkpoints, and deterministic crash-safe durable admission identity
- Optional
servebatch worker with TLS-protected S3 credentials, mandatory SFTPknown_hosts, symlink rejection, and verified SHA-256-addressed archive-before-delete semantics with S3 version IDs and an immutable SFTP drop contract plus immediate pre-delete digest verification - Blocking PostgreSQL 16/MinIO/SSH-SFTP gate covering replica exclusion, lease reclaim, the admission/checkpoint kill window, source mutation, host-key rejection, archive integrity, exact durable cardinality, and raw-PHI exclusion
- Slice 2.4 evidence: MR
!108pipeline19331passed 35/35, required batch job186259passed, merge commited32915frepeated the change on main, and main pipeline19344passed 38/38 with independent batch job186476 - Opt-in PostgreSQL Integration Session workspace with stable create/list/reopen/ archive routes, redacted samples by default, AES-256-GCM explicit retention, append-only artifact revisions, immutable terminal runs, durable accepted decisions/exports, and exact profile revision/digest preview provenance
- Required PostgreSQL 16 restart gate that reconstructs the workspace service, compares strict/tolerant profile outcomes, and proves no raw-PHI sentinel is persisted in session records
- Slice 3.1 evidence: MR
!111pipeline19409passed 37/37, required session job187425passed, merge commit15746ccdrepeated the change on main, and main pipeline19424passed 40/40 with independent session job187618 - Feature-gated authenticated GraphQL SSE for Integration Session run/stage/ diagnostic snapshots while WebSocket remains closed, with a transport-level session-subscription allowlist and durable terminal-run reconciliation
- Mapping Studio live server progression, deduplicated Problems diagnostics, and canonical HL7 inspector lineage navigation including repeated OBX fields; raw retained samples and persisted lineage value previews stay server-side
- Slice 3.2 evidence: MR
!115pipeline19464passed 34/34, required session job187950and benchmark job187953passed, merge commit36f2bb8crepeated the change on main, and main pipeline19482passed 37/37 with independent session job188135 - Durable Workflow Builder simulation against explicit immutable Integration Session runs and one exact append-only workflow revision, with production-pure route planning, configuration-free event/route/transform/action traces, deterministic prior-run deltas, restart-safe PostgreSQL storage, and no browser-supplied event payloads or action execution
- Append-only, versioned Integration Session publication with PHI-minimal fixture/expectation manifests, domain-separated digests, detached Ed25519 signatures, exact session-to-production content verification, trust-root-gated approval/deployment through the existing lifecycle catalog, and safe resume from an already-published release
- Source Profile review now compares the loaded immutable baseline with the edited draft using aligned line diffs and stores a required authenticated change summary with each immutable revision
- Integration Session exports now preserve arbitrary artifact content as opaque bytes, allowing YAML workflow revisions to round-trip safely alongside JSON profiles
- Slice 3.3 evidence: MR
!122pipeline19872passed 37/37 with required session job191685and benchmark job191688, merge commitd42f7233repeated the change on main, and main pipeline19878passed 40/40 with independent session job191786and benchmark job191789 - Blocking PostgreSQL 16/TCP MLLP gate covering pre-commit ACK exclusion, concurrent pause serialization, 32 reconnecting duplicates, resume, retirement, restart, durable cardinality, and raw-message leakage
- Slice 2.2 evidence: MR
!104pipeline19175passed 33/33, required MLLP job184996passed, merge commit6205fa39repeated the proof in main job185093, and main pipeline19193passed 36/36 - Verified MLLP client-certificate service identity: an optional
clients.identitiesallowlist in the immutable source revision maps an authority-scoped URI SAN and/or SPKI SHA-256 pin to one canonical service subject and its grants, resolved per connection immediately after the TLS handshake and before any frame is read - CA-valid MLLP certificates that map to zero or to multiple configured identities are closed without an acknowledgement, before artifact loading and before any durable record exists
- Mapped MLLP identities flow into the same fail-closed
integration.submitdecision as the HTTP ingress, over the exact tenant, integration revision, and registry-owned source, so an identity without a recognized submit grant authenticates but never admits FI_FHIR_MLLP_REQUIRE_CLIENT_IDENTITYrefuses to start a listener in certificate-identity compatibility mode; omitting the identity map preserves the existing deployment-fixed principal, server-issuedintegration:mllpgrant, and exact source-revision digests- Batch (S3/SFTP) workload identity: an optional
workloadblock in the immutable source revision names one canonical service subject and its grants, and nothing observed on the remote side — object keys, remote metadata, or MSH content — can select or influence it - The batch connector evaluates the shared fail-closed
integration.submitdecision before listing, leasing, opening, reading, artifact loading, or any durable write, so an ungranted subject leaves no lease or checkpoint state to poison a later retry FI_FHIR_BATCH_REQUIRE_WORKLOAD_IDENTITYrefuses to start a batch source in compatibility mode; omitting theworkloadblock preserves the existing deployment-fixed principal, server-issuedintegration:batchgrant, and exact source-revision digests- Per-root-field GraphQL transport-gate roles over all 131 schema root fields
with default-deny, a compile-time exhaustiveness test against the schema the
server executes, and a
transportgateblanketbuild tag that restores the old blanket allow as the kill-test's negative control test:transport-gateCI job andmake transport-gate/make transport-gate-negative-control- FHIR conformance proof (Slice 5.1a): every one of the mapper's 26
Map*entry points is driven with a representative event and every resource it produces is fed back throughValidateJSONat--mode us-core --strictwith zero issues, bound to the type by reflection so a new entry point without a row turns it red. Generated golden fixtures undertestdata/fhir/mapper/hold the mapper's exact bytes for all 21 checked resource types, plustestdata/fhir/diagnosticreport_uscore_lab.jsonin the curated set.make fhir-conformanceandmake fhir-conformance-negative-control, the latter restoring the pre-slice DiagnosticReport accepted set and requiring exactly theMapLabResultrow to fail TestFHIRConformance_DurableEngineProducesNoFHIRResource: the durable engine delivers a Kafka delivery-command envelope atapplication/json, the destination transport vocabulary is{kafka, https}with no FHIR class, and no package underinternal/integrationimportspkg/fhir. Executes the kill-test.loom/28-spec-fhir-ig-bulk-smart.mddefined for the moment Slice 4.1c-b merged; its answer is that Slice 5.1 remains blocked on a FHIR destination class (4.1c-c) that does not yet existfhir.ParseValidationMode,fhir.ValidationModes,fhir.ProfileCanonical, andfhir.USCoreDiagnosticReportLabProfile
Changed
-
The decision journal is one file per entry under
.loom/decisions/(make decisions-new TITLE=...,make decisions), and.loom/40-decisions.mdis a pointer page, mirroring the August worklog split.scripts/decisions.sh checkruns inlint:docsand rejects a dated entry appended to the pointer page or a second heading in an entry file. The single append-only journal had re-conflicted every open Sprint 5 merge request on each sibling merge. -
build:dockerandbuild:docker-uipass--network=hosttodocker build. Inside the Docker-in-Docker service the default per-build bridge network intermittently blackholed Alpine package fetches (RUN apk ...hung until the one-hour job timeout four times on 2026-09-02, while image pulls through the daemon and the same build on another runner slot succeeded); host networking routes RUN steps through the daemon's own egress. -
test:benchmarkis non-blocking on merge-request pipelines (allow_failure: trueon the manual rule). It stays manual so it can be played for performance-sensitive changes, and stays blocking on tags and the default branch. Previously the unplayed manual job held every Go-touching MR pipeline at statusmanual, so merge-when-pipeline-succeeds never fired and MRs could not merge without a human playing the job. -
Batch receipt provenance no longer trusts remote object modification time. The authoritative
received_atis now the server-owned custody timestamp recorded when an exact object version is first durably admitted, stable across lease reclaim, worker restart, and checkpoint resume -
Batch content provenance is now a SHA-256 digest computed over the exact bytes streamed during admission, resumed across checkpoints from marshaled hash state and cross-checked against a full re-read before archive; a disagreement quarantines the object with
DIGEST_MISMATCHinstead of archiving it -
S3 batch objects now pin the entity tag alongside the exact version ID and re-verify both at every read, archive, and delete
-
integration_batch_objects.object_modified_atis renamedremote_modified_at_advisoryand joined byobject_version,object_etag, anddigest_state(migration0002_batch_provenance). The provenance CHECK isNOT VALIDso rows admitted before this revision stay visibly distinguishable rather than being given invented provenance -
Operator control-plane GraphQL API over the existing durable delivery and lifecycle records: tenant-scoped, keyset-paginated browsing of receipts, canonical events, receipt-to-delivery lineage, delivery attempts, dead letters, destination circuits, delivery audit, and deployment inventory
-
Policy-aware semantic payload rendering that returns a canonical event's field coordinates, JSON kinds, and repetition flags while never returning a stored value, a value length, or a caller-influenced map key
-
Reason-required, role-gated, idempotent operator mutations for delivery replay, resubmit, and dead-letter discard, plus lifecycle pause, resume, retire, and deploy with expected-version optimistic concurrency, all delegating to the existing operation ledger and append-only audit trail
-
Durable
discardrecovery decision with a dead-letter resolution column, so a closed dead letter records whether it was replayed, resubmitted, or abandoned -
Blocking PostgreSQL 16 operator gate that completes the failure/replay and operator-audit golden journeys over the real GraphQL handler with a verified OIDC operator identity, proves duplicate control actions do not double-execute, proves unprivileged and cross-tenant callers reach no data and change no state, and proves a planted raw-PHI sentinel never leaves the process
-
Operator control-plane workspace at
/operatorin the IDE: durable message browser with server-owned filters and cursor paging, receipt-to-delivery trace drill-down, dead-letter and destination-circuit console, and deployment controls, with one reason-required dialog fronting every mutating action -
Operator actions the engine would refuse are disabled with an explanatory reason instead of failing after the click, and optimistic-concurrency conflicts reload the durable record and ask the operator to re-decide rather than retrying silently
Format Adapters
- CDA/CCDA clinical document parser with namespace-aware XML handling (
internal/parser/cda/) - CDA section handlers for structured data extraction (
internal/parser/cda/sections/) - CDA-to-canonical event mapper (
internal/parser/cda/mapper.go) - FHIR R4 resource parser for inbound FHIR ingestion (
internal/parser/fhir/) - HL7v2 MDM messages (T01–T11) — Medical Document Management with TXA/OBX support
- HL7v2 DFT messages (P03, P11) — Detail Financial Transactions with FT1/DG1/PR1/IN1
- HL7v2 VXU immunization messages
- HL7v2 RDE pharmacy messages
- EDI X12 270/271 eligibility inquiry and response transactions
- EDI X12 276/277 claim status inquiry and response transactions
- EDI companion guide framework with built-in payer guides (
internal/parser/edi/companion/) - Built-in companion guides: Medicare, BlueCross, United Healthcare (
companion/builtin/) - Companion guide validator and path-based field resolution
Event Sourcing / CQRS
- Event store interface with append-only semantics (
pkg/eventsourcing/store.go) - In-memory event store for testing (
pkg/eventsourcing/memory_store.go) - PostgreSQL event store for production (
pkg/eventsourcing/postgres_store.go) - Projection framework with checkpointing (
pkg/eventsourcing/projection.go) - Snapshot store interface with memory and PostgreSQL implementations
- Healthcare projections: patient timeline, event statistics, active encounters (
pkg/eventsourcing/projections/) - Event replay tooling with ProjectionRebuilder (progress, dry-run, snapshot-aware)
- Time range queries for point-in-time recovery (
pkg/eventsourcing/time_range.go) - Event archival and HIPAA-aware retention policies (
pkg/eventsourcing/archive.go) - Event stream compaction with aggregate snapshots (
pkg/eventsourcing/compaction.go) - Saga orchestration for multi-step transactions with compensation (
pkg/eventsourcing/saga.go) - Outbox pattern for reliable event publishing (
pkg/eventsourcing/outbox.go) - CLI commands:
eventstore init|stats|streams|read|append,projection list|status|run|rebuild
FHIR Resources
- US Core Patient, Encounter, Observation, DiagnosticReport (enhanced)
- US Core Condition, Procedure, Immunization, MedicationRequest
- US Core AllergyIntolerance, CarePlan, Goal, CareTeam, ServiceRequest
- US Core DocumentReference, DiagnosticReport (clinical notes), Provenance
- US Core Location, Organization, Practitioner, PractitionerRole, RelatedPerson
- Observation (Vital Signs) with 8 specific US Core profiles
- Da Vinci PAS Claim resource (837P → FHIR)
- PDex ExplanationOfBenefit resource (835 → FHIR)
- CoverageEligibilityResponse resource (271 → FHIR)
- FHIR Coverage resource (US Core)
- FHIR resource validation with configurable failure policy (warn vs error per profile)
- FHIR validation golden fixtures for high-volume resources
GraphQL API
- GraphQL schema with queries, mutations, and subscriptions (
internal/api/graphql/schema.graphql) - GraphQL schema retains legacy query, mutation, and subscription types; the deployed preview-role configuration authorizes only authenticated POST health/preview and leaves WebSocket unmounted
- Resolver implementations: event queries, workflow triggers, FHIR subscription CRUD
- Batch event submission endpoint (
submitBatchmutation with parallel/sequential modes) - DataLoaders for N+1 query prevention (
internal/api/graphql/dataloaders/) - Projection resolvers wired to event sourcing service layer
- GraphQL codegen with gqlgen and CI validation (
lint:gqlgen)
Terminology System
- LOINC file loader with panel expansion (
pkg/terminology/loinc.go) - ICD-10-CM loader with ETL pipeline integration (
pkg/terminology/db/icd10.go) - RxNorm loader and cross-walk queries (
pkg/terminology/db/rxnorm.go) - UMLS API integration with rate limiting, caching, ticket auth (
pkg/terminology/umls.go) - Cross-walk queries: ICD-10 ↔ SNOMED, RxNorm ↔ NDC
- Fuzzy terminology matching with confidence scoring (
pkg/terminology/fuzzy.go) - Terminology version pinning and registry/index (
fi-fhir terminology status|use) - Version-aware validation modes: pass / warn / error
- Semantic search engine (
pkg/terminology/semantic/) - Suggestion engine with feedback loop (
pkg/terminology/suggest/) - Full-text terminology indexing (
pkg/terminology/index/) - Mapping file upload pipeline (
pkg/terminology/upload/) - Automatic terminology routing engine (
internal/terminology/autoroute/) - Temporal workflow activities for terminology operations (
internal/terminology/workflow/)
LLM Features
- Multi-provider LLM client with retry and rate limiting (
pkg/llm/) - Embedding generation for semantic search (
pkg/llm/embeddings.go) - Natural language explanation generation (
internal/llm/explain/) - Structured data extraction from documents (
internal/llm/extract/) - Data quality analysis with scoring (
internal/llm/quality/) - CEL-based copilot actions (
pkg/llm/copilot/) - LLM-integrated workflow actions (
internal/workflow/actions_llm.go)
Patient Matching
- Deterministic matching rules: SSN, MBI, MRN exact match (
pkg/matching/deterministic.go) - Probabilistic scoring: Jaro-Winkler, Soundex, Levenshtein (
pkg/matching/similarity.go) - Combined matcher with configurable thresholds (
pkg/matching/matcher.go) - Master Patient Index (MPI) interface with in-memory implementation (
pkg/matching/mpi.go) - Batch matching with blocking keys for performance
UI / Mapping Studio
- SvelteKit 5 frontend with feature-based architecture (
ui/src/) - HL7 Inspector with segment/field viewer (
ui/src/lib/features/hl7/) - Profile Selector and Profile Draft Panel
- Sample Inbox for test message management
- Terminology Editor with mapping browser and uploader
- Autoroute Resolver and Pending Review List
- Workflow Builder with visual route/action/transform editors
- Workflow Monitor and Dry Run Panel
- Event Stream Panel for real-time event viewing
- System Status Panel
- LLM Extraction Panel
- Generate-from-description (natural language → workflow)
- Reusable UI component library: Badge, Button, Toast, Tooltip, Tabs, etc.
- Authenticated GraphQL HTTP preview client; subscription consumers fail locally while production WebSocket transport is disabled
- OpenAPI-generated type-safe API client
Source Profiles
fi-fhir profile infer— generate profile skeleton from sample messagesfi-fhir profile lint— schema validation + opinionated warnings- Vendor profile templates: Epic, Cerner, Meditech, Allscripts
- Template selection guide and feed-specific fork workflow
- Inference fixtures and golden outputs
Workflow Engine
- Email action (SMTP/SES; templated subject/body; retries + circuit breaker)
- File action (templated paths; atomic writes; rotation/retention)
- Exec action (allowlist + timeouts for custom scripts)
- LLM action for AI-powered workflow steps
- Event replay and simulation tooling (
internal/workflow/replay.go,simulation.go) - Performance benchmarking (
internal/workflow/benchmark_test.go) - Load testing utilities (
internal/workflow/loadtest.go)
ETL Pipeline
- Source/sink framework with provider abstraction (
pkg/etl/source/,pkg/etl/sink/) - CLI commands:
etl fetch,etl load,etl validate - Storage provider abstraction (file, S3, MinIO) (
pkg/storage/)
Observability
- Prometheus metrics adapter (
internal/workflow/metrics_prometheus.go) - OpenTelemetry distributed tracing adapter (
internal/workflow/tracing_otel.go) - Structured JSON logging with trace correlation (trace_id, span_id)
- Grafana dashboard templates (
dashboards/grafana/) - Prometheus alerting rules: standalone + Kubernetes PrometheusRule CRD (
dashboards/alerting/) - Health check endpoints: /health, /ready (
internal/workflow/health.go) - Log correlation with trace IDs (
internal/workflow/logging.go)
Reliability
- Retry with exponential backoff for HTTP actions (
internal/workflow/retry.go) - Circuit breaker pattern for failing external services (
internal/workflow/circuit_breaker.go) - Dead letter queue for failed events (
internal/workflow/dlq.go) - Rate limiting (token bucket) for high-volume streams (
internal/workflow/ratelimit.go) - Configuration validation (
internal/workflow/validate.go)
Testing
- End-to-end test framework with Docker Compose integration (
test/e2e/) - PostgreSQL integration tests with testcontainers
- CLI offline stubs + live tests (
-tags=live) - Performance benchmarks for workflow engine
- Load testing runner with event generators
- FHIR validation golden fixtures
Deployment
- Multi-stage Dockerfile with distroless base (enhanced)
- Kubernetes manifests with Kustomize overlays
- Helm chart with full templating: HPA, PDB, ServiceMonitor (
deploy/helm/fi-fhir/) - GitLab CI/CD pipeline with blocking lint, test, benchmark, security, build, image-scan, and API/UI publish gates
- Harbor container registry integration with automated pushes
- UI Docker image with Nginx serving
- Coordinated Kubernetes rollout of matching API/UI images behind suspended Flux automation, with live auth, origin, containment, provenance, and PHI-leakage probes before a reviewed automation resume
- Cross-platform release binaries (linux/darwin/windows × amd64/arm64)
- Helm OCI + npm registry publishing on tags
SDK
- TypeScript SDK with CLI wrapper (
sdk/typescript/) - Type definitions for events, workflow, and profiles
- Platform-specific optional dependency packaging (darwin/linux/windows)
- npm publish pipeline in CI
Documentation
- OpenAPI 3.1 specification for REST API (
api/openapi.yaml) - Production hardening guide for HIPAA compliance (
docs/operations/PRODUCTION-HARDENING.md) - Operations runbook with troubleshooting procedures (
docs/operations/RUNBOOK.md) - User guide: getting started, core concepts, CLI reference, playground tutorial
- Developer guide: architecture, setup, testing, adding parsers
- Mermaid architecture diagrams (overview, parsing phases, CLI flow, UI mapping)
- Planning documents for all major features (14 design docs)
- Component status matrix (
docs/STATUS.md) - Documentation conventions (
docs/DOCUMENTATION-CONVENTIONS.md)
Changed
- CI
test:integrationpromoted fromallow_failure: trueto a blocking merge gate (24/24 green onmain, pipelines 18521..22333). It now protects the terminology DB store and the Lane C1 autoroute expiry-sweep kill-test on every merge request. - CI
lint:docspromoted to a blocking merge gate (33/33 green onmain). Runmake docs-validatebefore pushing. - CI
test:docs-statusdeliberately remains advisory; promotion criteria are documented inline in.gitlab-ci.ymland in.loom/40-decisions.md.
Fixed
-
The X12 fixtures declare the segment count they actually contain.
271_rejected.edisaid 13 where it had 12,271_response.edisaid 23 where it had 22, and277_denied.edisaid 18 where it had 19 — trailers a receiving trading partner rejects, repaired in the corpus bydf45f683and never recorded here. The parser recomputesSegmentCountfrom the segments it reads and never compares it with the declared SE01, so nothing caught the drift;TestFixtureTrailerCountsMatchTheirTransactionSetsand thelint:edi-fixturesCI job now both do. -
workflow validate,workflow run, andworkflow dry-runreject invalid CEL, transform, and built-in action configuration before reading events. Validation diagnostics include severity, code, and path; warnings remain non-blocking (#21). -
FHIR validation no longer fails open on the mode string.
ValidationOptions.Modewas compared byte-exactly againstus-core, so any other value — includingUS-Core,uscore, and""— silently disabled both the required-element and the profile-presence checks and reported a non-conformant resource as clean;fi-fhir fhir validate --mode US-Coreprinted "FHIR validation passed" and exited 0 on bytes that--mode us-corerejects. The mode is now a closed, case-insensitive, whitespace-trimmed set (none,us-core) and anything outside it isErrUnknownValidationMode. Breaking for callers that relied on an unrecognised or empty mode meaning "validate structurally" — passnoneexplicitly. The CLI rejects the flag before reading input -
The shipped FHIR validator no longer rejects the shipped FHIR mapper's own output.
MapLabResultstampsus-core-diagnosticreport-lab(correct: US Core defines separate-laband-noteDiagnosticReport profiles and this package produces both), but-labwas a bare literal that was never declared as a constant and the checker accepted only-note -
A version-pinned profile canonical (
…/us-core-patient|9.0.0) no longer fails the profile-presence check. Policy: the mapper asserts bare canonicals, the checker accepts either form -
Patient.MRNis no longer dropped. The mapper read identifiers only fromPatient.Identifiers, so an MRN-only patient produced zero identifiers and a hardPatient.identifier is required (US Core)error. It is backfilled as anMR-typed identifier, value-deduplicated against identifiers already present -
DiagnosticReport.code.codingno longer repeats a(system, code)pair when a parser populates bothLabTest.LOINCCodeandLabTest.Code.Coding -
CI
test:integrationMinIO service container never started:minio/minioshipsCMD ["minio"], which prints usage and exits, so the service never listened onminio:9000.setupTestInfra()responded witht.Skipf, silently skipping 30 integration tests (event store, projections, terminology init/status, storage, mapping-decision CLI) behind a green job. The service now runsserver /data. Verified by coverage delta: 73.2% degraded vs 75.9% live. -
TestIntegration_TerminologyMappingDecisionCLIasserted an untruncated 23-character source code appeared in a decisions-table column rendered throughtruncate(decision.SourceCode, 12). The fixture now fits the column width. -
README action templates now use the JSON key paths the workflow engine actually evaluates (
{{.patient.family_name}}), replacing Go struct field paths ({{.Patient.Name.Family}}) that silently rendered<no value>. -
README dry-run example now calls the
fi-fhir workflow dry-runsubcommand;fi-fhir workflow runhas no--dry-runflag. -
README Prometheus metric names corrected to the emitted
fi_fhir_workflow_*namespace, replacing names that matched no registered metric. -
README project structure, format tables, semantic event names, and Helm install flag corrected against the current code.
-
README FHIR output bullet corrected from a four-resource description (Patient, Encounter, Observation, DiagnosticReport) to the 26 exported
Map*methods on the US Core R4 mapper inpkg/fhir/mapper.go. README Observability bullet no longer claims OpenTelemetry tracing is shipped; it is scaffolded at the workflow layer but not wired intoserve(see the Tracing section). -
Mermaid diagram sources regenerated to reflect the integration engine, deployment lifecycle, and IDE journey added in phases 1 through 3.
-
Concurrent durable receipt insertion now arbitrates both the deterministic receipt primary key and tenant/idempotency key before the authoritative stored- result lookup and request-fingerprint validation, preventing valid duplicate callers from surfacing a primary-key error.
-
Runtime verification CI now requires the fi-fhir binary for UI, TypeScript SDK, and smoke consumers; waits for the configured server port; runs the complete SvelteKit/Vitest suite; aggregates every smoke assertion safely under strict shell mode; and proves the production handler rejects GraphQL WebSocket upgrades and legacy routes. npm 10.9.3 is the canonical UI package manager and the stale pnpm lock has been removed.
-
Workflow benchmarks now replace terminal log actions with a benchmark-only no-op handler, parse
events/sec, and fail when a thresholded result is missing; benchmark test failures now propagate through the artifact-capture step, and shared-x86 latency ceilings are calibrated from default-branch evidence so the gate measures engine performance instead of console I/O or silently skipped records. The calibrated benchmark job is now blocking.
Security
- UI image runs
apk upgradein the nginx stage so libssl3/libcrypto3 carry the fix for CVE-2026-14456 (HIGH,security:trivy-ui-image).npm audit fixinui/moved browserslist past GHSA-c83g-rgw3-j3cx / GHSA-73wf-gq98-2v4g and postcss-selector-parser past GHSA-w9m9-85wc-3x92 (security:npm-audit-uihigh gate); three low-severitycookieadvisories remain behind the SvelteKit major and are below the gate. - golang.org/x/crypto bumped v0.53.0 → v0.55.0 for CVE-2026-56854 (x/crypto/ssh authentication bypass via unenforced source-address restrictions; used by the SFTP batch provider). Pulled x/mod, x/net, x/sync, x/sys, x/text and x/tools forward one minor each via
go mod tidy. Surfaced bysecurity:trivyon the first MR pipeline after the advisory published. - golang.org/x/crypto bumped v0.53.0 → v0.55.0 for CVE-2026-56854 (x/crypto/ssh authentication bypass via unenforced source-address restrictions; used by the SFTP batch provider). Pulled x/mod, x/net, x/sync, x/sys, x/text and x/tools forward one minor each via
go mod tidy. Surfaced bysecurity:trivyon the first MR pipeline after the advisory published. Same MR: google.golang.org/grpc v1.82.1 → v1.83.1 for CVE-2026-84304 (HIGH), surfaced bysecurity:trivy-imageon the built binary. - GraphQL now fails startup closed without a deployment tenant, principal, preview role, exact HTTP origins, one canonical [REDACTED], and a matching immutable integration registry. HTTP accepts only bounded JSON POST requests; WebSocket transport is unmounted and UI subscription consumers fail locally.
- GraphQL rejects duplicate, case-aliased, malformed, wrongly typed, or trailing JSON before gqlgen and presents catalog-safe errors without reflecting raw request/query content. nginx and Kubernetes ingress stream bounded request bodies without proxy temp-file buffering.
- The
integration:previewrole can invoke onlyhealthandpreviewIntegrationMessage. Legacy submit, batch, workflow-trigger, parse, session execution/raw retention, export, and live-parse paths are unavailable by default. Profile-YAML and unauthenticated generic-ingest HTTP bypasses are no longer mounted byserve; canonical UI and cluster proxies expose no legacy/apifallback. - The GraphQL transport gate no longer allows every operation to any
graphql:operatorholder. It enumerates all 131 root fields and refuses any it has no role for. The sixteen operator control-plane fields require the same roles as the service behind them —integration.operatorfor the nine reads, plusintegration.delivery.operatorfor replay/resubmit/discard andintegration.deployment.operatorfor pause/resume/retire/deploy — so a control-plane operator can be issued a token that reaches nothing else.graphql:operatoris retained as a named, deprecated compatibility grant that expands to the full set, so every existing operator token is unaffected; the remaining 115 root fields are still reachable only through it and each carries aTODOnaming the slice that should narrow it.serveprints the mapping's shape at startup.integration:previewand the SSE stream-context allowlist are unchanged, and every service-layer authorization check is unchanged: this is defence in depth, not a relocation. - Mapping Studio preview now compiles its public registry alias through the Vite environment namespace, validates complete tenant/provenance/correlation lineage, keeps raw samples and filename-derived labels in tab memory, and purges their two legacy localStorage keys during startup.
- Security evidence is now enforced: govulncheck, high-confidence/high-severity gosec, Trivy filesystem critical/secret checks, UI and TypeScript SDK npm audits, pinned go-licenses policy checks, and both runtime image scans are required merge-request jobs with their reports retained as artifacts.
- Refreshed the UI dependency lock within declared ranges, pinned the patched same-major Lodash resolution required by the current GraphQL Codegen toolchain, moved the UI to the compatible Vite 7/Svelte plugin 6 pair, and upgraded the TypeScript SDK to Vitest 4.1.10; both frozen npm 10.9.3 trees now contain no HIGH or CRITICAL audit findings.
- Replaced the mutable full nginx UI runtime base with a digest-pinned nginx Alpine slim image that removes the four vulnerable unused packages; backend and UI images are now built and scanned before merge and reject every CRITICAL plus every fixed HIGH finding. Main deploys wait for those scans, and tagged releases retag the exact scanned artifacts instead of rebuilding mutable inputs. The backend Docker context now excludes UI dependencies and local build/tool scratch data.
- Upgraded the Go build/runtime baseline to 1.26.5 and the Go-1.26-compatible golangci-lint baseline to 2.12.2; govulncheck and gosec versions are now pinned.
- Event-store and database workflow actions now reject configuration-controlled
SQL identifiers outside lowercase PostgreSQL identifiers (
[a-z_][a-z0-9_]*, maximum 63 characters) and quote identifiers at direct query boundaries. - Public PostgreSQL event, checkpoint, projection-snapshot, and stream-snapshot stores now quote raw unqualified table and derived index names internally; embedded NUL bytes and names over PostgreSQL's 63-byte limit receive a deterministic hash suffix.
- Non-root container execution
- Read-only root filesystem
- Secret provider interface (env, file, Vault, AWS SSM, K8s secrets)
- TLS 1.3 support
- Pod security standards (restricted)
- Network policy templates
- govulncheck + gosec in CI pipeline
- Trivy filesystem and image scanning
- Required npm audits for UI and TypeScript SDK dependencies
- Required pinned license compliance checking (go-licenses)
0.1.0 - 2024-01-15
Added
Core Functionality
- HL7v2 message parsing (ADT A01-A04, A08, ORU R01, SIU S12-S15, S26)
- CSV/flatfile parsing with schema inference
- EDI X12 parsing (837P claims, 835 remittance, 270/271 eligibility, 276/277 status)
- Canonical semantic event model (
pkg/events/) - Source Profile system for per-interface configuration
Workflow Engine
- YAML-based workflow DSL for event routing
- CEL (Common Expression Language) filter conditions
- Transform pipeline (set_field, map_terminology, redact)
- Action types: log, webhook, fhir, database, queue
- Dry-run mode for testing workflows
FHIR Integration
- FHIR R4 resource generation (Patient, Encounter, Observation, DiagnosticReport)
- US Core profile mapper
- OAuth2 client credentials flow with token caching
- Automatic 401 retry with token refresh
Reliability Features
- Retry with exponential backoff for HTTP actions
- Circuit breaker pattern for failing external services
- Dead letter queue (DLQ) for failed events
- Rate limiting for high-volume event streams
- Event replay from DLQ or recordings
Observability
- Prometheus metrics (
workflow_events_processed_total, etc.) - OpenTelemetry distributed tracing
- Structured JSON logging with trace correlation
- Grafana dashboard templates
- Prometheus alerting rules
CLI
parse- Parse messages (HL7v2, CSV, EDI)workflow run- Process events through workflowworkflow validate- Validate workflow configurationconfig show/validate/env/init- Configuration managementversion- Version information
Deployment
- Multi-stage Dockerfile with distroless base
- Docker Compose for local development
- Kubernetes manifests with Kustomize overlays
- Helm chart with full templating
- GitLab CI/CD pipeline (lint, test, security, build, release)
SDK
- TypeScript SDK with CLI wrapper
- Type definitions for events and workflow
Validation
- NPI (National Provider Identifier) validation with Luhn check
- MBI (Medicare Beneficiary Identifier) validation
- SSN format validation
- DEA number validation
Security
- Non-root container execution
- Read-only root filesystem
- Secret provider interface (env, file, Vault, AWS SSM, K8s secrets)
- TLS 1.3 support
- Pod security standards (restricted)
- Network policy templates
Types of Changes
Addedfor new featuresChangedfor changes in existing functionalityDeprecatedfor soon-to-be removed featuresRemovedfor now removed featuresFixedfor any bug fixesSecurityfor vulnerability fixes