@cosyte/astm 0.0.18 → 0.0.20
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 +359 -19
- package/README.md +75 -18
- package/dist/index.cjs +220 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +539 -135
- package/dist/index.d.ts +539 -135
- package/dist/index.mjs +220 -54
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,49 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
11
11
|
|
|
12
12
|
### Added
|
|
13
13
|
|
|
14
|
+
- **The PHI commit-gate now reads a stream written as a source string literal, and refuses a sweep
|
|
15
|
+
that did not observe its corpus.** Two separate holes, closed together because closing one alone
|
|
16
|
+
buys only the cross-cutting SSN/email floor.
|
|
17
|
+
- **Scan roots.** The all-mode walk covers `src`, `test` and `scripts`, where it previously
|
|
18
|
+
covered `src` and `test/fixtures` only. Measured against `git ls-files` on the commit before
|
|
19
|
+
this change: 106 of 165 tracked files were scanned by neither enumerating route, 66 of them
|
|
20
|
+
under `test/`, and 33 of those 66 contain a patient-record literal. Those fixtures were read by
|
|
21
|
+
hand before.
|
|
22
|
+
- **A source-embedded view.** The record-aware detector splits on newlines and asks whether a line
|
|
23
|
+
begins with the patient-record type letter. Most of this package's streams are string literals,
|
|
24
|
+
so every one of them arrived as a single line beginning with a quote and was never read,
|
|
25
|
+
including inside `src`, which the walk already covered. The detector now also runs over a view
|
|
26
|
+
with the source escape sequences decoded and the string-literal delimiters treated as record
|
|
27
|
+
boundaries, in addition to the raw bytes. Both are needed: an escape sequence supplies a
|
|
28
|
+
boundary only for a stream written whole in one literal, and this package also writes a single
|
|
29
|
+
record per literal and a stream as array elements joined at run time. The decode is one
|
|
30
|
+
left-to-right pass that consumes an escaped backslash as a pair; it runs only on a closed set of
|
|
31
|
+
source file extensions, so it cannot invent a record boundary in wire data whose backslash is
|
|
32
|
+
the repeat delimiter; and delimiters are read from the line view only, so a quoted fragment of
|
|
33
|
+
prose cannot redefine the delimiter set for a whole file.
|
|
34
|
+
- **An observation rule.** A sweep refuses (exit 2) when a declared root observed no files, when
|
|
35
|
+
the invocation as a whole observed none, and when git tracks in-scope files under a root that
|
|
36
|
+
the walk did not observe. A missing root, an emptied root, a dangling symbolic link as a root,
|
|
37
|
+
and a root that is a regular file are all covered; the last previously raised an uncaught
|
|
38
|
+
`ENOTDIR`, which exits 1, the code that means hits were found.
|
|
39
|
+
- **Two precision guards, each with its bound stated where it is applied.** A line that merely
|
|
40
|
+
starts with the patient-record type letter is not read as a patient record: its second field
|
|
41
|
+
must be a short digit run, which this package's own builder always writes there. And a name
|
|
42
|
+
component of one character is not read, so a bare middle initial does not have to be exempted
|
|
43
|
+
by name for the whole repository.
|
|
44
|
+
- What a green from the scan means is now written out in one place, `scanTarget`, with every
|
|
45
|
+
locus, record shape, token and file it does not cover named there. No other surface restates
|
|
46
|
+
it.
|
|
47
|
+
- The scanner resolves its repository from its own file location rather than from the working
|
|
48
|
+
directory, and every `git` subprocess it runs is pinned to that repository. Run from a parent
|
|
49
|
+
checkout it previously walked that tree and reported clean about this package having never
|
|
50
|
+
opened it.
|
|
51
|
+
- **`pnpm check`, a gate-coverage check** (`scripts/check-gate-coverage.ts`). It asserts that every
|
|
52
|
+
command this repository's own workflows run is reachable as a `pnpm run` script or is declared
|
|
53
|
+
with the reason it is not, that every `run-<x>: true` input passed to a shared pipeline has the
|
|
54
|
+
script that pipeline will call, and that the two gates whose names carry no recognisable prefix
|
|
55
|
+
still exist. Every declared-unreachable entry is printed on every run.
|
|
56
|
+
|
|
14
57
|
- **Two parse-path warning codes for two readings the parser could not defend, both landing on the
|
|
15
58
|
profile safety gate** (`ASTM-FRAME-RESIDUALS`, defects 4 and 11). Both are additive: no existing
|
|
16
59
|
warning was removed or renamed, no split changed, and every extracted value is byte-identical to
|
|
@@ -136,7 +179,8 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
136
179
|
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` fires where two escape alignments disagree about a **field**
|
|
137
180
|
boundary, the reading taken keeps it, and the escape character that reading resumes on heads **no
|
|
138
181
|
escape sequence at all**, which is exactly the character the competing alignment uses to close its
|
|
139
|
-
own sequence.
|
|
182
|
+
own sequence. (**That tail bound is widened later in this same release**, to any tail this reader
|
|
183
|
+
cannot interpret; the entry below carries the change and its measurement.) A gained field boundary shifts every later field one place. It is not tolerable, so
|
|
140
184
|
`{ strict: true }` refuses such a record whatever profile is in force.
|
|
141
185
|
|
|
142
186
|
**What the shift costs, measured on the canonical set.** `R|1|^^^687|28.6&F&|&U/L||||F` reads
|
|
@@ -180,16 +224,20 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
180
224
|
escaped again, entirely well formed, and refusing it is the over-refusal that sank a criterion
|
|
181
225
|
measured for this same family); and where it heads an **unrecognized** sequence, the alignment taken
|
|
182
226
|
still consumes it while the competing one would leave two escape characters bare, so the preference
|
|
183
|
-
|
|
184
|
-
residue
|
|
227
|
+
was read as stronger again, and this shipped silent there while the fields shifted just the same.
|
|
228
|
+
**That residue is closed later in this same release** (see the tail-bound entry below), which
|
|
229
|
+
corrects the reason rather than the code: the preference comparison is true and is not the question
|
|
230
|
+
this code asks.
|
|
185
231
|
|
|
186
232
|
**Measured against the corpus constants committed with the test**, on the same
|
|
187
233
|
strict-accepted-under-a-gate-legal-profile tier. Over `DECLARATION_ALPHABET`, `SPLITTING_ROLES`,
|
|
188
234
|
`BODY_ALPHABET` and `TAIL_SUFFIXES` in `test/records/alignment-shifted-fields.test.ts`, **864
|
|
189
|
-
tuples**:
|
|
190
|
-
the whole tolerable allow-list, and
|
|
191
|
-
|
|
192
|
-
|
|
235
|
+
tuples**: as this code shipped it fired on **96**, moved **32** from accepted to refused under a
|
|
236
|
+
profile built from the whole tolerable allow-list, and moved **0** back. **The tail-bound widening
|
|
237
|
+
below takes those to 192 and 64 on the same corpus**, which is what the committed test asserts now.
|
|
238
|
+
It fires on **none** of the 96 escape-clean tuples, and cannot, before or after: firing requires an
|
|
239
|
+
escape character heading no sequence this reader can interpret, and this package already reports
|
|
240
|
+
each of those as a deviation of its own. The criterion measured and rejected on this same corpus
|
|
193
241
|
would have refused 48 of them. The 32 that move are exactly the population the code above left
|
|
194
242
|
silent through its recognized-body exclusion.
|
|
195
243
|
|
|
@@ -207,9 +255,9 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
207
255
|
- **A parse-path warning code for a field a competing escape alignment read short**
|
|
208
256
|
(`ASTM-FRAME-RESIDUALS`, defect 17b). `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` fires where two
|
|
209
257
|
escape alignments of the same bytes disagree about a **repeat** boundary, the reading taken keeps
|
|
210
|
-
it, and the escape character that reading resumes on heads **no sequence at all**.
|
|
211
|
-
|
|
212
|
-
force.
|
|
258
|
+
it, and the escape character that reading resumes on heads **no sequence at all**. (**That tail
|
|
259
|
+
bound is widened later in this same release**; see the entry below.) It lands on the profile safety
|
|
260
|
+
gate, so `{ strict: true }` refuses a stream carrying it whatever profile is in force.
|
|
213
261
|
|
|
214
262
|
**Nothing shifts, and that is not the same as nothing being lost, which is the correction this
|
|
215
263
|
entry carries.** No field-indexed slot moves: the units slot and the result-status slot are read
|
|
@@ -244,20 +292,23 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
244
292
|
|
|
245
293
|
**Two further bounds, both deliberate.** It is wired to the **repeat** separator only: a gained
|
|
246
294
|
**component** boundary reaches a modeled slot too, and differently, moving it one slot along rather
|
|
247
|
-
than dropping it,
|
|
295
|
+
than dropping it, which is the entry below. And the tail is
|
|
248
296
|
weighed **one construct deep**, on the same test the shift report uses: where the escape character
|
|
249
297
|
past the boundary heads a **recognized** sequence the bytes prefer the reading taken and the two
|
|
250
298
|
repeats are the ones the sender wrote (`28.6&R&\&R&U/L` is the canonical repeat separator
|
|
251
299
|
escaped, written and escaped again, and refusing it is the over-refusal that sank a criterion
|
|
252
300
|
measured for this same family), and where it heads an **unrecognized** one the competing alignment
|
|
253
|
-
would leave two escape characters bare, so the preference
|
|
254
|
-
|
|
301
|
+
would leave two escape characters bare, so the preference was read as stronger again, and this
|
|
302
|
+
shipped silent there while the field truncated just the same. **That residue is closed later in
|
|
303
|
+
this same release** (see the tail-bound entry below).
|
|
255
304
|
|
|
256
305
|
**Measured against the corpus constants committed with the test**, on the same
|
|
257
306
|
strict-accepted-under-a-gate-legal-profile tier and the same **864 tuples** as the two measurements
|
|
258
|
-
before it, in `test/records/alignment-truncated-field.test.ts`:
|
|
259
|
-
**32** from accepted to refused under a profile built from the whole tolerable
|
|
260
|
-
**0** back, and
|
|
307
|
+
before it, in `test/records/alignment-truncated-field.test.ts`: as this code shipped it fired on
|
|
308
|
+
**96**, moved **32** from accepted to refused under a profile built from the whole tolerable
|
|
309
|
+
allow-list, moved **0** back, and fired on **none** of the 96 escape-clean tuples. **The
|
|
310
|
+
tail-bound widening below takes the first two to 192 and 64 on the same corpus**, which is what the
|
|
311
|
+
committed test asserts now, and leaves the escape-clean zero where it is. The pair-count criterion measured
|
|
261
312
|
and rejected on this same corpus would have refused 48 of them, which is why the tail is what gets
|
|
262
313
|
weighed. **Unlike that rejected criterion's population, this one is not confined to a set naming a
|
|
263
314
|
mnemonic letter as a delimiter**: the canonical repeat separator reaches it, on 12 of the 108
|
|
@@ -276,6 +327,211 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
276
327
|
factory, the `TruncatedFieldSink` type, and a further trailing optional sink parameter on
|
|
277
328
|
`splitEscapeAware`, `tokenizeRecord` and `tokenizeHeader`.
|
|
278
329
|
|
|
330
|
+
- **A parse-path warning code for components a competing escape alignment moved one slot along**
|
|
331
|
+
(`ASTM-FRAME-RESIDUALS`, defect 17c). `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` fires where two
|
|
332
|
+
escape alignments of the same bytes disagree about a **component** boundary, the reading taken
|
|
333
|
+
keeps it, and the escape character that reading resumes on heads **no sequence at all**. (**That
|
|
334
|
+
tail bound is widened later in this same release**; see the entry below.) It lands on the profile
|
|
335
|
+
safety gate, so `{ strict: true }` refuses a stream carrying it whatever profile is in force. With it the three roles a split is taken on are all wired; there is no fourth, because
|
|
336
|
+
nothing splits on the escape role.
|
|
337
|
+
|
|
338
|
+
**Nothing shifts between fields and nothing leaves the record: the slots MOVE.** That is the third
|
|
339
|
+
answer to the one question the two entries above ask, and it is why neither of them could carry it.
|
|
340
|
+
Components are modeled **inside** a field, so every component after the gained boundary sits one
|
|
341
|
+
place further right than the competing alignment puts it, and the slots that indexes into are named
|
|
342
|
+
things. Both costs are reachable on the **canonical** set, so no unusual declaration is needed:
|
|
343
|
+
- **A code system and a vendor's local code swap places.** `R|1|&F&^&GLU^L^687|28.6|U/L||||F`
|
|
344
|
+
reads a Universal Test ID of four components, so `L` is the coding scheme and `687` the local
|
|
345
|
+
code; the competing alignment reads three, and `687` is the **coding scheme**. A code-system
|
|
346
|
+
selector and a vendor's local code are not the same thing, so a consumer routing on either is
|
|
347
|
+
routing on the alignment.
|
|
348
|
+
- **A given name and a middle name shift along.** `P|1||MRN-0001||DOE&F&^&JANE^A||19700101|F`
|
|
349
|
+
reads a given name of `&JANE` and a middle name of `A`; under the competing alignment `A` is the
|
|
350
|
+
**given** name and there is no middle name at all.
|
|
351
|
+
|
|
352
|
+
Before this code the only warning on either stream was the tolerable
|
|
353
|
+
`ASTM_UNPAIRED_ESCAPE_CHARACTER`, so the widest gate-legal profile plus `{ strict: true }` accepted
|
|
354
|
+
both. **This closes a claim that had been recorded as false and shipped**: "a gained repeat or
|
|
355
|
+
component boundary divides one field and reaches nothing outside it, so it cannot move a modeled
|
|
356
|
+
slot". The first clause is true and the inference is not.
|
|
357
|
+
|
|
358
|
+
**Every gained boundary at or before the LAST MODELED COMPONENT INDEX moves those slots, not only
|
|
359
|
+
the first**, because the shift propagates from it to the end of the component list. That is the one
|
|
360
|
+
structural asymmetry with the repeat-separator code above, where a field is modeled out of its
|
|
361
|
+
first repeat alone and only the first gained boundary reaches a modeled slot. **Two bounds run the
|
|
362
|
+
other way and this fires inside both**, which is over-reporting relative to the named slots and
|
|
363
|
+
never under-reporting, measured rather than assumed: past the last modeled component index a model
|
|
364
|
+
reads a fixed number of components (a patient name three, a Universal Test ID four), so a contested
|
|
365
|
+
boundary further right shifts the components after it while every named slot reads the same under
|
|
366
|
+
both alignments; and inside a LATER repeat nothing modeled moves at all, because `components` is
|
|
367
|
+
the first repeat. Narrowing the sink to either would change which streams a published package
|
|
368
|
+
refuses and wants its own measurement, so both bounds are written down instead.
|
|
369
|
+
|
|
370
|
+
**Additive, and the split is unchanged.** Every decoded byte is identical and the components read
|
|
371
|
+
are the components that were always read: this reports the moved slots, it does not repair them.
|
|
372
|
+
Picking the other alignment was rejected for the same reason as before, on a published package, and
|
|
373
|
+
**withholding the moved slots stays a separate question**: declining to model a slot changes an
|
|
374
|
+
extracted value for every consumer already on the registry.
|
|
375
|
+
|
|
376
|
+
**The tail is weighed one construct deep**, on the same test the two codes above use. Where the
|
|
377
|
+
escape character past the boundary heads a **recognized** sequence the bytes prefer the reading
|
|
378
|
+
taken and the components are the ones the sender wrote (`&F&^&F&GLU` is a field separator escaped,
|
|
379
|
+
a component separator written, and a field separator escaped again, which raises no warning at all,
|
|
380
|
+
and refusing it is the over-refusal that sank a criterion measured for this same family); where it
|
|
381
|
+
heads an **unrecognized** one the competing alignment would leave two escape characters bare, so
|
|
382
|
+
the preference was read as stronger again, and this shipped silent there while the slots moved just
|
|
383
|
+
the same. **That residue is closed later in this same release** (see the tail-bound entry below).
|
|
384
|
+
|
|
385
|
+
**Measured against the corpus constants committed with the test**, on the same
|
|
386
|
+
strict-accepted-under-a-gate-legal-profile tier and the same **864 tuples** as the three
|
|
387
|
+
measurements before it, in `test/records/alignment-shifted-components.test.ts`: as this code
|
|
388
|
+
shipped it fired on **96**, moved **32** from accepted to refused under a profile built from the
|
|
389
|
+
whole tolerable allow-list, moved **0** back, and fired on **none** of the 96 escape-clean tuples.
|
|
390
|
+
**The tail-bound widening below takes the first two to 192 and 64 on the same corpus**, which is
|
|
391
|
+
what the committed test asserts now, and leaves the escape-clean zero where it is. The pair-count
|
|
392
|
+
criterion measured and rejected on this same corpus would have refused 48 of them, which is why the
|
|
393
|
+
tail is what gets weighed. Its column is **disjoint from both** earlier tail reports, asserted on
|
|
394
|
+
the shared corpus rather than reasoned from the wiring: the three partition the splitting roles.
|
|
395
|
+
|
|
396
|
+
**The index axes that corpus holds fixed are swept beside it, because a criterion measured only
|
|
397
|
+
there inherits its blind spot.** Across every component position of the first repeat the reading
|
|
398
|
+
taken reads exactly one component more than the competing alignment, so the harm is not bounded to
|
|
399
|
+
the first boundary. And the identical 864-tuple alphabet re-run with a clean first repeat in front
|
|
400
|
+
of the contested construct gives the same column and the same delta (**96 fire, 32 move, 0 back, 0
|
|
401
|
+
escape-clean**) while on **all 96** the modeled component list is identical under both alignments,
|
|
402
|
+
which states the over-reporting bound as a measurement rather than as prose.
|
|
403
|
+
|
|
404
|
+
**It does not survive a re-emit**, the same residue its siblings disclosed: the serializer rewrites
|
|
405
|
+
the preserved characters into recognized mnemonics, so a second-generation read is silent and is
|
|
406
|
+
correct about its own bytes. Catch it on the first read of the wire bytes.
|
|
407
|
+
|
|
408
|
+
This is a **narrowing on a published package**, on the strict path only: a lenient parse of the
|
|
409
|
+
same stream returns the same records, with the same values, and one more warning on them. New
|
|
410
|
+
public surface: `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`, the `alignmentShiftedComponents`
|
|
411
|
+
warning factory, the `ShiftedComponentsSink` type, and a further trailing optional sink parameter
|
|
412
|
+
on `splitEscapeAware`, `tokenizeRecord` and `tokenizeHeader`.
|
|
413
|
+
|
|
414
|
+
- **The three alignment tail reports now fire on a tail they cannot READ, not only on one they cannot
|
|
415
|
+
CONSUME** (`ASTM-FRAME-RESIDUALS`, defect 17, the disclosed residue). No new code and no rename:
|
|
416
|
+
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` and
|
|
417
|
+
`ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` all keep their names, their claims and their roles. What
|
|
418
|
+
changes is the one predicate behind them: a contested boundary is now reported where the escape
|
|
419
|
+
character the reading taken resumes on heads **no sequence this reader can interpret**, which is
|
|
420
|
+
two cases rather than one.
|
|
421
|
+
- It heads **no sequence at all** and is kept as a bare literal
|
|
422
|
+
(`ASTM_UNPAIRED_ESCAPE_CHARACTER`). Reported before, reported now.
|
|
423
|
+
- It heads a sequence whose body is **not a recognized mnemonic**, so the triple is preserved
|
|
424
|
+
verbatim and never guessed at (`ASTM_UNKNOWN_ESCAPE_SEQUENCE`). **This is what was silent.**
|
|
425
|
+
|
|
426
|
+
**The correction is to the reason, which is why it is worth stating.** The recorded ground for the
|
|
427
|
+
silence was a comparison of preferences: in that case the competing alignment would leave **two**
|
|
428
|
+
escape characters bare while the reading taken carries one unreadable body, so the bytes prefer the
|
|
429
|
+
reading taken more strongly than in the case that already fired. That comparison is true. It is not
|
|
430
|
+
the question these codes ask. They report a **cost**, a modeled slot decided by a boundary the
|
|
431
|
+
bytes do not force, and the cost is the same one: on the canonical set
|
|
432
|
+
`R|1|^^^687|28.6&F&|&Z&U/L||||F` still reads nine fields against the competing alignment's eight
|
|
433
|
+
and still hands back a status of `final` the sender put in no slot; `28.6&S&\&Z&U/L` still reads a
|
|
434
|
+
value of `28.6^`; `&F&^&Z&GLU^L^687` still reads `687` as a vendor local code where the competing
|
|
435
|
+
alignment reads it as the **coding scheme**. Each raised only the tolerable
|
|
436
|
+
`ASTM_UNKNOWN_ESCAPE_SEQUENCE` before, so the widest gate-legal profile plus `{ strict: true }`
|
|
437
|
+
accepted all three. **Consuming a triple is not interpreting one.**
|
|
438
|
+
|
|
439
|
+
**Exactly one tail is still excluded, and it is the one that carries the whole over-refusal
|
|
440
|
+
argument.** Where the escape character heads a sequence this reader **recognizes**, the reading
|
|
441
|
+
taken interprets a construct and nothing is reported: under a set naming the field separator `F`,
|
|
442
|
+
`28.6&F&F&F&U/L` is that separator escaped, written, and escaped again, entirely well formed. That
|
|
443
|
+
is the **only** tail on which a stream's escaping can be clean at all, so it is the only exclusion
|
|
444
|
+
that could ever protect conformant traffic, and refusing it is the over-refusal that sank a
|
|
445
|
+
criterion measured and rejected for this same family. The widened predicate **cannot** refuse an
|
|
446
|
+
escape-clean stream: every position it now rejects already raises an escape deviation of its own.
|
|
447
|
+
|
|
448
|
+
**That remaining silence is a TRADE, and it is not a claim that nothing was lost there.** On the
|
|
449
|
+
excluded tail the gained boundary is exactly as real, and it is not merely under-reported: it is
|
|
450
|
+
`warnings: []`, so a lenient consumer sees nothing at all. On the canonical set,
|
|
451
|
+
`R|1|^^^687|28.6&F&|&F&U/L||||F` reads **nine** fields against the competing alignment's eight and
|
|
452
|
+
hands back a status of **`final`**; `28.6&S&\&S&U/L` reads a value of `28.6^` and `^U/L` leaves
|
|
453
|
+
every modeled slot; `&F&^&F&GLU^L^687` reads one component more, so `687` is a vendor local code
|
|
454
|
+
under one reading and the **coding scheme** under the other. All three are `PRE-EXISTING`, all
|
|
455
|
+
three are now pinned by tests rather than by prose, and **read the raw line when an escape
|
|
456
|
+
character sits next to a delimiter, whether or not anything fired.**
|
|
457
|
+
|
|
458
|
+
**Additive in behaviour terms and still a report, not a repair.** No split changed, every decoded
|
|
459
|
+
byte is identical, and no tolerable code was struck off and no code became tolerable. **What the
|
|
460
|
+
three codes CLAIM about their role did have to move, and that is recorded rather than glossed**:
|
|
461
|
+
see the entry below. It still does not survive a re-emit:
|
|
462
|
+
the serializer rewrites the preserved characters into recognized mnemonics, so a second-generation
|
|
463
|
+
read is silent and correct about its own bytes. Catch it on the first read of the wire bytes.
|
|
464
|
+
|
|
465
|
+
**Measured against the corpus constants committed with the tests**, on the same
|
|
466
|
+
strict-accepted-under-a-gate-legal-profile tier as every measurement in this family. On the shared
|
|
467
|
+
**864-tuple** corpus each of the three codes now fires on **192** where it fired on 96, moves
|
|
468
|
+
**64** from accepted to refused where it moved 32, moves **0** back, and fires on **0** of the 96
|
|
469
|
+
escape-clean tuples exactly as before. The two reported tails contribute equally, so neither half
|
|
470
|
+
of a column carries the other.
|
|
471
|
+
|
|
472
|
+
**The axis that corpus holds fixed is swept beside it**, because a criterion measured only there
|
|
473
|
+
inherits its blind spot: that corpus varies whether the tail is bare, recognized or unrecognized,
|
|
474
|
+
but uses a single character as the unrecognized body. `test/records/alignment-unrecognized-tail.test.ts`
|
|
475
|
+
crosses `DECLARATION_ALPHABET`, `SPLITTING_ROLES` and `BODY_ALPHABET` with the same alphabet as the
|
|
476
|
+
**tail** body: **3,456 tuples**, of which **2,304** fire, **528** move from accepted to refused,
|
|
477
|
+
**0** move back, and **384** are escape-clean with **0** of them refused. Every tuple's observed
|
|
478
|
+
disposition is asserted against a predicate derived from those alphabets rather than against a
|
|
479
|
+
count, and the harness is failed on purpose against a perturbed mnemonic set in both directions.
|
|
480
|
+
|
|
481
|
+
**One universal that sweep refuted, recorded because it is the obvious thing to write.** "The
|
|
482
|
+
reading taken reads exactly one more segment than the competing alignment" is **false**. Where the
|
|
483
|
+
tail sequence's body is the delimiter the split is being taken on, the reading taken keeps that
|
|
484
|
+
character inside an opaque atom while the competing alignment splits on it, so the two readings
|
|
485
|
+
produce the **same** number of segments in **different** places: under `H~\^&`, `28.6&F&~&~&U/L`
|
|
486
|
+
reads `28.6&F&` + `&~&U/L` against the competitor's `28.6&F&~&` + `&U/L`. That is 144 of the 2,304
|
|
487
|
+
firing tuples. What holds on all of them is that the two readings **disagree** and that both
|
|
488
|
+
consume every byte, so neither is forced.
|
|
489
|
+
|
|
490
|
+
This is a **narrowing on a published package**, on the strict path only: a lenient parse of the
|
|
491
|
+
same stream returns the same records, with the same values, and one more warning on them. **No new
|
|
492
|
+
public surface**: no code, type, factory or parameter was added, removed or renamed.
|
|
493
|
+
|
|
494
|
+
- **The three alignment tail reports now state the one class in which nothing moves index**
|
|
495
|
+
(`ASTM-FRAME-RESIDUALS`, defect 17, the claim correction the widening above forced). Widening the
|
|
496
|
+
predicate widened the firing population past the point where each code's own cost claim held
|
|
497
|
+
everywhere, and the claims did not move with it. They do now. Where a surface states the cost, it
|
|
498
|
+
names the exception: the three sink docs in `src/common/escapes.ts`, the three code entries, the
|
|
499
|
+
three factory leads and the three **runtime warning messages** in `src/common/warnings.ts`,
|
|
500
|
+
`src/profiles/safety.ts`, the `referenceCorpus` rationale string, `README.md`,
|
|
501
|
+
`docs-content/quickstart.md`, `docs-content/limitations.md` and
|
|
502
|
+
`docs-content/troubleshooting.md`. Where a surface only paraphrased a cost, the paraphrase was
|
|
503
|
+
**removed** rather than re-qualified, so there is one place to keep true: the `@param` text on
|
|
504
|
+
`splitEscapeAware` in `src/common/escapes.ts` and on `tokenizeRecord` in `src/records/tokenize.ts`
|
|
505
|
+
now defers to the sink docs instead of restating them.
|
|
506
|
+
|
|
507
|
+
**The class, named rather than left to be found.** Where the sequence past the contested boundary
|
|
508
|
+
carries **the splitting delimiter itself** as its body, the reading taken holds that character
|
|
509
|
+
inside an opaque atom while the competing alignment splits on it, so the two readings return the
|
|
510
|
+
**same number** of segments in **different places**. Nothing moves index, and what differs is the
|
|
511
|
+
contents. On the canonical set `R|1|^^^687|28.6&F&|&|&U/L||||F` reads **nine** fields under both,
|
|
512
|
+
with the status `F` in field 9 under both and units of `&|&U/L` against `&U/L`; `28.6&F&\&\&U/L`
|
|
513
|
+
reads **two** repeats under both; `DOE&F&^&^&JANE^A` reads **three** components under both, with
|
|
514
|
+
`A` the middle name under both. So "every later field is one place further right", "the field is
|
|
515
|
+
read as more repeats than the other reading gives it" and "every later component sits one place
|
|
516
|
+
further right" were each false on that class, in `dist/index.d.ts`, in the immutable docs tarball,
|
|
517
|
+
and in three runtime messages.
|
|
518
|
+
|
|
519
|
+
**The class is bounded, and the bound is measured against constants committed with the test, not
|
|
520
|
+
argued.** `test/records/alignment-unrecognized-tail.test.ts` crosses `DECLARATION_ALPHABET`,
|
|
521
|
+
`SPLITTING_ROLES`, `BODY_ALPHABET` and `TAIL_BODY_ALPHABET` with two further constants,
|
|
522
|
+
`PAYLOAD_PREFIXES` and `PAYLOAD_SUFFIXES`, so the carrier shape is not held fixed the way every
|
|
523
|
+
earlier corpus in this family held it: **55,296 tuples**, of which **36,864** fire and **2,304**
|
|
524
|
+
are the class. **0** of those lack `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` and **0** were
|
|
525
|
+
accepted before this release, because that tail body is a splitting delimiter in force. So the
|
|
526
|
+
three codes over-report relative to the indexes there and never under-report, and the class costs
|
|
527
|
+
no stream its disposition. The class is exactly "the tail body is the delimiter being split on",
|
|
528
|
+
with **0** counterexamples in either direction. Every figure is derived from those six constants
|
|
529
|
+
inside its own assertion; **never quote one against a different sha or a different corpus.**
|
|
530
|
+
|
|
531
|
+
**What holds on every firing tuple**, and it is what the claims now lead with: the two readings
|
|
532
|
+
**disagree**, and both **consume every byte**, so neither reading is forced. No code, message
|
|
533
|
+
identity, type, factory or parameter changed, and no stream's disposition moved.
|
|
534
|
+
|
|
279
535
|
### Fixed
|
|
280
536
|
|
|
281
537
|
- **A header that is not short no longer reports that it is too short** (`ASTM-FRAME-RESIDUALS`,
|
|
@@ -599,10 +855,13 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
599
855
|
unchanged, still `test/fixtures/**` and `src/**.ts`, so a staged link outside it is still not
|
|
600
856
|
looked at; narrowing what a scope admits is not widening the scope. That scope also bounds the
|
|
601
857
|
gitlink half: a submodule staged at `test/fixtures/nested` is refused, while one at `src/nested` is
|
|
602
|
-
a directory name that fails the `.ts` suffix and is not looked at.
|
|
603
|
-
still
|
|
858
|
+
a directory name that fails the `.ts` suffix and is not looked at. **`R` (rename) and `C` (copy)
|
|
859
|
+
are still not enumerated by `--staged` at all, so a staged rename that also appends PHI passes that
|
|
604
860
|
route; pre-existing, unchanged here, and admitting it needs the two-path record shape handled,
|
|
605
|
-
which is a scope decision rather than this one
|
|
861
|
+
which is a scope decision rather than this one.** That last sentence was true when it was written
|
|
862
|
+
and is **superseded later in this same release**: the entry below closes it, and it turned out to
|
|
863
|
+
need no two-path record shape at all. It is kept rather than rewritten because the measurement it
|
|
864
|
+
records is what the later entry starts from. A tracked file **absent from the worktree** is
|
|
606
865
|
still caught at `git add` time only.
|
|
607
866
|
|
|
608
867
|
**And this scanner still has no rule that a sweep observing ZERO targets must refuse**, which is
|
|
@@ -627,6 +886,87 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
627
886
|
is not, which makes closing it a separate slice with its own argument rather than a rider on a PHI
|
|
628
887
|
blindness fix. It is a different defect and it fails in the safe direction.
|
|
629
888
|
|
|
889
|
+
- **The PHI scanner's staged route no longer lets git decide what it is safe not to look at, so a
|
|
890
|
+
`git mv` into a fixture directory can no longer carry PHI past the pre-commit gate**
|
|
891
|
+
(`PHI-SCAN-RENAME-BLIND-AT-PRECOMMIT`). Development tooling only: `scripts/phi-scan.ts` ships in no
|
|
892
|
+
tarball and the package's public surface is unchanged.
|
|
893
|
+
|
|
894
|
+
Reproduced on `427af85` before any fix, on git 2.39.5, with the same synthetic name-bearing payload
|
|
895
|
+
the symlink cases use (an ASTM `P` record carrying a patient name, a mother's maiden name and a
|
|
896
|
+
birthdate, plus a dashed SSN and an email at a non-test domain). Four ways the enumeration could be
|
|
897
|
+
emptied before any of the refusals above were reached, each measured as its own throwaway index:
|
|
898
|
+
- **A staged rename.** `git mv test/fixtures/original.astm test/fixtures/renamed.astm` on a file
|
|
899
|
+
carrying the payload paired as a single record with **two** paths and a status letter of `R`, and
|
|
900
|
+
`--diff-filter=AMT` returned nothing for it, so `pnpm phi-scan --staged` printed `OK: no hits`
|
|
901
|
+
and exited **0**. The same move with the payload **edited into the destination in the same
|
|
902
|
+
staging** passed identically, because git still pairs it when enough of the old content survives.
|
|
903
|
+
No similarity score is quoted here: it moves with how much survives, so a number taken from one
|
|
904
|
+
fixture is wrong in the next, and what is load-bearing is that the record was paired at all.
|
|
905
|
+
- **A staged rename of a symbolic link.** `git mv` of a tracked link into `test/fixtures/` put a
|
|
906
|
+
mode-`120000` entry under a scan root. The mode refusal added by the previous entry already knew
|
|
907
|
+
how to refuse that; the pairing deleted the record before the mode could be read, and the run
|
|
908
|
+
exited **0**.
|
|
909
|
+
- **A gitlink the caller's config hid.** `diff.ignoreSubmodules = all` in the caller's git config
|
|
910
|
+
dropped a staged gitlink under `test/fixtures/` from the raw output entirely. The **same index**
|
|
911
|
+
refused at exit 2 without that config and reported `OK: no hits` at exit **0** with it.
|
|
912
|
+
- **An unmerged path.** An in-scope path recorded unmerged, with the payload on one side, was
|
|
913
|
+
returned by neither `AM` nor `AMT`, and the run printed `OK: no hits` and exited **0** over an
|
|
914
|
+
index it could not read: such a path is recorded at one or more of stages 1/2/3 and never at
|
|
915
|
+
stage 0, and stage 0 is exactly what the `:<path>` form this route reads with names, so there is
|
|
916
|
+
no one blob for it to read. **Nothing rests on what `git show` does with such a path**: the route
|
|
917
|
+
refuses before it would call it, and an earlier draft that pinned that exit code did not
|
|
918
|
+
reproduce across git versions.
|
|
919
|
+
|
|
920
|
+
**The remedy is entirely in the argv, and it is one rule: stop trusting the caller's git config,
|
|
921
|
+
and stop letting the filter decide what is safe not to look at.** The route now reads
|
|
922
|
+
`git diff --cached --raw -z --no-renames --ignore-submodules=none --diff-filter=AMTUB`.
|
|
923
|
+
`--no-renames` makes a paired record impossible, so the destination arrives as an ordinary
|
|
924
|
+
single-path `A` and the source as a `D` the filter drops; verified under `diff.renames` at
|
|
925
|
+
`true`, `copies` and `false` and under `diff.renameLimit=1`, every one yielding the same
|
|
926
|
+
single-path record, so the two-field record stride is now **structural** rather than conditional on
|
|
927
|
+
the caller's config. `--ignore-submodules=none` restores a record the config could delete.
|
|
928
|
+
`U` is in the filter **so it can be refused, not scanned**, with its own message: an unmerged
|
|
929
|
+
record's destination mode is `000000`, so the existing refusal would have described it with a
|
|
930
|
+
sentence about symbolic links and gitlinks that is false for it. That case builds the unmerged
|
|
931
|
+
index directly, with `git update-index --index-info`, rather than provoking it with a real merge:
|
|
932
|
+
whether a merge conflicts depends on the merge machinery and on the caller having a git identity,
|
|
933
|
+
and the index state is the thing under test. Staged as a real conflict it did not reproduce on
|
|
934
|
+
every git version. None of the four costs the record
|
|
935
|
+
stride anything, because each carries a single path.
|
|
936
|
+
|
|
937
|
+
**Be exact about what that changes.** The new enumeration is a **superset** of the old one, not a
|
|
938
|
+
strictly larger set: the two are **equal** whenever nothing is renamed, copied, unmerged or hidden
|
|
939
|
+
by that config, and larger only when something is. Nothing the old argv enumerated stops being
|
|
940
|
+
enumerated. The `--staged` path scope is **unchanged**, still `test/fixtures/**` and `src/**.ts`, so
|
|
941
|
+
an unmerged or renamed path outside it is still not looked at, and both of those are pinned.
|
|
942
|
+
|
|
943
|
+
**`B` is in the filter because `-B` is not inert, and the mechanism is sharper than "a `B` record
|
|
944
|
+
the filter drops".** A complete rewrite under `-B` prints a **single-path record whose status
|
|
945
|
+
letter is `M`**, with a break score, which the record regex parses happily, so a reader checking
|
|
946
|
+
the raw output concludes the record is an `M` and that `AMTU` therefore keeps it. It does not:
|
|
947
|
+
`--diff-filter` classifies a broken pair as `B` **whatever letter it prints**. Measured on one
|
|
948
|
+
index, over a wholly rewritten in-scope fixture carrying a dashed SSN: `-B --diff-filter=AMTU`
|
|
949
|
+
returns empty while `-B --diff-filter=B` and `-B --diff-filter=AMTUB` each return the same record,
|
|
950
|
+
and a copy of the scanner with `-B` injected exits **0** with `OK: no hits` on the superseded
|
|
951
|
+
filter and **1** with the hit on the shipped one. `B` costs the enumeration nothing today, since
|
|
952
|
+
git only breaks a pair when `-B` is given, so it is there to stop the flag ever being a silent
|
|
953
|
+
blindfold. `-M`, `-C` and `--find-copies-harder` each turn detection back on over the top of
|
|
954
|
+
`--no-renames` and empty the route again, which is measured beside it; none of the four may be
|
|
955
|
+
added.
|
|
956
|
+
|
|
957
|
+
**6 of the file's 41 cases are red on `427af85`**, and the premises each rests on are pinned beside
|
|
958
|
+
it, so a case cannot quietly stop testing anything. Refusal messages still name only the entry's own
|
|
959
|
+
repo-relative path and an engine-owned reason, never a link target or any stream content, and that
|
|
960
|
+
is asserted on the new refusal too.
|
|
961
|
+
|
|
962
|
+
**What this does not cover, stated narrowly.** The all-mode walk is untouched: it has no concept of
|
|
963
|
+
a rename, and nothing here widens or narrows what it enumerates. The scan roots' own paths are still
|
|
964
|
+
outside the `--staged` scope, so a staged blob, link or gitlink at exactly `test/fixtures`, `test`
|
|
965
|
+
or `src` is still not looked at by that route. Neither route reaches `test/**/*.test.ts`, where this
|
|
966
|
+
package keeps most of its inline fixtures, so those are still read by hand. And this scanner still
|
|
967
|
+
has no rule that a sweep observing zero targets must refuse, exactly as recorded above. Each of
|
|
968
|
+
those is a scope decision with its own argument, not a consequence of anything here.
|
|
969
|
+
|
|
630
970
|
- **The frame encoder no longer writes a different character than the one it was handed**
|
|
631
971
|
(`ASTM-FRAME-BYTE-RESIDUALS`, closing known defect 7). `composeAstmFrames` turned a record given
|
|
632
972
|
as a `string` into bytes with `charCodeAt(i) & 0xff`, so any character above `U+00FF` was replaced
|
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
<
|
|
2
|
-
<
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
<a href="https://cosyte.com">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
|
|
4
|
+
<img alt="Cosyte: a plus mark set in two overlapping rounded squares, one solid and one outlined, beside the Cosyte wordmark" src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
|
|
5
|
+
</picture>
|
|
6
|
+
</a>
|
|
5
7
|
|
|
6
8
|
# @cosyte/astm
|
|
7
9
|
|
|
@@ -222,9 +224,12 @@ mirror: catch it on the first read.
|
|
|
222
224
|
|
|
223
225
|
**And where that gained boundary is a FIELD boundary, it moves a result's status, which is a
|
|
224
226
|
different question and so a different code.** The two alignments resume one character apart, so they
|
|
225
|
-
disagree about
|
|
226
|
-
|
|
227
|
-
|
|
227
|
+
disagree about the bytes after the boundary, not only about it (they can resync later, and the
|
|
228
|
+
class where they do is named below). Where the escape character the
|
|
229
|
+
reading taken resumes on heads no sequence this reader can _interpret_ (none at all, or one whose
|
|
230
|
+
body is not a recognized mnemonic and is therefore preserved verbatim rather than read), that
|
|
231
|
+
reading bought the boundary with bytes it cannot read while the competing alignment is the one that
|
|
232
|
+
can use them. On the field separator that
|
|
228
233
|
matters clinically: `R|1|^^^687|28.6&F&|&U/L||||F` reads **nine** fields under the alignment taken
|
|
229
234
|
and **eight** under the other, so the sender's trailing `F` lands in field 9, the result status,
|
|
230
235
|
under the first and in no field at all under the second. The parse hands back units of `&U/L` and a
|
|
@@ -235,9 +240,24 @@ tolerate**; before it existed the only warning on that stream was the tolerable
|
|
|
235
240
|
unchanged: it reports the shift rather than repairing it. It is wired to the **field** separator
|
|
236
241
|
only, because a gained repeat or component boundary divides one field and so moves no field-indexed
|
|
237
242
|
slot. **That bound is a choice, not a consequence**: components are modeled inside a field, so a
|
|
238
|
-
gained repeat or component boundary does reach a modeled slot
|
|
239
|
-
|
|
240
|
-
|
|
243
|
+
gained repeat or component boundary does reach a modeled slot, and each of those two has a code of
|
|
244
|
+
its own below. It stays silent in exactly one case,
|
|
245
|
+
where the trailing escape character heads a sequence this reader RECOGNIZES: that is the escape
|
|
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
|
|
248
|
+
and not a claim that nothing was lost there**: the gained field boundary is exactly as real, and on
|
|
249
|
+
that tail it is `warnings: []`, so `R|1|^^^687|28.6&F&|&F&U/L||||F` reads nine fields against the
|
|
250
|
+
competing alignment's eight and hands back a status of `final` with nothing reported at all. Read
|
|
251
|
+
the raw line when an escape character sits next to a delimiter, whether or not anything fired.
|
|
252
|
+
**The shift has one measured exception, and it is named rather than left to be found**: where the
|
|
253
|
+
sequence past the boundary carries the field separator itself as its body, the reading taken holds
|
|
254
|
+
that character inside an opaque atom while the competing alignment splits on it, so the two readings
|
|
255
|
+
read the **same number** of fields in **different places**. `R|1|^^^687|28.6&F&|&|&U/L||||F` reads
|
|
256
|
+
nine fields under both, with the status `F` in field 9 under both, and what differs is the units.
|
|
257
|
+
The code still fires there, which is over-reporting relative to the field indexes and never
|
|
258
|
+
under-reporting, and that class costs no stream its disposition, because
|
|
259
|
+
`ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` has already refused it. It does not survive a re-emit
|
|
260
|
+
either: catch it on the first read.
|
|
241
261
|
|
|
242
262
|
**And where that gained boundary is a REPEAT boundary, nothing shifts and the field can still be
|
|
243
263
|
read short.** What the report says is that the field is read as more repeats than the competing
|
|
@@ -253,12 +273,44 @@ legal profile accepted a truncated value and an emptied test identity. The readi
|
|
|
253
273
|
reports the gained boundary rather than repairing it. **At a LATER boundary it fires and no modeled
|
|
254
274
|
slot moves**, because the first repeat is then the same under both readings; that is over-reporting
|
|
255
275
|
relative to the modeled slots and never under-reporting, and it is measured rather than assumed. Its
|
|
256
|
-
tail bound is the shift report's, for the same reason
|
|
257
|
-
escaped, written, and escaped again, and refusing it would
|
|
258
|
-
**
|
|
259
|
-
|
|
260
|
-
as the
|
|
261
|
-
|
|
276
|
+
tail bound is the shift report's, for the same reason, and so is its one exclusion:
|
|
277
|
+
`28.6&R&\&R&U/L` is the repeat separator escaped, written, and escaped again, and refusing it would
|
|
278
|
+
refuse a well-formed stream. **That silence is a trade, not a claim that nothing was lost**:
|
|
279
|
+
`28.6&S&\&S&U/L` still reads a value of `28.6^` with `warnings: []`. **The truncation has the same
|
|
280
|
+
one exception as the shift**: where the sequence past the boundary carries the repeat separator
|
|
281
|
+
itself, the two readings read the same number of repeats in different places, so the field is not
|
|
282
|
+
read as more repeats at all, and that class was already refused by
|
|
283
|
+
`ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE`. It is wired to the **repeat** separator only; a gained
|
|
284
|
+
**component** boundary reaches a modeled slot differently, and has its own code below. It does not
|
|
285
|
+
survive a re-emit either: catch it on the first read.
|
|
286
|
+
|
|
287
|
+
**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,
|
|
290
|
+
`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
|
+
scheme and `687` the vendor's local code; the competing alignment reads three, and `687` is the
|
|
292
|
+
**coding scheme**. A code-system selector and a vendor's local code are not the same thing.
|
|
293
|
+
`P|1||MRN-0001||DOE&F&^&JANE^A||19700101|F` reads a given name of `&JANE` and a middle name of `A`,
|
|
294
|
+
where the competing alignment makes `A` the **given** name with no middle name at all. That raises
|
|
295
|
+
`ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`, which **no profile may tolerate**; before it existed the
|
|
296
|
+
only warning on either stream was the tolerable `ASTM_UNPAIRED_ESCAPE_CHARACTER`, so a strict parse
|
|
297
|
+
under a legal profile accepted both. The reading is unchanged: it reports the moved slots rather than
|
|
298
|
+
repairing them. **Every gained boundary at or before the last modeled component index moves them,
|
|
299
|
+
not only the first**, because the shift propagates to the end of the component list, which is where
|
|
300
|
+
this differs from the repeat role. **Two bounds run the other way and it fires inside both**,
|
|
301
|
+
over-reporting and never under: past the last modeled index nothing named moves (a name models three
|
|
302
|
+
components, a Universal Test ID four), and inside a later repeat nothing modeled moves at all. Its
|
|
303
|
+
tail bound is the other two reports', for the same reason, and so is its one exclusion, **and that
|
|
304
|
+
silence is a trade rather than a claim that nothing was lost**: `&F&^&F&GLU^L^687` still reads one
|
|
305
|
+
component more than the competing alignment, with `warnings: []`. **A THIRD bound runs the other way
|
|
306
|
+
here, and unlike the two above it is about the bytes past the boundary rather than where the
|
|
307
|
+
boundary sits**: where the sequence past it carries the component separator itself, the two readings
|
|
308
|
+
read the same number of components in different places, so `DOE&F&^&^&JANE^A` reads three components
|
|
309
|
+
under both with `A` the middle name under both, and that class was already refused by
|
|
310
|
+
`ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE`. What holds wherever any of the three codes fires is
|
|
311
|
+
that the two readings disagree and that both consume every byte, so neither is forced. It does not
|
|
312
|
+
survive a re-emit either: catch it on the first read. With this the three splitting roles are all
|
|
313
|
+
wired, and there is no fourth: nothing splits on the escape role.
|
|
262
314
|
|
|
263
315
|
### A header that names one character in two delimiter roles
|
|
264
316
|
|
|
@@ -355,12 +407,17 @@ outside it:
|
|
|
355
407
|
where the leftmost alignment lets a delimiter split that a competing alignment would have held,
|
|
356
408
|
gains a boundary instead of losing one and raises `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`, also
|
|
357
409
|
not tolerable. Where that gained boundary is a **field** boundary and the reading taken resumes on
|
|
358
|
-
an escape character heading no sequence, every later field shifts one place and a
|
|
410
|
+
an escape character heading no sequence it can interpret, every later field shifts one place and a
|
|
411
|
+
result's units
|
|
359
412
|
and status are read out of slots the other alignment does not put them in: that raises
|
|
360
413
|
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, not tolerable either. Where it is a **repeat** boundary
|
|
361
414
|
nothing shifts, but the field is read out of its first repeat alone, so a gained first boundary
|
|
362
415
|
truncates a value and costs a test identity or a patient name the components that sat after it:
|
|
363
|
-
that raises `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD`, not tolerable either.
|
|
416
|
+
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
|
|
419
|
+
alignment does not put it in: that raises `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`, not
|
|
420
|
+
tolerable either.
|
|
364
421
|
|
|
365
422
|
All are accepted limits, for two different reasons: widening the field-separator check would mean
|
|
366
423
|
deciding which set a record ought to have had, which is the same guess the parser declines to make
|