Skip to content

Commit 67a6548

Browse files
committed
V1_PLAN: drop atomic-ehr deps, defer UCUM and downloader rework
Decision (today): no third-party packages from atomic-ehr or any other org as runtime deps. Their projects are pre-1.0; pinning to upstream cadence costs more than the borrowed code saves. Theme 2 reframed from "borrow" to "self-built coverage gaps": - UCUM-aware quantity ops: deferred to post-v1. Documented as a known limitation; ~2-3KLoC of careful implementation out of scope for the v1 freeze. - Canonical / package resolution: keep the existing downloader. It works; harden in v2 driven by reported failures. - FHIRPath evaluator: scope-document the supported subset, don't swap engines. Version walk re-ordered. Streamable HTTP completion (SSE + batched JSON-RPC) now lands as v0.51.0 instead of v0.53.0.
1 parent 8fbf1f9 commit 67a6548

2 files changed

Lines changed: 53 additions & 54 deletions

File tree

V1_PLAN.md

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ of cleanups that should land before the freeze takes effect.
2121

2222
1. **Cleanups before freeze** — gaps the audit caught that should not
2323
ship in v1 untouched.
24-
2. **Borrow what's better elsewhere** — bounded swaps from the
25-
atomic-ehr ecosystem; net code reduction or pure additions.
24+
2. **Self-built coverage gaps** — capabilities other libraries solve
25+
(UCUM, canonical resolution) that we'd previously considered
26+
borrowing; **dropping third-party deps as a hard rule**, so each
27+
either gets a bounded in-house implementation or ships post-v1.
2628
3. **Open issues triage** — the 3 issues that survived the close-out
2729
sweep get a decision per item.
2830
4. **Pre-v1 polish** — stability scaffolding (deprecations, perf
@@ -69,55 +71,53 @@ top two levels today.
6971

7072
---
7173

72-
## Theme 2 — Borrow from atomic-ehr
74+
## Theme 2 — Self-built coverage gaps
7375

74-
Three takeaways surfaced by the cross-org compare. Two are clear wins;
75-
the third is a deliberate non-adoption.
76+
**Decision (2026-04-29):** we will not adopt third-party packages
77+
from atomic-ehr (or any other org) as runtime deps. Their projects
78+
are pre-1.0, scope overlap is partial, and the typed-FHIR-client niche
79+
is small enough that pinning to an upstream's release cadence costs
80+
more than it saves. Every gap below ships in-house or is deferred.
7681

77-
### 2.1 UCUM integration — `@atomic-ehr/ucum`
82+
### 2.1 UCUM-aware FHIRPath quantity ops
7883

79-
**Gap.** FHIR `Quantity` arithmetic and the `code-value-quantity`
80-
composite-search-param normalizer treat `5 'kg'` and `5000 'g'` as
81-
unequal. Silent correctness bug.
84+
**Gap.** FHIR `Quantity` equality, ordering, and arithmetic are
85+
spec-defined to be UCUM-aware. Today `5 'kg' = 5000 'g'` returns
86+
`false` (property equality), `<`/`>` cast to `NaN`, and `+`/`-`
87+
return empty. Silent correctness bug in invariants and FHIRPath
88+
evaluation.
8289

83-
- [ ] Add `@atomic-ehr/ucum` as an optional peer dep on
84-
`@fhir-dsl/fhirpath`.
85-
- [ ] Wire into FHIRPath quantity comparison (`=`, `~`, `<`, `>`).
86-
- [ ] Wire into FHIRPath quantity arithmetic (`+`, `-`).
87-
- [ ] Wire into core's composite-search normalizer.
88-
- [ ] Test fixture: `5 kg = 5000 g`, `1 'L' = 1000 'mL'`.
90+
**Decision: deferred to post-v1.** A correct UCUM implementation —
91+
parser for `mol/(L.s).cm-1` style expressions, dimension algebra,
92+
prefix normalization, special-unit (Celsius, decibel, pH) handling —
93+
is ~2-3KLoC of careful code. Out of scope for the v1 freeze. Track
94+
as a known limitation:
8995

90-
**Cost.** S — half a day. Highest value/lowest risk swap in this plan.
96+
- [ ] Document the gap in `packages/fhirpath/README.md` so callers
97+
know not to rely on UCUM-aware quantity semantics.
98+
- [ ] Add an explicit `it.skip` test pinning current behaviour so a
99+
future implementation breaks loudly.
100+
- [ ] Open a v2 issue: "Native UCUM expression evaluator".
91101

92-
### 2.2 `@atomic-ehr/fhir-canonical-manager` — replace downloader
102+
### 2.2 Canonical / package resolution
93103

94-
**Gap.** `packages/generator/src/downloader.ts` rolls its own tgz
95-
fetch, registry resolution, and canonical lookup. ~200 LoC of code we
96-
don't need to maintain when their `fcm` package handles the same
97-
problem with disk caching and registry redirects already.
104+
**Gap.** `packages/generator/src/downloader.ts` does its own tgz
105+
fetch and registry resolution. It works, but registry edge cases
106+
(redirects, mirror failover, signed packages) are not covered.
98107

99-
- [ ] Add `@atomic-ehr/fhir-canonical-manager` as runtime dep on
100-
`@fhir-dsl/generator`.
101-
- [ ] Verify license compatibility (likely MIT — confirm before
102-
adopting).
103-
- [ ] Adapt `DownloadedSpec` shape from `fcm`'s output via a thin
104-
shim.
105-
- [ ] Delete `downloader.ts` (modulo the `--src` local-dir path,
106-
which stays).
107-
- [ ] Smoke test: `pnpm --filter @fhir-dsl/example generate` still
108-
produces an identical tree.
108+
**Decision: keep our own.** It's working today. Hardening goes in v2
109+
once we have specific user-reported failures to drive the design.
109110

110-
**Cost.** S/M — half a day plus the shape-mismatch surprise tax.
111+
### 2.3 FHIRPath evaluator scope
111112

112-
### 2.3 FHIRPath evaluator — DON'T swap (decision)
113+
We own a sync, type-safe FHIRPath evaluator that covers the subset
114+
FHIR invariants and the common navigation patterns actually use.
115+
Broadening to the full FHIRPath N1 spec is post-v1 work, driven by
116+
real user expressions that fail.
113117

114-
Their `@atomic-ehr/fhirpath` is async and broader-spec; ours is sync
115-
and narrower. Adopting their evaluator would force a public API break
116-
across `@fhir-dsl/fhirpath` (every `evaluate()` becomes async). Our
117-
differentiator is the typed builder, not the evaluator internals.
118-
119-
- [ ] Document the decision in the package README so it's not
120-
revisited every quarter.
118+
- [ ] Document the supported subset and the boundary in
119+
`packages/fhirpath/README.md` so users know when to file a
120+
"missing feature" vs. when to choose a different evaluator.
121121

122122
---
123123

@@ -216,12 +216,12 @@ Documented here so they don't bleed scope:
216216

217217
## Risks
218218

219-
- **The atomic-ehr swaps add upstream risk.** Their packages are still
220-
pre-1.0; we'd be pinning to versions whose APIs may shift. Mitigate
221-
by exact-version pinning in `package.json` until they cut their own
222-
1.0.
223219
- **#50 setValue is the largest scope in this plan.** If it slips,
224220
ship v1 without it (move to v1.1) rather than holding the freeze.
221+
- **Documenting the UCUM gap is not the same as closing it.** Users
222+
with quantity-heavy invariants (vital signs, lab ranges, dose
223+
arithmetic) will hit it. Document loudly; revisit in v2 driven by
224+
actual user reports rather than a speculative implementation.
225225

226226
## Exit criteria
227227

@@ -236,13 +236,12 @@ Documented here so they don't bleed scope:
236236

237237
| Version | Theme | Notes |
238238
|---|---|---|
239-
| v0.51.0 | 2.1 | UCUM integration |
240-
| v0.52.0 | 2.2 | `fhir-canonical-manager` swap |
241-
| v0.53.0 | 1.1 | Streamable HTTP — SSE + batched JSON-RPC |
242-
| v0.54.0 | 3.1 | FHIRPath setValue / patch (#50) |
243-
| v0.55.0 | 1.2 | Per-property invariants |
244-
| v0.56.0 | 4.1 + 4.3 | Deprecation pass + docs parity |
245-
| v0.57.0 | 4.2 + 4.4 | Perf baseline + hand-written changelog |
239+
| v0.51.0 | 1.1 | Streamable HTTP — SSE + batched JSON-RPC |
240+
| v0.52.0 | 1.2 | Per-property invariants |
241+
| v0.53.0 | 3.1 | FHIRPath setValue / patch (#50) |
242+
| v0.54.0 | 2.1 + 2.3 | Document UCUM + FHIRPath-subset gaps |
243+
| v0.55.0 | 4.1 + 4.3 | Deprecation pass + docs parity |
244+
| v0.56.0 | 4.2 + 4.4 | Perf baseline + hand-written changelog |
246245
| v1.0.0 | | API freeze. Tag `surface-v1.0.0` from the locked snapshot. |
247246

248247
Each version is independently mergeable. Order is suggested, not

apps/docs/docs/roadmap.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ The full plan lives in [`V1_PLAN.md`](https://github.com/awbx/fhir-dsl/blob/main
1717
- **Streamable HTTP — finish the spec.** Today's `httpTransport()` only handles POST → single JSON response. SSE on GET (for server-initiated notifications), `text/event-stream` responses, and batched JSON-RPC arrays land before v1 so the framing isn't observable later.
1818
- **Per-property invariants.** Phase 6 follow-up (v0.49.0) wires invariants on root + backbone elements; deeper-level constraints flow into the same `s.refine()` machinery.
1919

20-
### Borrowing from the atomic-ehr ecosystem
20+
### Known limitations to document loudly before the freeze
2121

22-
- **UCUM integration** `@atomic-ehr/ucum` plugged into FHIRPath quantity arithmetic and the `code-value-quantity` composite-search normalizer. Closes a real correctness gap (`5 'kg'` vs `5000 'g'` are silently unequal today).
23-
- **`@atomic-ehr/fhir-canonical-manager`** — replaces the generator's roll-your-own tgz/registry handling.
22+
- **UCUM-aware quantity ops are deferred to post-v1.** `5 'kg' = 5000 'g'` returns `false` today, FHIRPath quantity arithmetic returns empty, and `<`/`>` cast through `NaN`. A correct in-house UCUM implementation is ~2-3KLoC and out of scope for the v1 freeze; we'll document the gap and ship a v2 that addresses it driven by real user-reported failures rather than speculation.
23+
- **The FHIRPath evaluator covers a subset of N1.** The supported surface is the patterns FHIR invariants and common navigation actually use. The boundary will be documented in `packages/fhirpath/README.md` before the freeze so users know when to file a missing-feature ticket vs. choose a different evaluator.
2424

2525
### One open feature ask
2626

0 commit comments

Comments
 (0)