Status: shipped at v1.0.0. Every theme in this plan is checked
off; the curated narrative for what landed lives at
audit/v1-changelog.md. This file is preserved as the historical
plan that drove the v0.51-v1.0.0 ship line.
The 0.x line was feature-complete against the FHIR-compliance plan
(see FHIR_COMPLIANCE_PLAN.md — every Phase 0–8 row in the shipped
table is [x] as of v0.50.0). v1.0.0 is a stability commitment,
not a feature ship: API freeze, semver discipline, and the small set
of cleanups that landed before the freeze took effect.
- API freeze. Every
exportfrom every public package is locked.audit:export-surfacealready protects against accidental drift; v1 makes that promise to consumers, not just to ourselves. - Semver discipline post-v1. Breaking changes go through a deprecation
cycle (one minor with
@deprecated+ a console warning), then land in the next major. - Documentation parity. Every public symbol referenced in the spec-coverage table has either generated TSDoc or a docs page entry.
- Cleanups before freeze — gaps the audit caught that should not ship in v1 untouched.
- Self-built coverage gaps — capabilities other libraries solve (UCUM, canonical resolution) that we'd previously considered borrowing; dropping third-party deps as a hard rule, so each either gets a bounded in-house implementation or ships post-v1.
- Open issues triage — the 3 issues that survived the close-out sweep get a decision per item.
- Pre-v1 polish — stability scaffolding (deprecations, perf smoke, docs sync).
- Out for v1, in for v2 — explicitly deferred.
These are the items the previous audit-pass already filed as issues but flagged as defensive or low-priority. Re-evaluating them with v1 in mind: a few should ship, a few stay deferred. None require new design.
httpTransport() now matches the full Streamable HTTP shape from the
MCP spec:
- GET
/mcpopens an SSE stream for server-initiated notifications (keepalive-only today; framing in place for future producers). -
text/event-streamresponse on POST when the client sendsAccept: text/event-stream. - Batched JSON-RPC arrays accepted on POST; per-entry errors
preserved with the right
id.
- Extended the parser to extract property-level
ElementDefinition.constraint[*](filtering trivially-inheritedele-1so generated output isn't bloated by universal Element constraints we already enforce structurally). - New
refineschema-AST node wraps the property's computed schema so the predicate evaluates against the property value rather than the parent resource. Both zod and native adapters render it. - Per-property and resource-level invariants compose: a property
with its own constraint sits inside the parent's
s.refinecall.
Decision (2026-04-29): we will not adopt third-party packages from atomic-ehr (or any other org) as runtime deps. Their projects are pre-1.0, scope overlap is partial, and the typed-FHIR-client niche is small enough that pinning to an upstream's release cadence costs more than it saves. Every gap below ships in-house or is deferred.
Gap. FHIR Quantity equality, ordering, and arithmetic are
spec-defined to be UCUM-aware. Today 5 'kg' = 5000 'g' returns
false (property equality), </> cast to NaN, and +/-
return empty. Silent correctness bug in invariants and FHIRPath
evaluation.
Decision: deferred to post-v1. A correct UCUM implementation —
parser for mol/(L.s).cm-1 style expressions, dimension algebra,
prefix normalization, special-unit (Celsius, decibel, pH) handling —
is ~2-3KLoC of careful code. Out of scope for the v1 freeze. Track
as a known limitation:
- Document the gap in
packages/fhirpath/README.mdso callers know not to rely on UCUM-aware quantity semantics. ✅ v0.54.0 - Add an explicit pin test so a future implementation breaks
loudly (
spec-gaps.test.ts"pins raw unit-string equality"). ✅ v0.54.0 - Open a v2 issue: "Native UCUM expression evaluator". ✅ #51
Gap. packages/generator/src/downloader.ts does its own tgz
fetch and registry resolution. It works, but registry edge cases
(redirects, mirror failover, signed packages) are not covered.
Decision: keep our own. It's working today. Hardening goes in v2 once we have specific user-reported failures to drive the design.
We own a sync, type-safe FHIRPath evaluator that covers the subset FHIR invariants and the common navigation patterns actually use. Broadening to the full FHIRPath N1 spec is post-v1 work, driven by real user expressions that fail.
- Document the supported subset and the boundary in
packages/fhirpath/README.mdso users know when to file a "missing feature" vs. when to choose a different evaluator. ✅ v0.54.0 - Open a v2 issue tracking unimplemented evaluator functions. ✅ #52
Repo state: 3 open issues at v1 plan time.
User-driven feature. The builder already encodes a typed path; setting
a value through that path (creating intermediate nodes per where()
restrictions) is genuinely useful and has no equivalent in atomic-ehr's
fhirpath.
- Wire
setValue(resource, value)andcreatePatch(resource, value)onto every typed builder leaf via the proxy. - Implement
setValuefor the simple navigation subset (a.b.c). - Implement
setValuethroughwhere()predicates — create missing intermediate objects matching the predicate constraints. Conjunctions ofeqchecks supported;or/notrejected. - Emit RFC 6902 JSON Patch via
createPatch()with proper JSON Pointer escaping (~→~0,/→~1). - Test fixture matches the issue's example. Throws
FhirPathSetterErroron filter ops or non-invertible predicates.
Cost. L — 1.5 days. Real new feature surface; warrants a minor.
Generator-side. Marked P3 by the audit author. Deferred for v1. The slicing parser already exists (Phase 2.1, v0.38.0); only the FHIRPath builder narrowing is missing. Re-evaluate if a user files a duplicate.
Marked P3 + "low value; defer". Affects Patient.deceased[x] and
similar. Deferred for v1. Workaround exists (cast through as).
- Diffed
audit:export-surfacesnapshot at v0.30.0 vs v0.54.0: zero removals or renames. Surface has been additive-only. No@deprecatedaliases need to be re-added. - No
@deprecatedtags exist in the codebase to shim. Theconsole.warn-once helper is therefore deferred to v2 (will land alongside the first real deprecation).
- Generator end-to-end on R4 + US Core (warm cache): 221 ms (target under 30 s, 136× headroom).
- 1000-resource Bundle through native validator: 1.7 ms (target under 100 ms, 58× headroom).
- FHIRPath
Patient.name.family× 10 000 iters: 5.8 ms (target under 500 ms, 86× headroom). - Captured in
audit/perf-baseline.mdwith regression policy and the runner script (scripts/perf-baseline.mjs).
- Roadmap doc rewritten to reflect the v0.51-0.54 ship line and
remaining v1 scaffolding (
apps/docs/docs/roadmap.md). - README spec-coverage table already marks Phase 6 follow-up as ✅ shipped at v0.49.0.
-
httpTransport()documented inapps/docs/docs/guides/mcp.md(v0.51.0 work). -
validateInvariantsdocumented inapps/docs/docs/guides/validation.md+ main README invariants section (v0.49.0+v0.52.0 work). -
setValue/createPatchdocumented inapps/docs/docs/guides/fhirpath-and-queries.md(v0.53.0).
- Hand-curated v1.0.0 narrative lives at
audit/v1-changelog.mdalongside the auto-generated commit log inCHANGELOG.md. Covers surface highlights per package, the v0.5x ship line by theme, known limitations, and explicit v2 scope.
Documented here so they don't bleed scope:
- React adapter (
@fhir-dsl/react). Useful, but the API of the query builder must freeze first. - Server adapter packages (HAPI, Azure, Google Cloud Healthcare). Same reason — stabilize the client surface before pinning per-server quirks.
- Watch mode for the generator. Convenient, but not a stability blocker.
- Incremental generation. Same.
- Middleware / interceptor pipeline on the runtime. Real feature, but adding it to the executor changes the request flow — better as v2 with a clean design pass.
- #50 setValue is the largest scope in this plan. If it slips, ship v1 without it (move to v1.1) rather than holding the freeze.
- Documenting the UCUM gap is not the same as closing it. Users with quantity-heavy invariants (vital signs, lab ranges, dose arithmetic) will hit it. Document loudly; revisit in v2 driven by actual user reports rather than a speculative implementation.
- All Theme 1, 2, 3.1, and 4 checkboxes ticked.
audit:export-surfacesnapshot has been frozen and taggedsurface-v1.0.0.pnpm test/pnpm lint/pnpm -r typecheckclean onmainfor at least 3 consecutive commits.- Roadmap doc rewritten; v1.0.0 hand-written changelog entry merged.
| Version | Theme | Notes |
|---|---|---|
| v0.51.0 | 1.1 | ✅ Streamable HTTP — GET/SSE + batched JSON-RPC |
| v0.52.0 | 1.2 | ✅ Per-property invariants |
| v0.53.0 | 3.1 | ✅ FHIRPath setValue / patch (#50) |
| v0.54.0 | 2.1 + 2.3 | ✅ Document UCUM + FHIRPath-subset gaps |
| v0.55.0 | 4.1 + 4.3 | ✅ Deprecation pass + docs parity |
| v0.56.0 | 4.2 + 4.4 | ✅ Perf baseline + hand-written changelog |
| v1.0.0 | ✅ API freeze. Tagged surface-v1.0.0 from the locked snapshot. |
Each version is independently mergeable. Order is suggested, not required — anything can slip without blocking the rest.