fix(safety): report a doNotPerform value this library cannot read, instead of reading it as absent (FHIR-XML-UNREADABLE-BOOLEAN-IS-SILENT) - #80
Merged
Conversation
…stead of reading it as absent (FHIR-XML-UNREADABLE-BOOLEAN-IS-SILENT) ## What this fixes `FHIR-XML-UNREADABLE-BOOLEAN-IS-SILENT`, a STOP-THE-LINE filed under meta-repo ADR 0016, raised by `#79`'s gate outside its slice and reproduced independently by the coordinator at `05ecc5a`. Measured again by this slice's own probe at the same commit: ``` <doNotPerform value="true"/> negations: ["do-not-perform"] issues: [] safeToSummarize: true assert clean <doNotPerform value="1"/> negations: [] issues: [] safeToSummarize: true assert clean <doNotPerform value="Y"/> negations: [] issues: [] safeToSummarize: true assert clean <doNotPerform value="0"/> negations: [] issues: [] safeToSummarize: true assert clean <doNotPerform value="N"/> negations: [] issues: [] safeToSummarize: true assert clean ``` `#79` closed the first row. `"1"` and `"Y"` are ordinary v2 and C-CDA converter output for a boolean, and R4's `boolean` lexical space is `true|false` (datatypes.html), so they are **not readable** rather than false. The reading is **indistinguishable from `"0"` / `"N"`**: an author who wrote "yes, do not administer" got the same answer as one who wrote "no", with nothing recorded anywhere. **The gap was structural, and it is the item.** `SafetyReadout` had location channels for content the codec **could not read** (`nestedArrays`, `droppedText`) and none for "value written, not readable". ## The change: a REPORT, not a wider read `booleanOf` is byte-identical to `#79`'s and `doNotPerform` still reads `undefined`. Widening the read to accept `"1"` / `"Y"` would be a worse defect than the one filed: it invents a reading R4 does not license, and it turns `<doNotPerform value="0"/>` and `value="N"` into a JS `false` that `serializeResource` then emits, authoring a value and laundering it across a format change, which is what `#74` refused. `"1"` and `"Y"` also arrive on real wires meaning a boolean's `true` *and*, elsewhere, its opposite, so there is no safe direction to guess in. What is added is `SafetyReadout.unreadableBooleans`, a third not-readable location channel beside `nestedArrays` and `droppedText`, with `unreadableBooleans(resource, path)` exported beside them. It feeds `safeToSummarize` and `assertSafeToSummarize`. It is **value-free**: the text that failed to read reaches neither the locations nor the error message. Detection is `hasUnreadableBoolean`, the **exact complement** of the read: the same values through the same array wrapper (`scalarValues`), the same `booleanOf`. So a location is emitted for precisely the values `primitiveBooleans` declined, and the report cannot drift from the read by growing a rule of its own. **Decided in the safety layer, not the reader.** The reader is schema-free: `<doNotPerform value="1"/>` and `<status value="1"/>` are the same node to the codec, so nothing at parse time knows the text should spell a boolean. The safety layer is the first place that knows the datatype. ## Consumer-side additivity, established from the consumers and measured `#79` paid a refuted pass for the rule that additivity is a property of the CONSUMERS, not the helper. **That class is unreachable here by construction, in its strongest form: this slice widens no read at all.** No value that was `undefined` becomes defined anywhere, so nothing that treats `undefined` as *inherit* or *absent* ever sees a new value. | what is added | who reads it | can it remove anything | |---|---|---| | `SafetyReadout.unreadableBooleans` | nothing in tree before this slice | new field, no prior consumer | | `safeToSummarize` | `assertSafeToSummarize` only (every other mention in `src/` is prose) | a conjunction of empty-list tests, one term added: monotone `true -> false`, never the reverse | | `assertSafeToSummarize` locations | `FhirSafetyError.locations` | appended last, no existing location dropped or reordered | | the read (`booleanOf`, `readDoNotPerform`, `primitiveBoolean`) | unchanged | nothing added at all | **Measured in a real `05ecc5a` worktree over a 33-document corpus**, comparing every `SafetyReadout` field that exists on both trees, both writers' output, the parse issues, the `ValidationIssue` list and `valid`: **13 documents move, and in every one the only field that moves is `safeToSummarize`, `true -> false`.** The other 20 are identical on every channel. **No diagnostic moves anywhere.** ## Does `safeToSummarize` move? Yes, and that is the answer rather than a side effect Its contract is that the library declines when a summary would have to assert something it cannot establish. An unreadable negation is exactly that: `value="1"` and `value="0"` are indistinguishable to a reader with no licence to guess, so affirming over either affirms over a coin flip, and the coin decides whether a medication is given. Both now refuse. The fix does **not** make `"Y"` and `"N"` read differently. They still read alike, because they are alike to this library. What changed is that the shared answer is a refusal instead of an affirmation. ## The census, wider than the filed line **Booleans the safety spine reads out of a document: exactly one.** `MedicationRequest.doNotPerform`. `retracted` and `noKnownAllergy` are derived from codes and codings, not from a boolean element. So the channel is complete for its own layer and for nothing beyond it, and its docblock says so. | site | read via | unreadable value at head | disposition | |---|---|---|---| | `MedicationRequest.doNotPerform` | `primitiveBooleans` (safety) | **REPORTED** | **FIXED** | | `ElementDefinition.mustSupport` | `primitiveBoolean` (convenience) | silent | standing, pinned | | `ElementDefinition.slicing.ordered` | `primitiveBoolean` (convenience) | silent | standing, pinned | | `ElementDefinition.min` (`unsignedInt`) | `parseMin` | silent | standing, pinned | | `Quantity` magnitude (`+5`, `05`, `.5`, `5.`) | `readQuantity` | silent | standing, `#78`'s pin | | FHIRPath `numberOf` | FHIRPath | silent | standing, `#79`'s census | The two profile booleans have **nowhere to go** as well as `#79`'s measured retirement reason: a `StructureDefinition` is not a safety resource and has no `SafetyReadout`. ## One deliberate asymmetry: no `ValidationIssue` This is the first place on the readout where `safeToSummarize: false` sits beside `valid: true`, stated rather than glossed. The validator's five fail-closed rules are all about shapes FHIR gives no meaning to at any position, decidable with no datatype in hand. This one is decidable only because the safety layer knows the element's datatype, and the structural validator is schema-free. Putting a datatype-dependent rule there is the change `#78` measured at `validatePrimitiveValue` and declined, because in place it retires a real mismatch. Pinned. ## Measurements - **Red-at-base 18-of-30**, 30-of-30 at head, in a real `05ecc5a` worktree with a base shim supplying the new surface with base's own semantics (an always-empty channel), so each test measures behaviour rather than a missing export. The **12 both-states pins, named**: the conformant `true` and `false` spellings, the absent element, a value-less primitive carrying only metadata, the non-MedicationRequest type gate, a conformant JSON `MedicationRequest`, this package's own XML round trip, the no-`ValidationIssue` pin, the object / empty-array shape, the JSON `null`, the two profile booleans, and the non-boolean datatype. - **Non-vacuity by mutation, six, each reds at least one pin:** dropping the `MedicationRequest` type gate reds 1; stopping `hasUnreadableBoolean` reading through the array wrapper reds 2; firing it on a value-less primitive reds 2; dropping the new term from `safeToSummarize` reds 13; accepting `"1"` in `booleanOf` reds 1; reading only `node.properties` and not the shadowed `duplicates` reds 1. - `verify.sh fhir` green: **11 `==>` step headers, 11 `ran:` entries, zero `(FAIL)`**, no unladdered-script warning. Suite **65 files / 1,346 tests** = `#79`'s 1,316 + this file's 30, so **no existing test moved**. ## The pin this closes, rewritten rather than deleted `#79`'s residual pin "still affirms safeToSummarize over a doNotPerform this reader cannot read" looped over `["true", "1"]`. The `"1"` arm is what this slice closes, so the pin now covers the `"true"` arm only, is renamed to say so, and names where the other half went. Closing a pin must red it in the same change, and it did: it was the **only** test in the whole suite that moved. ## Documentation carriers swept `README.md`, `CHANGELOG.md` (both the new entry and the two clauses in `#79`'s entry this slice falsifies: "silently" and "safeToSummarize is unmoved in both directions"), the `#79` note's residual 4, the new changeset, `documentation/agent-notes/xml-unreadable-boolean.md` and its cursor. The `booleanOf` docblock's "that refusal is silent" is corrected and scoped to the convenience read, which is still silent. `documentation/agent-notes.md` was at 249,983 of its 250,000-byte cap, so the cursor did not fit. **The ceiling is not raised**: `#77`'s "untaggable `resourceType`" narrative is relocated verbatim to `documentation/agent-notes/untaggable-resource-type.md` with a cursor left behind (it had zero inbound anchors), which brings the archive to 239,092. Nothing was deleted. ## Corpus caveat Hand-authored XML and JSON fixtures, mutations and hand-built probes, **not** the FHIR R4 published-examples corpus. Nothing here is corpus-wide. ## Not touched PR #73 (Version Packages) is untouched and stays open. No umbrella pointer bump in this PR.
… (conformance-refuter pass 1)
`conformance-refuter` pass 1 returned **REFUTED** on the frozen sha `d96f97d`, with two `INTRODUCED`
majors. **Neither touched the code**: the pass verified `booleanOf`, `scalarValues`,
`primitiveBooleans` and `primitiveBoolean` byte-identical to `05ecc5a`, confirmed the complement is
exact, confirmed monotonicity, re-derived red-at-base 18-of-30 by inspection, and confirmed the
relocation verbatim. Both findings were claim defects, and this commit takes the **cuts** the pass
prescribed rather than qualifying anything.
## Finding 1, major: a false universal about the validator, on the public surface
The draft said the report raises no `ValidationIssue` because "the validator is schema-free and every
rule it carries is about a shape FHIR gives no meaning to at any position". **The validator is not
schema-free.** `validateResource` takes `{ schemas }`, `validate.ts` resolves a datatype from the
registry, and `validate/primitives.ts:160` decides `boolean` on it. Measured:
```
validateResource(<doNotPerform value="1"/>) -> RESOURCE_NOT_MODELED valid: true
validateResource(<doNotPerform value="1"/>, { schemas }) -> TYPE_MISMATCH valid: false
validateResource(<doNotPerform value="true"/>, { schemas }) -> TYPE_MISMATCH valid: false
```
The universal is **cut** at all five carriers (`README.md`, `CHANGELOG.md`, the changeset, the notes,
and the `checkUnreadableBooleans` docblock, which reaches `dist/index.d.ts` via its neighbour) and
replaced with the site-specific fact: `MedicationRequest` has **no built-in schema**, so with no
caller-supplied one the validator has no datatype for the element and says nothing. Supply one and it
speaks, **but it is no substitute, because it draws the same `TYPE_MISMATCH` on the conformant
`value="true"`** (`#78`'s recorded false error, not reopened), so it does not separate readable from
unreadable. The pin now asserts **both** paths against string literals, which is what stops the
universal regrowing. This was the sixth generation of the wider-than-the-code defect this lineage has
paid for.
## Finding 2, major: the sweep stopped one carrier short, at the carrier the backlog names
`#79`'s **pending** changeset `.changeset/olive-lions-gather.md` still carried both clauses this slice
falsifies ("that refusal is silent, which is a residual rather than a guarantee", and "`SafetyReadout`
has ... none for 'value written, not readable', so that shape survives this fix"). `CHANGELOG.md` has
no released version heading, so the changesets **are** the published release body: `changeset version`
would have emitted release notes asserting both that the shape survives and that it is closed. The
same two-clause correction already applied to `CHANGELOG.md` is now mirrored into that changeset.
## Finding 3, minor: `FhirSafetyError.message` moves, outside the measured set
The message names the sixth shape, so the string changes for **every** refusal it raises, including
the five that already refused, not only the 13 documents that gain one. `locations`, the class and the
thrown type are unchanged. Now disclosed in `CHANGELOG.md`, the changeset and the notes, because the
corpus measurement enumerates readout fields, parse issues, `ValidationIssue`, `valid`, `negations`
and both writers, and a public-observable string moving outside that set has to be said out loud.
## Finding 4, minor: the type-gate rationale was falsified by the slice's own coverage
`checkUnreadableBooleans` justified its gate as "reporting it would name a location no read visited",
then reported at every resource root, where `Bundle.entry[0].resource.doNotPerform` **is** exactly
that, pinned green by this slice's own test. The docblock now separates the two: the **element name**
is type-gated the way the read is, while the **window** is deliberately wider than the read and equal
to `checkArrayWrapping`'s, which is the fail-safe direction and the asymmetry `arrayWrappedScalars`
already carries. The public `unreadableBooleans` docblock (which does reach `dist/index.d.ts`) is
corrected the same way.
## Raised by the pass, PRE-EXISTING, and it wants its own item
**`ServiceRequest.doNotPerform` is not read at all.** R4 marks it (and `CommunicationRequest`'s) a
`?!` boolean modifier element exactly as `MedicationRequest`'s is, but `readSafety` gates on
`isType("MedicationRequest")` and `ServiceRequest` is not in `SAFETY_RESOURCE_TYPES`, so a
**conformant** `{"resourceType":"ServiceRequest","doNotPerform":true}` reads `negations: []`,
`safeToSummarize: true`, `assertSafeToSummarize` clean. Identical at `05ecc5a`, so `PRE-EXISTING` and
not a refutation, but it is a sharper shape than the item this slice closed: the document is
conformant and the instruction is simply not looked for. Recorded in
`documentation/agent-notes/xml-unreadable-boolean.md` and reported to the coordinator. **Not folded in
here**: the remedy is a question about the spine's type scope, not about a report.
Also `PRE-EXISTING`, minor, left standing: `README.md` and six sites in `src/` say "the six safety
resource types" while `SAFETY_RESOURCE_TYPES` holds **seven**. A stale count across seven sites,
unrelated to this item, not swept here.
## Re-measured after the remedy
- Suite **65 files / 1,346 tests**, all green. Red-at-base still **18-of-30**, 30-of-30 at head, with
the same 12 both-states pins (the `ValidationIssue` pin grew a second path and stayed both-states
green, which is correct: the validator half is unchanged from base).
- Non-vacuity by mutation is now **seven**, the seventh added because the pass named a surface the six
did not cover: dropping the channel from `assertSafeToSummarize`'s location list reds 1.
- `verify.sh fhir` green again: **11 `==>` headers, 11 `ran:` entries, zero `(FAIL)`**, no
unladdered-script warning.
…reach (conformance-refuter pass 2)
`conformance-refuter` pass 2 returned **REFUTED** on `9760562`, cumulative 2. **It refuted no code
again**: it verified the remedy diff was docblock text only, `booleanOf` unwidened base-to-head,
`hasUnreadableBoolean` and `checkUnreadableBooleans` untouched, the complement exact,
`safeToSummarize` monotone toward refusal, the window a superset of the read, and the seventh
mutation consistent with the code. Every finding was a claim defect, and this commit is the
**deletion** the pass prescribed. No `src/` behaviour changes.
## Finding 1, major: the corrected claim was a new claim, and it was also too wide
Pass 1's replacement said the validator "is no substitute, because it draws `TYPE_MISMATCH` on the
lexical `"1"` and on a conformant `<doNotPerform value="true"/>` alike, so it does not separate
readable from unreadable". **The three rows behind that were all XML**, and the conclusion was drawn
unscoped over a channel whose own docblock says "in either wire format". On the **JSON** wire it is
plainly false: `validatePrimitiveValue` shape-checks `typeof value === "boolean"`, so with a schema
supplied a conformant `{"doNotPerform":true}` validates **clean** while `{"doNotPerform":"Y"}` draws
`TYPE_MISMATCH`. The package's own pending `olive-comets-listen` changeset already recorded that, so
two changesets in one release body contradicted each other.
**Deleted, not qualified**, at all six carriers: `README.md`, `CHANGELOG.md` (frozen under ADR 0001
while PR #73 is open), `.changeset/olive-herons-report.md`, the notes, and both docblocks in
`src/safety/status.ts`. The pin's `conformant` row went with it, since it existed only to support the
deleted sentence and the same false error is already pinned with the `Quantity` residuals.
**What survives unscoped is availability, not discrimination**: `MedicationRequest` has no built-in
schema (`BUILTIN_SCHEMAS` is `[Patient]`), so the validator is silent about this element unless a
caller supplies one, and a readout that has to hold on every document cannot rest on a diagnostic
that only exists when a caller opts in. The pin now asserts exactly that and nothing more: silent by
default, available on request.
**This was the second generation of one over-general sentence in one paragraph, one per pass.** The
notes record both refuted versions verbatim beside the survivor, because the trap is that a corrected
claim inherits nothing.
## Finding 2, minor: the public docblock named the wrong read, and it ships in `dist/index.d.ts`
`unreadableBooleans`' docblock (and its README twin) said the window is "wider than `readSafety`'s
own read, which only visits the resource it is handed". `readSafety` does walk the whole tree, and
`arrayWrappedScalars`, named two clauses earlier, is itself computed at every nested resource root.
The subject is the **`doNotPerform` read**, which is what the private docblock already said. Corrected
in both.
## Finding 3, minor: the mirrored correction pointed the wrong way, in the frozen file
`CHANGELOG.md` twice said the channel is added by "the entry below". The `unreadableBooleans` entry is
under `### Added` at the top of `[Unreleased]`; the `#79` entry referencing it is under `### Fixed`
below. Both now say "the entry above". Not present at base.
## Still `PRE-EXISTING`, re-verified by pass 2, and the coordinator owes an item
**`ServiceRequest.doNotPerform` and `CommunicationRequest.doNotPerform` are `?!` boolean modifiers the
safety spine never reads.** Neither type is in `SAFETY_RESOURCE_TYPES` and `readSafety` gates on
`isType("MedicationRequest")`, so a **conformant** `{"resourceType":"ServiceRequest",
"doNotPerform":true}` reads `negations: []`, `safeToSummarize: true`, `assertSafeToSummarize` clean.
Identical at `05ecc5a`, so not a refutation. It is a sharper shape than the item this slice closes:
the document is conformant and the instruction is simply not looked for. Recorded in the notes;
**it is not yet in the backlog**, which is the coordinator's to file. Also still standing: "the six
safety resource types" at `README.md` and six `src/` sites while the set holds seven.
## Re-measured after the remedy
- Suite **65 files / 1,346 tests**, all green. Red-at-base still **18-of-30**, 30-of-30 at head, same
12 both-states pins.
- `verify.sh fhir` green: **11 `==>` headers, 11 `ran:` entries, zero `(FAIL)`**, no
unladdered-script warning.
…UNGRADED) **This commit is UNGRADED and that is disclosed rather than glossed.** `conformance-refuter` pass 3 returned **NOT REFUTED** on `23cd9fa`, cumulative 3, the ADR 0016 cap. It found **no blocker and no major**, said the slice does not stop for a human, and left two minor findings it graded as non-blocking at the coordinator's discretion. They are applied rather than deferred because one carrier is `CHANGELOG.md`, frozen while PR #73 is open, where an over-wide clause would be permanent under ADR 0001. **Documentation and one test comment only**: `git diff 23cd9fa HEAD -- src/` is docblock text, and the suite is unchanged at 65 files / 1,346 tests. ## Pass 3, finding 1, minor: the availability sentence was wide by one word The surviving reason said the validator "is silent about this element unless a caller supplies one". Its datatype half is true, but with **no** schema at all the validator still speaks at this element through the **shape** channels, on both wires: `{"doNotPerform":["Y"]}` draws `ARRAY_WRAPPED_SCALAR`, a repeated `doNotPerform` draws `DUPLICATE_PROPERTY`, and two FHIR-namespace prefixes spelling the element twice draw `ARRAY_WRAPPED_SCALAR` from XML. Narrowed to **"silent about this element's datatype"** at all six carriers, with the shape channels named beside it. Pass 3 attributed the defect to the slice rather than to the pass-2 remedy (`9760562` already read "says nothing about it"). ## Pass 3, finding 2, minor: a rebuttal in a test comment read as a standalone claim The comment's "with a schema supplied it does NOT fail to separate readable from unreadable either" is sound as a one-counterexample rebuttal of the sentence pass 2 killed, but false as a proposition on the XML wire, which is the wire the file is about. Rewritten to name its wire in both directions. ## Pass 3, finding 3, `PRE-EXISTING` advisory, folded rather than filed twice `readDoNotPerform` is **root-only**, so a `Bundle` whose `entry[0].resource` is a **conformant** `MedicationRequest` with `doNotPerform: true` reads `negations: []` and `safeToSummarize: true`, while the *unreadable* spelling at the same location **is** reported by the new channel. Identical at `05ecc5a`. Pass 3's own disposition is adopted: fold it into the `ServiceRequest` / `CommunicationRequest` item pass 2 raised, since both are the spine's **read scope** and not a report. Recorded in the notes; the backlog item is the coordinator's to file. ## What pass 3 re-derived itself The remedy diff was docblock-only (comment-stripped `status.ts` byte-identical, 340/340 significant lines); the suite 65 files / 1,346 tests green; `check-no-emdash` and `check-no-internal-refs` clean; `BUILTIN_SCHEMAS` is `[Patient]`; pass 2's JSON counterexample reproduced; the corrected window wording true and the public and private docblocks now in agreement; the `CHANGELOG` direction fix correct; and the deleted `conformant` assertion already held, more strongly, by `test/xml-quantity-magnitude.test.ts`. It could not re-derive `verify.sh` or the red-at-base fraction and said so. ## The gate, in full `conformance-refuter` **REFUTED -> REFUTED -> NOT REFUTED**, cumulative 3, on `d96f97d`, `9760562` and `23cd9fa`. **Not one pass refuted the code.** Every finding across all three was a claim defect: pass 1 two majors, pass 2 one major, pass 3 two minors, each one the *reason* reaching past the *measurement*, and each remedy a deletion or a narrowing. The code was upheld on all three passes, which independently verified `booleanOf` unwidened, the complement exact, `safeToSummarize` monotone toward refusal, and the report's window a superset of the read. **ADR 0027 is NOT invoked and is not needed**: the gate converged on its own with a `NOT REFUTED` third pass, so there is no unconverged verdict to dispose of. This commit is the ordinary after-the-cap advisory application, disclosed here, in the PR body and in the repo's notes. `verify.sh fhir` green: 11 `==>` headers, 11 `ran:` entries, zero `(FAIL)`, no unladdered-script warning.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
FHIR-XML-UNREADABLE-BOOLEAN-IS-SILENT, a STOP-THE-LINE filed under meta-repo ADR 0016, raised by#79's gate outside its slice and reproduced independently by the coordinator and by this slice's ownprobe at the base commit
05ecc5a:#79closed the first row."1"and"Y"are ordinary v2 and C-CDA converter output for a boolean,and R4's
booleanlexical space istrue|false(datatypes.html), so they are not readable ratherthan false. The reading was indistinguishable from
"0"/"N": an author who wrote "yes, do notadminister" got the same answer as one who wrote "no", with nothing recorded anywhere.
The gap was structural, and it was the item.
SafetyReadouthad location channels for content thecodec could not read (
nestedArrays,droppedText) and none for "value written, not readable".The change: a REPORT, not a wider read
booleanOfis byte-identical to#79's anddoNotPerformstill readsundefined. Widening the readto accept
"1"/"Y"would have been a worse defect than the one filed: it invents a reading R4 doesnot license, and it turns
<doNotPerform value="0"/>andvalue="N"into a JSfalsethatserializeResourcethen emits, authoring a value and laundering it across a format change, which iswhat
#74refused."1"and"Y"also arrive on real wires meaning a boolean'strueand,elsewhere, its opposite, so there is no safe direction to guess in.
What is added is
SafetyReadout.unreadableBooleans, a third not-readable location channel besidenestedArraysanddroppedText, withunreadableBooleans(resource, path)exported beside them. Itfeeds
safeToSummarizeandassertSafeToSummarize, and it is value-free: the text that failed toread reaches neither the locations nor the error message.
Detection is
hasUnreadableBoolean, the exact complement of the read: the same values through thesame array wrapper (
scalarValues), the samebooleanOf. A location is emitted for precisely thevalues
primitiveBooleansdeclined, so the report cannot drift from the read by growing a rule of itsown.
Decided in the safety layer, not the reader. The reader is schema-free:
<doNotPerform value="1"/>and
<status value="1"/>are the same node to the codec, so nothing at parse time knows the text shouldspell a boolean. The safety layer is the first place that knows the datatype.
Consumer-side additivity, established from the consumers and measured
#79paid a refuted pass for the rule that additivity is a property of the consumers, not thehelper. That class is unreachable here by construction, in its strongest form: this slice widens no
read at all. No value that was
undefinedbecomes defined anywhere, so nothing that treatsundefinedas inherit or absent ever sees a new value.SafetyReadout.unreadableBooleanssafeToSummarizeassertSafeToSummarizeonly (every other mention insrc/is prose)true -> false, never the reverseassertSafeToSummarizelocationsFhirSafetyError.locationsbooleanOf,readDoNotPerform,primitiveBoolean)Measured in a real
05ecc5aworktree over a 33-document corpus, comparing everySafetyReadoutfield that exists on both trees, both writers' output, the parse issues, the
ValidationIssuelist andvalid: 13 documents move, and in every one the only field that moves issafeToSummarize,true -> false. The other 20 are identical on every channel. No diagnostic moves anywhere. Onepublic surface moves outside that set and is disclosed:
FhirSafetyError's message now names thesixth shape, so the string changes for every refusal it raises;
locationsand the thrown type areunchanged.
Does
safeToSummarizemove? Yes, and that is the answer rather than a side effectIts contract is that the library declines when a summary would have to assert something it cannot
establish.
value="1"andvalue="0"are indistinguishable to a reader with no licence to guess, soaffirming over either affirms over a coin flip, and the coin decides whether a medication is given.
Both now refuse. The fix does not make
"Y"and"N"read differently: they still read alike,because they are alike to this library. What changed is that the shared answer is a refusal.
The census, wider than the filed line
Booleans the safety spine reads out of a document: exactly one.
MedicationRequest.doNotPerform.retractedandnoKnownAllergyare derived from codes and codings, not from a boolean element. So thechannel is complete for its own layer and for nothing beyond it, and its docblock says so.
MedicationRequest.doNotPerformprimitiveBooleans(safety)ElementDefinition.mustSupportprimitiveBoolean(convenience)ElementDefinition.slicing.orderedprimitiveBoolean(convenience)ElementDefinition.min(unsignedInt)parseMinQuantitymagnitude (+5,05,.5,5.)readQuantity#78's pinnumberOf#79's censusThe two profile booleans have nowhere to go as well as
#79's measured retirement reason: aStructureDefinitionis not a safety resource and has noSafetyReadout.One deliberate asymmetry: no
ValidationIssueof its ownOn the default path this is the first place a
safeToSummarize: falsesits besidevalid: true.The reason is availability and nothing stronger, after two refuted attempts to say more:
MedicationRequesthas no built-in schema (BUILTIN_SCHEMASis[Patient]), so the validator issilent about this element's datatype unless a caller supplies one, and a readout that must hold on
every document cannot rest on a diagnostic that only exists on request. The shape channels are not
scoped that way and still fire with no schema at all. Both paths pinned.
Measurements
05ecc5aworktree with a base shim supplyingthe new surface with base's own semantics (an always-empty channel), so each test measures behaviour
rather than a missing export. The 12 both-states pins, named: the conformant
trueandfalsespellings, the absent element, a value-less primitive carrying only metadata, the
non-
MedicationRequesttype gate, a conformant JSONMedicationRequest, this package's own XMLround trip, the no-
ValidationIssuepin, the object / empty-array shape, the JSONnull, the twoprofile booleans, and the non-boolean datatype.
MedicationRequesttypegate reds 1; stopping
hasUnreadableBooleanreading through the array wrapper reds 2; firing it on avalue-less primitive reds 2; dropping the new term from
safeToSummarizereds 13; accepting"1"inbooleanOfreds 1; reading onlynode.propertiesand not the shadowedduplicatesreds 1; droppingthe channel from
assertSafeToSummarize's location list reds 1.verify.sh fhirgreen: 11==>step headers, 11ran:entries, zero(FAIL), nounladdered-script warning. Suite 65 files / 1,346 tests =
#79's 1,316 + this file's 30, so noexisting test moved.
The pin this closes, rewritten rather than deleted
#79's residual pin "still affirms safeToSummarize over a doNotPerform this reader cannot read"looped over
["true", "1"]. The"1"arm is what this slice closes, so the pin now covers the"true"arm only, is renamed to say so, and names where the other half went. It was the only test in the
whole suite that moved.
The gate
conformance-refuter, REFUTED -> REFUTED -> NOT REFUTED, cumulative 3 (d96f97d,9760562,23cd9fa).and every rule it carries is about a shape FHIR gives no meaning to at any position", measured false:
validateResourcetakes{ schemas }and decidesbooleanon the resolved datatype. And the carriersweep stopped one short of
#79's pending changeset, which still carried both clauses this slicefalsifies; since
CHANGELOG.mdhas no released heading, the changesets are the release body, sochangeset versionwould have published a release note asserting both that the shape survives andthat it is closed. Both cut, not qualified.
TYPE_MISMATCHon the conformantvalue="true"too, so it does not separate readable fromunreadable", was drawn from three XML rows and is false on the JSON wire, where a conformant
truevalidates clean with a schema supplied. Deleted at six carriers, along with the testassertion that existed only to support it.
independently re-derived the docblock-only remedy, the suite, both repo gates,
BUILTIN_SCHEMAS, theJSON counterexample, the corrected window wording, the
CHANGELOGdirection fix, and that the deletedassertion is already held more strongly by
test/xml-quantity-magnitude.test.ts.Not one pass refuted the code. Every finding across all three was the reason reaching past the
measurement. All three independently verified
booleanOfunwidened, the complement exact,safeToSummarizemonotone toward refusal, and the report's window a superset of the read.One commit is UNGRADED and disclosed:
98e78efapplies pass 3's two minors (narrowing "silentabout this element" to its datatype, and scoping a test comment's rebuttal to its wire). It is
documentation and one test comment;
git diff 23cd9fa 98e78ef -- src/is docblock text and the suite isunchanged. Applied rather than deferred because one carrier is
CHANGELOG.md, frozen while #73 is open,where an over-wide clause would be permanent under ADR 0001. ADR 0027 is not invoked and is not
needed: the gate converged on its own.
Raised by the gate, PRE-EXISTING, and each wants a backlog item
ServiceRequest.doNotPerformandCommunicationRequest.doNotPerformare?!boolean modifiers thesafety spine never reads. Neither type is in
SAFETY_RESOURCE_TYPES, so a conformant{"resourceType":"ServiceRequest","doNotPerform":true}readsnegations: [],safeToSummarize: true,assertSafeToSummarizeclean. Identical at05ecc5a.readDoNotPerformis root-only, so aBundlewhoseentry[0].resourceis a conformantMedicationRequestwithdoNotPerform: truereadsnegations: []andsafeToSummarize: true, whilethe unreadable spelling at the same location is reported. Pass 3's disposition, adopted: fold it
into the item above, since both are the spine's read scope and not a report.
README.mdand sixsrc/sites say "the six safety resource types" whileSAFETY_RESOURCE_TYPESholds seven. Stale count, unrelated to this item, left standing.
Both safety findings are recorded in
documentation/agent-notes/xml-unreadable-boolean.md; the backlogitems are the coordinator's to file.
Budget
documentation/agent-notes.mdstood at 249,983 of its 250,000-byte cap, so the cursor did not fit.The ceiling is not raised.
#77's "untaggableresourceType" narrative is relocated verbatimto
documentation/agent-notes/untaggable-resource-type.mdwith a cursor left behind (it had zeroinbound anchors), bringing the archive to 239,092. Nothing was deleted; pass 1 verified the relocation
line-for-line.
Corpus caveat
Hand-authored XML and JSON fixtures, mutations and hand-built probes, not the FHIR R4
published-examples corpus. Nothing here is corpus-wide.
Not touched
PR #73 (Version Packages) is untouched and stays open. No umbrella pointer bump in this PR.