@cosyte/astm 0.0.20 → 0.0.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -11,6 +11,81 @@ this file is maintained by hand (Changesets handles the version bump and publish
11
11
 
12
12
  ### Added
13
13
 
14
+ - **The PHI sweep now reads the bytes git carries, as a union with the working-tree walk.** All mode
15
+ reconciled the paths it walked against the paths git tracks, and a comparison of path _sets_ is
16
+ satisfied by a working tree that carries different content at those paths. Four states were
17
+ reproduced on the previous release, each printing `[phi-scan] OK: no hits` and exiting `0` over a
18
+ synthetic stream carrying a patient name, a mother's maiden name, a birthdate and a dashed SSN:
19
+ decoy content at a tracked path; a tracked path outside every walk root; a tracked symbolic link
20
+ or nested repository outside every walk root; and an empty index, against which every check passes
21
+ vacuously. All four now report or refuse. The mechanism is written down in exactly one place,
22
+ `buildTargetsForIndex` in `scripts/phi-scan.ts`, and every other surface states only what a green
23
+ means.
24
+ - **It is a union, never a replacement.** No scan root was narrowed and no clause dropped; a file
25
+ the walk reads is still read from disk with exactly the views it had, and a blob whose bytes the
26
+ walk has provably already scanned is skipped, so nothing is reported twice. The skip is a byte
27
+ comparison rather than a timestamp, a size or a hash, because those are exactly what a decoy
28
+ defeats.
29
+ - **The scan reported eighteen tracked files outside every walk root that no route had opened.**
30
+ One carried this package's own published contact address, which is already public in every
31
+ release's registry metadata and is not patient data; it is declared in
32
+ `scripts/phi-allow-list.txt` with the cost of that declaration written beside it, because an
33
+ allowed email domain is global and applies on every route.
34
+ - **A positive control proves the sweep opens the corpus it clears.** This package's own manifest
35
+ is placed at the same out-of-root path in a throwaway tree and the declaration is then struck:
36
+ the same corpus reports the address at exit `1`. A green over an unopened corpus is
37
+ indistinguishable from a green over a corpus that was read, so a case showing the scanner
38
+ passing proves nothing on its own.
39
+ - **What it deliberately does not do**, so a green is not read as wider than it is: it does not
40
+ credit a scan root (a root emptied on disk still refuses, though the index carries its files);
41
+ it does not reach `--staged`, whose scope decides what a _commit_ is blocked on; and markdown
42
+ stays excluded, which is the walk's own rule rather than a new one. Working-tree bytes at a path
43
+ outside every scan root are still read by neither route, unchanged from before and now stated.
44
+ - **Line endings are deliberately not normalized before the byte comparison.** Doing so would
45
+ compare a derived form of the two byte strings, and content differing only in what the
46
+ normalizer erases would then be skipped.
47
+
48
+ - **The two-file contract between `CLAUDE.md` and `documentation/agent-notes.md` is now gated**
49
+ (`pnpm check:agent-notes`, `scripts/check-agent-notes.ts`), with the teeth in
50
+ `test/scripts/agent-notes.test.ts` so it rides the required `ci / verify` matrix rather than
51
+ adding a workflow that would have to be made a required context separately. It asserts that every
52
+ pointer resolves, that no section a pointer reaches is empty, that the record stays reachable from
53
+ the always-read file, and that the always-read file still links the record by path. It asserts
54
+ **no** ecosystem-wide contract: several cosyte repos carry no such record at all, and a gate
55
+ phrased as though every repo did would be an overclaim.
56
+ - **The matcher was re-derived against this tree rather than ported, and that was the whole job.**
57
+ Both sibling spellings score **zero** here: the path-qualified form used by `ccda` and `mllp`,
58
+ and the basename-qualified form used by `terminology`. Every pointer in this repository is a
59
+ third spelling neither sibling gate matches, a backtick-quoted anchor with no filename at all. A
60
+ verbatim port would have reported "all resolving" over a corpus it never opened. Both sibling
61
+ forms are matched anyway so a pasted pointer is never invisible, and the live form falling to
62
+ zero is a refusal rather than a pass.
63
+ - **The anchor space is this record's explicit `<a id>` tags, not GitHub heading slugs.** Not one
64
+ pointer here resolves to a heading slug, because the headings carry long titles with dates and
65
+ status in them. A gate checking only slugs, which is what the siblings check, would have
66
+ reported every pointer in the repository as dangling.
67
+ - **An early draft was vacuous rather than wrong, and only a positive control found it.** Treating
68
+ an explicit anchor and the heading it precedes as separate sections made the empty-section
69
+ assertion cover nothing: the anchor looked empty and the heading looked unreferenced, so both
70
+ were skipped and a deliberately emptied section still printed `OK`. Anchors are now bound to the
71
+ heading they name.
72
+ - **The one declared non-pointer is pinned to the single sentence it describes**, and refuses both
73
+ when it matches nothing (a phantom skip) and when it matches more than once. The second case
74
+ fired during this change's own build.
75
+ - The corpus is `git ls-files` reconciled as sets, with **no exclusion list, no binary skip and no
76
+ NUL skip**; this repository tracks NUL-bearing TypeScript sources, most of them tests, that a NUL
77
+ partition would drop in silence. A pointer is matched if and only if the file spells it in ASCII
78
+ bytes, pinned in both directions by test. Every tracked file is scanned, so a backticked anchor
79
+ is a live pointer wherever it is written, **including `README.md` and `docs-content/`, which
80
+ ships in an immutable release asset**: reference a section by title on any surface that ships.
81
+ - **CI caught a defect the local run structurally could not, and the gate was right.** Because the
82
+ corpus is the index, a new file is invisible until staged: `verify.sh` ran green while both new
83
+ files were untracked, and CI went red once they were tracked, because the gate's own test file
84
+ spelled pointers in its fixtures that were then scanned against the real record. Both the checker
85
+ and its test now **build** pointer strings at run time rather than spelling them, pinned by a
86
+ test; exempting those paths would have been the exclusion list the gate refuses to have. **Re-run
87
+ a corpus-scanning gate after staging, never only before.**
88
+
14
89
  - **The PHI commit-gate now reads a stream written as a source string literal, and refuses a sweep
15
90
  that did not observe its corpus.** Two separate holes, closed together because closing one alone
16
91
  buys only the cross-cutting SSN/email floor.
@@ -444,6 +519,10 @@ this file is maintained by hand (Changesets handles the version bump and publish
444
519
  that could ever protect conformant traffic, and refusing it is the over-refusal that sank a
445
520
  criterion measured and rejected for this same family. The widened predicate **cannot** refuse an
446
521
  escape-clean stream: every position it now rejects already raises an escape deviation of its own.
522
+ **Both sentences in this paragraph are scoped later in this same release**, under "The companion
523
+ universal is false where the declaration names the escape character in a splitting role too": they
524
+ hold wherever the escape role is a character distinct from the three splitting roles, and not where
525
+ it is not. They are kept as written because the measurement this entry records starts from them.
447
526
 
448
527
  **That remaining silence is a TRADE, and it is not a claim that nothing was lost there.** On the
449
528
  excluded tail the gained boundary is exactly as real, and it is not merely under-reported: it is
@@ -1072,6 +1151,126 @@ this file is maintained by hand (Changesets handles the version bump and publish
1072
1151
 
1073
1152
  ### Documented
1074
1153
 
1154
+ - **The corpus that certifies the contested-alignment correction never observed one of the three
1155
+ codes it certifies, and three of the figures the correction quotes were asserted only as "more
1156
+ than zero".** `PRE-EXISTING`, all three named by the refuter passes on the preceding slice and
1157
+ backlogged there rather than closed. No guard moves: no runtime code is added, removed or renamed,
1158
+ no split changes, no extracted value moves, no stream's disposition changes, and no exported
1159
+ surface moves.
1160
+ - **The companion corpus wired only the repeat and component roles, so the
1161
+ `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` orphan population was outside it.** Which role a
1162
+ declaration puts the swept character in decides where the collision is; which delimiter the
1163
+ contested reading gains a boundary on decides which of the three codes fires. They are
1164
+ independent axes, and both arms of that corpus held the second one fixed by keeping the
1165
+ contested construct inside a single field of the carrier. Measured, that corpus observes
1166
+ `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` **zero** times, which is now asserted rather than left to
1167
+ be noticed. Two field arms sweep it: the collision stays in the repeat or component role, where
1168
+ it is expressible, while the gained boundary is on the field separator, which the swept
1169
+ character never is. That is what puts a field-code orphan in reach even though the field role
1170
+ cannot collide with the escape role. The arms pin their own figures: the distinct arm is 1,296
1171
+ tuples where 432 fire and **0** are orphans, the colliding arm is 1,296 tuples where 360 fire
1172
+ and **144** are orphans, and every one of those 144 carries
1173
+ `ASTM_RECORD_DELIMITER_ROLE_COLLISION` and is refused by the widest profile the safety gate
1174
+ permits, before and after. `H|F^F` with `C|1|I|FFF|F|G` is one of them, written out in full. The
1175
+ union assertions that carry the correction now read over all four arms, and on the wider
1176
+ "any tail code fired" population rather than on the one code each arm was built to observe.
1177
+ - **Three figures could drift with the suite staying green.** The report-count corpus asserted its
1178
+ firing population, its under-stating class and its over-stating class as "more than zero", so a
1179
+ corpus that had quietly lost nine tenths of each would still have passed. They are pinned now
1180
+ (3,072 firing, 1,568 carrying one warning on a displacement of two, 320 carrying two warnings on
1181
+ a displacement of one, 64 in the tie class), together with the whole joint distribution of
1182
+ report count against displacement, so any cell that moves reds the suite and names itself.
1183
+ Pinning the table surfaced a fact none of the three figures carried: **there is no cell at zero
1184
+ reports and zero displacement.** Every tuple this code says nothing about is displaced anyway,
1185
+ by one or by two, which is the standing exclusion for a recognized tail doing what it is for and
1186
+ is the silent third direction in which counting warnings fails.
1187
+ - **The retired universal, and the sites this entry changed.** The three test files that were
1188
+ recorded, plus `alignment-unrecognized-tail.test.ts` and
1189
+ `alignment-criterion-population.test.ts`, which word the same fact differently, plus the
1190
+ corollary about the escape companion at the "refuses NOT ONE escape-clean stream" tests in the
1191
+ first three. Each is cut or scoped rather than re-qualified in fresh words: the unqualified
1192
+ sentence is gone, the corpus's own bound (it holds the escape role at `&`) is stated in its
1193
+ place, and what that bound is worth is named at the file that measures it. The predicate's own
1194
+ implementation site in `src/common/escapes.ts` is scoped too, and the claim in the doc block on
1195
+ the private `headsInterpretableSequence` that it was "the single place that says what happens
1196
+ there" is deleted rather than repaired, having already been false against the README. Neither string reaches `dist/index.d.ts`, and no
1197
+ behaviour, no warning code and no documented surface moves. The paragraph earlier in this
1198
+ release that states it unqualified is kept as written, because the measurement it records starts
1199
+ from it, and now says where it is scoped.
1200
+
1201
+ - **Two claims the contested-alignment codes rest on were false, and both were on consumer
1202
+ surfaces.** `PRE-EXISTING`, both named by the third refuter pass on the tail-residue slice and
1203
+ backlogged there rather than closed. No guard moves, no code is added, removed or renamed, no
1204
+ split changes, no extracted value moves, and no stream's disposition changes. The remedy is the
1205
+ claim, for the fifth time in this family.
1206
+ - **The companion universal is false where the declaration names the escape character in a
1207
+ splitting role too.** The three tail codes rest on a defence against over-refusal: firing
1208
+ requires an escape character heading no sequence this codec can interpret, and the package
1209
+ already reports each of those, so a stream whose escaping raises nothing can never be refused.
1210
+ That was written and asserted as a universal over the whole firing population. Every corpus in
1211
+ this family fixes the escape role at `&`, so none of them could see the case that breaks it. On
1212
+ a colliding set one byte both opens a sequence and ends a segment, the split claims it first,
1213
+ and neither escape report ever sees a sequence to raise: a tail code fires with **neither**
1214
+ companion. Measured on the committed corpus in
1215
+ `test/records/alignment-companion-universal.test.ts`, whose figures that file pins so they can be
1216
+ re-derived: the distinct arm is 2,304 tuples, 1,536 fire, **0** are orphans; the collides arm is
1217
+ 1,008 tuples, 648 fire, **288** are orphans, and **0** orphan in either arm lacks
1218
+ `ASTM_RECORD_DELIMITER_ROLE_COLLISION`. The defence
1219
+ therefore survives in the form it was needed in (no stream whose escaping **and** whose
1220
+ declaration are both clean is refused by a tail code) and not in the form it was written in.
1221
+ **That replacement is scoped to the STREAM and does not hold per message**: the collision is
1222
+ reported once per set change rather than once per record, so a second header re-declaring the
1223
+ same colliding set raises nothing while the tail codes in its message fire again, leaving a
1224
+ consumer that scopes warnings to a message looking at one of the three codes standing entirely
1225
+ alone. Also measured, and stated because "three splitting roles" reads as three reachable
1226
+ collisions: the **field** role cannot collide with the escape role at all, because the
1227
+ declaration is the three characters after the field separator and stops at the next one, so
1228
+ such a header terminates itself one character short and is refused.
1229
+ - **"One place further right" understates.** It is a floor, not a figure, and it was already
1230
+ known to be wrong in one direction (the tie class, where the offset is zero). It is wrong in
1231
+ the other direction too, and every corpus in this family fixes the axis that shows it: they
1232
+ carry exactly one contested construct per record. The competing reading resumes a character
1233
+ further on at each contested position, so it falls further out of step and the gap widens once
1234
+ per construct. `A&Z&|&BX&Z&|&F&C` reads **three** fields against **one**, and a chain of n
1235
+ constructs displaces by n, measured to n = 6. **The number of warnings is not the displacement
1236
+ either, in either direction**: over 3,072 firing tuples on a two-construct corpus, one warning
1237
+ sits on a displacement of two in 1,568 of them (a gained boundary whose tail is a recognized
1238
+ mnemonic is excluded from the report and still displaces) and two warnings sit on a
1239
+ displacement of one in 320 (one of the constructs is a tie). A consumer cannot recover the
1240
+ offset by counting, and the corrected surfaces say to read the raw line instead. The practical
1241
+ consequence is that one code covers two opposite outcomes: on
1242
+ `R|1|^^^687|28.6&F&|&Z&U/L||||F` the sender's trailing `F` lands **in** the status slot and
1243
+ reads as `final`, while on `R|1|^^^687|28.6&Z&|&BX&Z&|&F&U/L||||F` the same displacement runs
1244
+ twice, the `F` overshoots, and the status reads `unspecified`.
1245
+ - **Every other surface now names the KIND of cost and leaves the MAGNITUDE to one paragraph.**
1246
+ That is the rule the tail-residue slice paid three refuter passes for: a paraphrase at each sink
1247
+ is a separate claim that goes stale on its own, and seven copies of one had survived two sweeps.
1248
+ Both corrected statements live on `ShiftedFieldsSink`, which is exported and therefore reaches
1249
+ `dist/index.d.ts`, and which all three warning factories already point at. The magnitude was
1250
+ being paraphrased in **three** different vocabularies ("one place further right", "shifts every
1251
+ later field one place", "moves one slot along"), which is why the first sweep of this slice
1252
+ missed most of them; a newline-folded grep over all three found nineteen, in
1253
+ `src/common/escapes.ts`, `src/common/warnings.ts` (including the three `WARNING_CODES` registry
1254
+ entries and the three runtime messages), `src/profiles/safety.ts`,
1255
+ `src/profiles/reference-corpus.ts` (a runtime string), `src/records/parse.ts`,
1256
+ `src/records/tokenize.ts`, `README.md`, `docs-content/troubleshooting.md`,
1257
+ `docs-content/quickstart.md` and `docs-content/limitations.md`. All of them are corrected. The
1258
+ three runtime **messages**, which no gate in this repo reads for truth, now say the displacement
1259
+ is not fixed, name its three values, and tell the operator that counting warnings does not give
1260
+ it.
1261
+ - **"At least one place" was rejected as the replacement wording**, although it was the obvious
1262
+ one: it contradicts the tie class named in the same sentence, where the displacement is zero.
1263
+ Every corrected surface says the displacement is **not fixed** and names its three values
1264
+ instead.
1265
+ - **Both were RED before and are GREEN after, against the retired assertions verbatim.**
1266
+ `alignment-shifted-fields.test.ts` asserted `leftmost.length === competing.length + 1` and
1267
+ `alignment-unrecognized-tail.test.ts` asserted an escape companion on every firing tuple; on the
1268
+ swept axes the first reads 10 against an expected 9 and the second reads false. Both files now
1269
+ scope those assertions to the corpus that measures them instead of stating them as universals.
1270
+ Pinned in `test/records/alignment-companion-universal.test.ts` and
1271
+ `test/records/alignment-offset-rephasing.test.ts`, each sweeping the axis its family's corpora
1272
+ hold fixed, each carrying a negative control that is run rather than declared.
1273
+
1075
1274
  - **A header delimiter-declaration surplus loses all 31 of the C0/`DEL` characters that can reach
1076
1275
  it, and the published surfaces stated the preservation rule without that exception.**
1077
1276
  `PRE-EXISTING`; the
package/README.md CHANGED
@@ -244,7 +244,10 @@ gained repeat or component boundary does reach a modeled slot, and each of those
244
244
  its own below. It stays silent in exactly one case,
245
245
  where the trailing escape character heads a sequence this reader RECOGNIZES: that is the escape
246
246
  mechanism working, and refusing it would refuse well-formed traffic. That is the only tail on which
247
- a stream's escaping can be clean, which is why it is the only exclusion. **That silence is a trade
247
+ a stream's escaping can be clean, and so the only exclusion, **wherever the escape role is a
248
+ character distinct from the three splitting roles**. Where a header names the escape character in a
249
+ splitting role too, these codes can fire with neither escape report beside them, and what refuses the
250
+ stream is `ASTM_RECORD_DELIMITER_ROLE_COLLISION` instead. **That silence is a trade
248
251
  and not a claim that nothing was lost there**: the gained field boundary is exactly as real, and on
249
252
  that tail it is `warnings: []`, so `R|1|^^^687|28.6&F&|&F&U/L||||F` reads nine fields against the
250
253
  competing alignment's eight and hands back a status of `final` with nothing reported at all. Read
@@ -285,8 +288,10 @@ read as more repeats at all, and that class was already refused by
285
288
  survive a re-emit either: catch it on the first read.
286
289
 
287
290
  **And where that gained boundary is a COMPONENT boundary, nothing leaves the record and the slots
288
- MOVE.** Components are modeled inside a field, so every component after the gained boundary sits one
289
- place further right than the competing alignment puts it. On the canonical set,
291
+ MOVE.** Components are modeled inside a field, so every component after the gained boundary sits
292
+ further right than the competing alignment puts it, by a displacement that is **not fixed**: one
293
+ place where the field carries a single contested construct, one more for each additional one, and
294
+ none at all on the tie class. Counting warnings does not give it. On the canonical set,
290
295
  `R|1|&F&^&GLU^L^687|28.6|U/L||||F` reads a Universal Test ID of four components, so `L` is the coding
291
296
  scheme and `687` the vendor's local code; the competing alignment reads three, and `687` is the
292
297
  **coding scheme**. A code-system selector and a vendor's local code are not the same thing.
@@ -407,15 +412,15 @@ outside it:
407
412
  where the leftmost alignment lets a delimiter split that a competing alignment would have held,
408
413
  gains a boundary instead of losing one and raises `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`, also
409
414
  not tolerable. Where that gained boundary is a **field** boundary and the reading taken resumes on
410
- an escape character heading no sequence it can interpret, every later field shifts one place and a
415
+ an escape character heading no sequence it can interpret, every later field shifts and a
411
416
  result's units
412
417
  and status are read out of slots the other alignment does not put them in: that raises
413
418
  `ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, not tolerable either. Where it is a **repeat** boundary
414
419
  nothing shifts, but the field is read out of its first repeat alone, so a gained first boundary
415
420
  truncates a value and costs a test identity or a patient name the components that sat after it:
416
421
  that raises `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD`, not tolerable either. Where it is a
417
- **component** boundary nothing leaves the record and every component after it moves one slot
418
- along, so a coding scheme, a vendor local code or a given name is read out of a position the other
422
+ **component** boundary nothing leaves the record and every component after it moves along the
423
+ component list, so a coding scheme, a vendor local code or a given name is read out of a position the other
419
424
  alignment does not put it in: that raises `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`, not
420
425
  tolerable either.
421
426
 
package/dist/index.cjs CHANGED
@@ -273,9 +273,12 @@ var WARNING_CODES = {
273
273
  * Two escape alignments of the same bytes disagreed about a **field** boundary, the reading taken
274
274
  * kept it, and the escape character that reading resumes on **heads no sequence this codec can
275
275
  * interpret**, so the boundary was bought with bytes this reading cannot read while the competing
276
- * alignment is exactly the reading that can. Every field after that point sits one place further
277
- * right than the competing alignment puts it, **except on the one class named at the end of this
278
- * entry**, where the two readings instead read the same number of fields in different places.
276
+ * alignment is exactly the reading that can. Every field after that point sits further
277
+ * right than the competing alignment puts it, by a displacement that is **not fixed**: one place
278
+ * on a record carrying a single contested construct, one more for each additional one, and none
279
+ * at all **on the one class named at the end of this entry**, where the two readings instead read
280
+ * the same number of fields in different places. Counting these warnings does not give it; see
281
+ * {@link ShiftedFieldsSink}.
279
282
  *
280
283
  * **The shift is the harm, and on a result record it reaches the status slot.** Measured on the
281
284
  * canonical set, `R|1|^^^687|28.6&F&|&U/L||||F` reads **9** fields under the reading taken and
@@ -320,7 +323,8 @@ var WARNING_CODES = {
320
323
  * taken interprets a construct and leaves nothing unread: under a set naming the field separator
321
324
  * `F`, `28.6&F&F&F&U/L` is that separator escaped, written, and escaped again, entirely well
322
325
  * formed, and refusing it would be an over-refusal. That is the only tail on which a stream's
323
- * escaping can be clean, which is why it is the only exclusion. Where the tail heads a sequence
326
+ * escaping can be clean, on the declarations {@link ShiftedFieldsSink} scopes that to, which is
327
+ * why it is the only exclusion. Where the tail heads a sequence
324
328
  * whose body is *unrecognized*, the reading taken consumes a triple it cannot read, preserved
325
329
  * verbatim and never guessed at, the field shift is the same shift, and **this reports it**.
326
330
  * Consuming a triple is not interpreting one.
@@ -398,8 +402,8 @@ var WARNING_CODES = {
398
402
  *
399
403
  * **Two further deliberate bounds, both stated rather than left to be found.**
400
404
  * - **Only the repeat role.** The **component** role reaches a modeled slot too, and differently:
401
- * there the components stay in the record and move one slot along, so a local code reads as a
402
- * coding scheme and a given name as a middle name. That is measured and is now reported by
405
+ * there the components stay in the record and move along the component list, so a local code
406
+ * reads as a coding scheme and a given name as a middle name. That is measured and is now reported by
403
407
  * {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS}, a third code on the same tail
404
408
  * test wired to the component split. It is still not covered **here**, because wiring a sink to
405
409
  * another split is another criterion, which is why it took its own population measurement.
@@ -408,8 +412,8 @@ var WARNING_CODES = {
408
412
  * this codec *recognizes*, it interprets a construct and the repeats are the ones the sender
409
413
  * wrote: under a set naming the repeat separator `F`, `28.6&F&F&F&U/L` is that separator
410
414
  * escaped, written, and escaped again, entirely well formed, and refusing it would be an
411
- * over-refusal. That is the only tail on which a stream's escaping can be clean, which is why it
412
- * is the only exclusion. Where the tail heads a sequence whose body is *unrecognized*, the
415
+ * over-refusal. That is the only tail on which a stream's escaping can be clean, on the
416
+ * declarations {@link ShiftedFieldsSink} scopes that to, which is why it is the only exclusion. Where the tail heads a sequence whose body is *unrecognized*, the
413
417
  * reading taken consumes a triple it cannot read, preserved verbatim and never guessed at, the
414
418
  * truncation is the same truncation, and **this reports it**.
415
419
  * - **That remaining silence is a TRADE, not a claim that nothing was lost.** On the excluded tail
@@ -444,10 +448,11 @@ var WARNING_CODES = {
444
448
  * {@link WARNING_CODES.ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD}, on the third and last splitting
445
449
  * role, and what it costs is a third thing again, which is why it is a third code.
446
450
  *
447
- * **The components are neither shifted out of the record nor dropped from it: they MOVE one slot
448
- * along.** A field's components are modeled *inside* it, so a gained component boundary shifts
449
- * every component after it exactly as a gained field boundary shifts every later field, **except
450
- * on the one class named at the end of this entry**, where the two readings instead read the same
451
+ * **The components are neither shifted out of the record nor dropped from it: they MOVE along the
452
+ * component list.** A field's components are modeled *inside* it, so a gained component boundary shifts
453
+ * every component after it exactly as a gained field boundary shifts every later field, by the
454
+ * displacement {@link ShiftedFieldsSink} states, **except on the one class named at the end of
455
+ * this entry**, where the two readings instead read the same
451
456
  * number of components in different places. Nothing leaves the record and nothing changes field
452
457
  * number; what changes is which modeled slot each component lands in, and those slots are named
453
458
  * things:
@@ -499,7 +504,8 @@ var WARNING_CODES = {
499
504
  * *recognizes*, it interprets a construct and the components are the ones the sender wrote: under
500
505
  * a set naming the component separator `F`, `GLU&F&F&F&L` is that separator escaped, written, and
501
506
  * escaped again, entirely well formed, and refusing it would be an over-refusal. That is the only
502
- * tail on which a stream's escaping can be clean, which is why it is the only exclusion. Where the
507
+ * tail on which a stream's escaping can be clean, on the declarations
508
+ * {@link ShiftedFieldsSink} scopes that to, which is why it is the only exclusion. Where the
503
509
  * tail heads a sequence whose body is *unrecognized*, the reading taken consumes a triple it
504
510
  * cannot read, preserved verbatim and never guessed at, the slots move exactly as far, and **this
505
511
  * reports it**.
@@ -685,21 +691,21 @@ function ambiguousEscapeAlignment(position) {
685
691
  function alignmentShiftedFields(position) {
686
692
  return {
687
693
  code: WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS,
688
- message: "A field boundary here is one of two escape alignments the bytes carry, and the reading that took it resumes on an escape character heading no sequence this reader can interpret, which the other alignment uses to close one. The two readings disagree, and both consume every byte, so neither is forced. Usually every later field is one place further right than the other reading puts it, so a result's units and status may be read out of slots the sender did not put them in; one measured class does not shift at all, where the sequence past the boundary carries the field separator itself and the two readings read the same number of fields with different contents. The reading was kept and every byte is preserved.",
694
+ message: "A field boundary here is one of two escape alignments the bytes carry, and the reading that took it resumes on an escape character heading no sequence this reader can interpret, which the other alignment uses to close one. The two readings disagree, and both consume every byte, so neither is forced. How far the later fields are displaced is NOT fixed: one place where the record carries a single contested construct, one more for each additional one, and none at all on one measured class, where the sequence past the boundary carries the field separator itself and the two readings read the same number of fields with different contents. So a result's units and status may be read out of slots the sender did not put them in. Counting these warnings does not give the displacement. Read the raw line. The reading was kept and every byte is preserved.",
689
695
  position
690
696
  };
691
697
  }
692
698
  function alignmentTruncatedField(position) {
693
699
  return {
694
700
  code: WARNING_CODES.ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD,
695
- message: "A repeat boundary here is one of two escape alignments the bytes carry, and the reading that took it resumes on an escape character heading no sequence this reader can interpret, which the other alignment uses to close one. The two readings disagree, and both consume every byte, so neither is forced. Usually this field is read as more repeats than the other reading gives it, and its modeled value and components are taken from the first repeat alone, so where that boundary is the first one the rest of the field leaves every modeled slot; one measured class reads the same number of repeats, where the sequence past the boundary carries the repeat separator itself. The reading was kept and every byte is preserved.",
701
+ message: "A repeat boundary here is one of two escape alignments the bytes carry, and the reading that took it resumes on an escape character heading no sequence this reader can interpret, which the other alignment uses to close one. The two readings disagree, and both consume every byte, so neither is forced. How many more repeats this field is read as is NOT fixed: one where the record carries a single contested construct, one more for each additional one, and none at all on one measured class, where the sequence past the boundary carries the repeat separator itself. Its modeled value and components are taken from the first repeat alone, so where that boundary is the first one the rest of the field leaves every modeled slot. Counting these warnings does not give the difference. Read the raw line. The reading was kept and every byte is preserved.",
696
702
  position
697
703
  };
698
704
  }
699
705
  function alignmentShiftedComponents(position) {
700
706
  return {
701
707
  code: WARNING_CODES.ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS,
702
- message: "A component boundary here is one of two escape alignments the bytes carry, and the reading that took it resumes on an escape character heading no sequence this reader can interpret, which the other alignment uses to close one. The two readings disagree, and both consume every byte, so neither is forced. Usually every later component of this field sits one place further right than the other reading puts it, so a test identity's coding scheme and local code, or a patient's given and middle names, may be read out of slots the sender did not put them in; one measured class does not move at all, where the sequence past the boundary carries the component separator itself and the two readings read the same number of components with different contents. The reading was kept and every byte is preserved.",
708
+ message: "A component boundary here is one of two escape alignments the bytes carry, and the reading that took it resumes on an escape character heading no sequence this reader can interpret, which the other alignment uses to close one. The two readings disagree, and both consume every byte, so neither is forced. How far the later components of this field are displaced is NOT fixed: one place where the field carries a single contested construct, one more for each additional one, and none at all on one measured class, where the sequence past the boundary carries the component separator itself and the two readings read the same number of components with different contents. So a test identity's coding scheme and local code, or a patient's given and middle names, may be read out of slots the sender did not put them in. Counting these warnings does not give the displacement. Read the raw line. The reading was kept and every byte is preserved.",
703
709
  position
704
710
  };
705
711
  }
@@ -1328,7 +1334,7 @@ var referenceCorpus = defineAstmProfile({
1328
1334
  tolerate: [
1329
1335
  {
1330
1336
  code: "ASTM_UNKNOWN_ESCAPE_SEQUENCE",
1331
- rationale: "The OSS reference stack (python-astm codec.py, firsthand-verified) treats '&' as literal data and never decodes escape sequences, so streams from that ecosystem carry non-standard '&\u2026&' bodies our escape-aware tokenizer flags. The body is preserved byte-for-byte in the value. NOT always non-clinical, and this is measured rather than assumed: where the body is a delimiter, that delimiter never became a field boundary, so `28.6&|&U/L` reads as one value with no units and no final status. That subset now also raises ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE, which this profile cannot tolerate, so tolerating this code no longer hides it. The same holds for the mirror case, where a sequence ends where another could have begun and a delimiter splits that the other alignment would have held: that raises ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT, which this profile cannot tolerate either. Where that gained boundary is a field boundary and the reading taken resumes on an escape character heading no sequence this reader can interpret, every later field shifts and a result's units and status move with it, which raises ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS, untolerable here as well. Where that same gained boundary is a repeat boundary, nothing shifts but the field is read out of its first repeat alone, so a value truncates and a test identity empties, which raises ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD, untolerable here too. Where it is a component boundary, nothing leaves the record and no field number changes, and every component after it moves one slot along, so a test identity's coding scheme and local code and a patient's given and middle names are read out of positions the other alignment does not put them in, which raises ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS, untolerable here as well. Each of those three shift claims has one measured exception, where the sequence past the boundary carries the splitting delimiter itself: the two readings then read the same number of segments in different places, nothing moves index, and the record was already refused by ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE. All three carry the same tail bound and stay silent in exactly one case, where the escape character past the boundary heads a sequence this reader RECOGNIZES, which is the escape mechanism working. How often that happens on this corpus is NOT measured and must not be guessed at: the firsthand grounding above is of the DECODE path only, and a relayed stream can still carry a device-written mnemonic. Tolerating this code does not quiet those three, and their absence is not a statement that nothing was lost."
1337
+ rationale: "The OSS reference stack (python-astm codec.py, firsthand-verified) treats '&' as literal data and never decodes escape sequences, so streams from that ecosystem carry non-standard '&\u2026&' bodies our escape-aware tokenizer flags. The body is preserved byte-for-byte in the value. NOT always non-clinical, and this is measured rather than assumed: where the body is a delimiter, that delimiter never became a field boundary, so `28.6&|&U/L` reads as one value with no units and no final status. That subset now also raises ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE, which this profile cannot tolerate, so tolerating this code no longer hides it. The same holds for the mirror case, where a sequence ends where another could have begun and a delimiter splits that the other alignment would have held: that raises ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT, which this profile cannot tolerate either. Where that gained boundary is a field boundary and the reading taken resumes on an escape character heading no sequence this reader can interpret, every later field shifts and a result's units and status move with it, which raises ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS, untolerable here as well. Where that same gained boundary is a repeat boundary, nothing shifts but the field is read out of its first repeat alone, so a value truncates and a test identity empties, which raises ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD, untolerable here too. Where it is a component boundary, nothing leaves the record and no field number changes, and every component after it moves along the component list, so a test identity's coding scheme and local code and a patient's given and middle names are read out of positions the other alignment does not put them in, which raises ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS, untolerable here as well. Each of those three shift claims has one measured exception, where the sequence past the boundary carries the splitting delimiter itself: the two readings then read the same number of segments in different places, nothing moves index, and the record was already refused by ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE. All three carry the same tail bound and stay silent in exactly one case, where the escape character past the boundary heads a sequence this reader RECOGNIZES, which is the escape mechanism working. How often that happens on this corpus is NOT measured and must not be guessed at: the firsthand grounding above is of the DECODE path only, and a relayed stream can still carry a device-written mnemonic. Tolerating this code does not quiet those three, and their absence is not a statement that nothing was lost."
1332
1338
  }
1333
1339
  ]
1334
1340
  });
@@ -2902,7 +2908,7 @@ function concatBytes3(chunks) {
2902
2908
  }
2903
2909
 
2904
2910
  // src/index.ts
2905
- var VERSION = "0.0.20";
2911
+ var VERSION = "0.0.22";
2906
2912
 
2907
2913
  exports.ALL_ASTM_WARNING_CODES = ALL_ASTM_WARNING_CODES;
2908
2914
  exports.AMBIGUOUS_CODES = AMBIGUOUS_CODES;