@cosyte/astm 0.0.17 → 0.0.19
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 +380 -11
- package/README.md +93 -15
- package/dist/index.cjs +304 -40
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +721 -91
- package/dist/index.d.ts +721 -91
- package/dist/index.mjs +303 -41
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -136,7 +136,8 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
136
136
|
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS` fires where two escape alignments disagree about a **field**
|
|
137
137
|
boundary, the reading taken keeps it, and the escape character that reading resumes on heads **no
|
|
138
138
|
escape sequence at all**, which is exactly the character the competing alignment uses to close its
|
|
139
|
-
own sequence.
|
|
139
|
+
own sequence. (**That tail bound is widened later in this same release**, to any tail this reader
|
|
140
|
+
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
141
|
`{ strict: true }` refuses such a record whatever profile is in force.
|
|
141
142
|
|
|
142
143
|
**What the shift costs, measured on the canonical set.** `R|1|^^^687|28.6&F&|&U/L||||F` reads
|
|
@@ -173,23 +174,27 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
173
174
|
profile, and both are **`PRE-EXISTING`** (byte-identical on the base of this slice), so they are
|
|
174
175
|
disclosed here as a separate open condition rather than covered: wiring this sink to another
|
|
175
176
|
delimiter role is a different criterion and wants its own population measurement. On the repeat role
|
|
176
|
-
what is lost is the value,
|
|
177
|
+
what is lost is the value, not reported by this code and now reported by its own, below. And the tail is weighed **one construct deep**: where
|
|
177
178
|
the escape character past the boundary heads a **recognized** sequence, the alignment taken
|
|
178
179
|
interprets it while the competing one would leave it bare, so the bytes prefer the alignment taken
|
|
179
180
|
(under a set naming the field separator `F`, `28.6&F&F&F&U/L` is that separator escaped, written and
|
|
180
181
|
escaped again, entirely well formed, and refusing it is the over-refusal that sank a criterion
|
|
181
182
|
measured for this same family); and where it heads an **unrecognized** sequence, the alignment taken
|
|
182
183
|
still consumes it while the competing one would leave two escape characters bare, so the preference
|
|
183
|
-
|
|
184
|
-
residue
|
|
184
|
+
was read as stronger again, and this shipped silent there while the fields shifted just the same.
|
|
185
|
+
**That residue is closed later in this same release** (see the tail-bound entry below), which
|
|
186
|
+
corrects the reason rather than the code: the preference comparison is true and is not the question
|
|
187
|
+
this code asks.
|
|
185
188
|
|
|
186
189
|
**Measured against the corpus constants committed with the test**, on the same
|
|
187
190
|
strict-accepted-under-a-gate-legal-profile tier. Over `DECLARATION_ALPHABET`, `SPLITTING_ROLES`,
|
|
188
191
|
`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
|
-
|
|
192
|
+
tuples**: as this code shipped it fired on **96**, moved **32** from accepted to refused under a
|
|
193
|
+
profile built from the whole tolerable allow-list, and moved **0** back. **The tail-bound widening
|
|
194
|
+
below takes those to 192 and 64 on the same corpus**, which is what the committed test asserts now.
|
|
195
|
+
It fires on **none** of the 96 escape-clean tuples, and cannot, before or after: firing requires an
|
|
196
|
+
escape character heading no sequence this reader can interpret, and this package already reports
|
|
197
|
+
each of those as a deviation of its own. The criterion measured and rejected on this same corpus
|
|
193
198
|
would have refused 48 of them. The 32 that move are exactly the population the code above left
|
|
194
199
|
silent through its recognized-body exclusion.
|
|
195
200
|
|
|
@@ -204,6 +209,286 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
204
209
|
`ShiftedFieldsSink` type, and a trailing optional sink parameter on `splitEscapeAware`,
|
|
205
210
|
`tokenizeRecord` and `tokenizeHeader`.
|
|
206
211
|
|
|
212
|
+
- **A parse-path warning code for a field a competing escape alignment read short**
|
|
213
|
+
(`ASTM-FRAME-RESIDUALS`, defect 17b). `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` fires where two
|
|
214
|
+
escape alignments of the same bytes disagree about a **repeat** boundary, the reading taken keeps
|
|
215
|
+
it, and the escape character that reading resumes on heads **no sequence at all**. (**That tail
|
|
216
|
+
bound is widened later in this same release**; see the entry below.) It lands on the profile safety
|
|
217
|
+
gate, so `{ strict: true }` refuses a stream carrying it whatever profile is in force.
|
|
218
|
+
|
|
219
|
+
**Nothing shifts, and that is not the same as nothing being lost, which is the correction this
|
|
220
|
+
entry carries.** No field-indexed slot moves: the units slot and the result-status slot are read
|
|
221
|
+
out of the same field numbers under either alignment, which is exactly why the shift report above
|
|
222
|
+
cannot see this. What it reports is that **the field is read as more repeats than the competing
|
|
223
|
+
alignment gives it**, which is true wherever it fires. **Where that gained boundary is the FIRST
|
|
224
|
+
one in the field it reaches a modeled slot**, because a field's modeled value and its components
|
|
225
|
+
are taken from its first repeat alone: everything past the boundary stays on the wire, stays in
|
|
226
|
+
`repeats`, and leaves every modeled slot. Both costs are reachable on the **canonical** set, so no
|
|
227
|
+
unusual declaration is needed for either:
|
|
228
|
+
- **A value truncates.** `R|1|^^^687|28.6&S&\&U/L|U/L||||F` reads a value of `28.6^`, and `&U/L`
|
|
229
|
+
leaves the result entirely; the competing alignment reads one repeat carrying all of it.
|
|
230
|
+
- **A modeled identity empties, which is the half the shift report could not reach.**
|
|
231
|
+
`R|1|&F&\&687|28.6|U/L||||F` reads a Universal Test ID of one component holding a decoded
|
|
232
|
+
field separator, so the local code `687` is in no modeled slot at all and the identity comes back
|
|
233
|
+
as a LOINC candidate nobody wrote. `DOE&S&\&JANE^A` reads a last name and **no given or middle
|
|
234
|
+
name**. Before this code the only warnings on those streams were tolerable ones, so the widest
|
|
235
|
+
gate-legal profile plus `{ strict: true }` accepted a truncated value and an emptied test
|
|
236
|
+
identity.
|
|
237
|
+
|
|
238
|
+
**At a LATER boundary it fires and no modeled slot moves**, because the first repeat is then
|
|
239
|
+
identical under both readings, and what differs is the repeat structure after it. That is
|
|
240
|
+
**over-reporting relative to the modeled slots and never under-reporting**, it is measured on its
|
|
241
|
+
own sweep beside the main corpus rather than assumed, and it is stated on the code itself.
|
|
242
|
+
Narrowing the sink to the first boundary would change which streams a published package refuses
|
|
243
|
+
and wants its own measurement, so the bound is written down instead.
|
|
244
|
+
|
|
245
|
+
**Additive, and the split is unchanged.** Every decoded byte is identical, `repeats` still carries
|
|
246
|
+
every one of them, and the value read is the value that was always read: this reports the gained
|
|
247
|
+
boundary, it does not repair it. Picking the other alignment was rejected for the same reason as
|
|
248
|
+
before, on a published package.
|
|
249
|
+
|
|
250
|
+
**Two further bounds, both deliberate.** It is wired to the **repeat** separator only: a gained
|
|
251
|
+
**component** boundary reaches a modeled slot too, and differently, moving it one slot along rather
|
|
252
|
+
than dropping it, which is the entry below. And the tail is
|
|
253
|
+
weighed **one construct deep**, on the same test the shift report uses: where the escape character
|
|
254
|
+
past the boundary heads a **recognized** sequence the bytes prefer the reading taken and the two
|
|
255
|
+
repeats are the ones the sender wrote (`28.6&R&\&R&U/L` is the canonical repeat separator
|
|
256
|
+
escaped, written and escaped again, and refusing it is the over-refusal that sank a criterion
|
|
257
|
+
measured for this same family), and where it heads an **unrecognized** one the competing alignment
|
|
258
|
+
would leave two escape characters bare, so the preference was read as stronger again, and this
|
|
259
|
+
shipped silent there while the field truncated just the same. **That residue is closed later in
|
|
260
|
+
this same release** (see the tail-bound entry below).
|
|
261
|
+
|
|
262
|
+
**Measured against the corpus constants committed with the test**, on the same
|
|
263
|
+
strict-accepted-under-a-gate-legal-profile tier and the same **864 tuples** as the two measurements
|
|
264
|
+
before it, in `test/records/alignment-truncated-field.test.ts`: as this code shipped it fired on
|
|
265
|
+
**96**, moved **32** from accepted to refused under a profile built from the whole tolerable
|
|
266
|
+
allow-list, moved **0** back, and fired on **none** of the 96 escape-clean tuples. **The
|
|
267
|
+
tail-bound widening below takes the first two to 192 and 64 on the same corpus**, which is what the
|
|
268
|
+
committed test asserts now, and leaves the escape-clean zero where it is. The pair-count criterion measured
|
|
269
|
+
and rejected on this same corpus would have refused 48 of them, which is why the tail is what gets
|
|
270
|
+
weighed. **Unlike that rejected criterion's population, this one is not confined to a set naming a
|
|
271
|
+
mnemonic letter as a delimiter**: the canonical repeat separator reaches it, on 12 of the 108
|
|
272
|
+
canonical tuples swept. A second sweep beside it moves the contested boundary off the front of the
|
|
273
|
+
field and measures the same column: **96 fire, 32 move, 0 back, 0 escape-clean**, and on every one
|
|
274
|
+
of the 96 the first repeat is identical under both alignments, which is the bound on the claim
|
|
275
|
+
stated as a measurement rather than as prose.
|
|
276
|
+
|
|
277
|
+
**It does not survive a re-emit**, the same residue its siblings disclosed: the serializer rewrites
|
|
278
|
+
the preserved characters into recognized mnemonics, so a second-generation read is silent and is
|
|
279
|
+
correct about its own bytes. Catch it on the first read of the wire bytes.
|
|
280
|
+
|
|
281
|
+
This is a **narrowing on a published package**, on the strict path only: a lenient parse of the
|
|
282
|
+
same stream returns the same records, with the same values, and one more warning on them. New
|
|
283
|
+
public surface: `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD`, the `alignmentTruncatedField` warning
|
|
284
|
+
factory, the `TruncatedFieldSink` type, and a further trailing optional sink parameter on
|
|
285
|
+
`splitEscapeAware`, `tokenizeRecord` and `tokenizeHeader`.
|
|
286
|
+
|
|
287
|
+
- **A parse-path warning code for components a competing escape alignment moved one slot along**
|
|
288
|
+
(`ASTM-FRAME-RESIDUALS`, defect 17c). `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` fires where two
|
|
289
|
+
escape alignments of the same bytes disagree about a **component** boundary, the reading taken
|
|
290
|
+
keeps it, and the escape character that reading resumes on heads **no sequence at all**. (**That
|
|
291
|
+
tail bound is widened later in this same release**; see the entry below.) It lands on the profile
|
|
292
|
+
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
|
|
293
|
+
nothing splits on the escape role.
|
|
294
|
+
|
|
295
|
+
**Nothing shifts between fields and nothing leaves the record: the slots MOVE.** That is the third
|
|
296
|
+
answer to the one question the two entries above ask, and it is why neither of them could carry it.
|
|
297
|
+
Components are modeled **inside** a field, so every component after the gained boundary sits one
|
|
298
|
+
place further right than the competing alignment puts it, and the slots that indexes into are named
|
|
299
|
+
things. Both costs are reachable on the **canonical** set, so no unusual declaration is needed:
|
|
300
|
+
- **A code system and a vendor's local code swap places.** `R|1|&F&^&GLU^L^687|28.6|U/L||||F`
|
|
301
|
+
reads a Universal Test ID of four components, so `L` is the coding scheme and `687` the local
|
|
302
|
+
code; the competing alignment reads three, and `687` is the **coding scheme**. A code-system
|
|
303
|
+
selector and a vendor's local code are not the same thing, so a consumer routing on either is
|
|
304
|
+
routing on the alignment.
|
|
305
|
+
- **A given name and a middle name shift along.** `P|1||MRN-0001||DOE&F&^&JANE^A||19700101|F`
|
|
306
|
+
reads a given name of `&JANE` and a middle name of `A`; under the competing alignment `A` is the
|
|
307
|
+
**given** name and there is no middle name at all.
|
|
308
|
+
|
|
309
|
+
Before this code the only warning on either stream was the tolerable
|
|
310
|
+
`ASTM_UNPAIRED_ESCAPE_CHARACTER`, so the widest gate-legal profile plus `{ strict: true }` accepted
|
|
311
|
+
both. **This closes a claim that had been recorded as false and shipped**: "a gained repeat or
|
|
312
|
+
component boundary divides one field and reaches nothing outside it, so it cannot move a modeled
|
|
313
|
+
slot". The first clause is true and the inference is not.
|
|
314
|
+
|
|
315
|
+
**Every gained boundary at or before the LAST MODELED COMPONENT INDEX moves those slots, not only
|
|
316
|
+
the first**, because the shift propagates from it to the end of the component list. That is the one
|
|
317
|
+
structural asymmetry with the repeat-separator code above, where a field is modeled out of its
|
|
318
|
+
first repeat alone and only the first gained boundary reaches a modeled slot. **Two bounds run the
|
|
319
|
+
other way and this fires inside both**, which is over-reporting relative to the named slots and
|
|
320
|
+
never under-reporting, measured rather than assumed: past the last modeled component index a model
|
|
321
|
+
reads a fixed number of components (a patient name three, a Universal Test ID four), so a contested
|
|
322
|
+
boundary further right shifts the components after it while every named slot reads the same under
|
|
323
|
+
both alignments; and inside a LATER repeat nothing modeled moves at all, because `components` is
|
|
324
|
+
the first repeat. Narrowing the sink to either would change which streams a published package
|
|
325
|
+
refuses and wants its own measurement, so both bounds are written down instead.
|
|
326
|
+
|
|
327
|
+
**Additive, and the split is unchanged.** Every decoded byte is identical and the components read
|
|
328
|
+
are the components that were always read: this reports the moved slots, it does not repair them.
|
|
329
|
+
Picking the other alignment was rejected for the same reason as before, on a published package, and
|
|
330
|
+
**withholding the moved slots stays a separate question**: declining to model a slot changes an
|
|
331
|
+
extracted value for every consumer already on the registry.
|
|
332
|
+
|
|
333
|
+
**The tail is weighed one construct deep**, on the same test the two codes above use. Where the
|
|
334
|
+
escape character past the boundary heads a **recognized** sequence the bytes prefer the reading
|
|
335
|
+
taken and the components are the ones the sender wrote (`&F&^&F&GLU` is a field separator escaped,
|
|
336
|
+
a component separator written, and a field separator escaped again, which raises no warning at all,
|
|
337
|
+
and refusing it is the over-refusal that sank a criterion measured for this same family); where it
|
|
338
|
+
heads an **unrecognized** one the competing alignment would leave two escape characters bare, so
|
|
339
|
+
the preference was read as stronger again, and this shipped silent there while the slots moved just
|
|
340
|
+
the same. **That residue is closed later in this same release** (see the tail-bound entry below).
|
|
341
|
+
|
|
342
|
+
**Measured against the corpus constants committed with the test**, on the same
|
|
343
|
+
strict-accepted-under-a-gate-legal-profile tier and the same **864 tuples** as the three
|
|
344
|
+
measurements before it, in `test/records/alignment-shifted-components.test.ts`: as this code
|
|
345
|
+
shipped it fired on **96**, moved **32** from accepted to refused under a profile built from the
|
|
346
|
+
whole tolerable allow-list, moved **0** back, and fired on **none** of the 96 escape-clean tuples.
|
|
347
|
+
**The tail-bound widening below takes the first two to 192 and 64 on the same corpus**, which is
|
|
348
|
+
what the committed test asserts now, and leaves the escape-clean zero where it is. The pair-count
|
|
349
|
+
criterion measured and rejected on this same corpus would have refused 48 of them, which is why the
|
|
350
|
+
tail is what gets weighed. Its column is **disjoint from both** earlier tail reports, asserted on
|
|
351
|
+
the shared corpus rather than reasoned from the wiring: the three partition the splitting roles.
|
|
352
|
+
|
|
353
|
+
**The index axes that corpus holds fixed are swept beside it, because a criterion measured only
|
|
354
|
+
there inherits its blind spot.** Across every component position of the first repeat the reading
|
|
355
|
+
taken reads exactly one component more than the competing alignment, so the harm is not bounded to
|
|
356
|
+
the first boundary. And the identical 864-tuple alphabet re-run with a clean first repeat in front
|
|
357
|
+
of the contested construct gives the same column and the same delta (**96 fire, 32 move, 0 back, 0
|
|
358
|
+
escape-clean**) while on **all 96** the modeled component list is identical under both alignments,
|
|
359
|
+
which states the over-reporting bound as a measurement rather than as prose.
|
|
360
|
+
|
|
361
|
+
**It does not survive a re-emit**, the same residue its siblings disclosed: the serializer rewrites
|
|
362
|
+
the preserved characters into recognized mnemonics, so a second-generation read is silent and is
|
|
363
|
+
correct about its own bytes. Catch it on the first read of the wire bytes.
|
|
364
|
+
|
|
365
|
+
This is a **narrowing on a published package**, on the strict path only: a lenient parse of the
|
|
366
|
+
same stream returns the same records, with the same values, and one more warning on them. New
|
|
367
|
+
public surface: `ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS`, the `alignmentShiftedComponents`
|
|
368
|
+
warning factory, the `ShiftedComponentsSink` type, and a further trailing optional sink parameter
|
|
369
|
+
on `splitEscapeAware`, `tokenizeRecord` and `tokenizeHeader`.
|
|
370
|
+
|
|
371
|
+
- **The three alignment tail reports now fire on a tail they cannot READ, not only on one they cannot
|
|
372
|
+
CONSUME** (`ASTM-FRAME-RESIDUALS`, defect 17, the disclosed residue). No new code and no rename:
|
|
373
|
+
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD` and
|
|
374
|
+
`ASTM_RECORD_ALIGNMENT_SHIFTED_COMPONENTS` all keep their names, their claims and their roles. What
|
|
375
|
+
changes is the one predicate behind them: a contested boundary is now reported where the escape
|
|
376
|
+
character the reading taken resumes on heads **no sequence this reader can interpret**, which is
|
|
377
|
+
two cases rather than one.
|
|
378
|
+
- It heads **no sequence at all** and is kept as a bare literal
|
|
379
|
+
(`ASTM_UNPAIRED_ESCAPE_CHARACTER`). Reported before, reported now.
|
|
380
|
+
- It heads a sequence whose body is **not a recognized mnemonic**, so the triple is preserved
|
|
381
|
+
verbatim and never guessed at (`ASTM_UNKNOWN_ESCAPE_SEQUENCE`). **This is what was silent.**
|
|
382
|
+
|
|
383
|
+
**The correction is to the reason, which is why it is worth stating.** The recorded ground for the
|
|
384
|
+
silence was a comparison of preferences: in that case the competing alignment would leave **two**
|
|
385
|
+
escape characters bare while the reading taken carries one unreadable body, so the bytes prefer the
|
|
386
|
+
reading taken more strongly than in the case that already fired. That comparison is true. It is not
|
|
387
|
+
the question these codes ask. They report a **cost**, a modeled slot decided by a boundary the
|
|
388
|
+
bytes do not force, and the cost is the same one: on the canonical set
|
|
389
|
+
`R|1|^^^687|28.6&F&|&Z&U/L||||F` still reads nine fields against the competing alignment's eight
|
|
390
|
+
and still hands back a status of `final` the sender put in no slot; `28.6&S&\&Z&U/L` still reads a
|
|
391
|
+
value of `28.6^`; `&F&^&Z&GLU^L^687` still reads `687` as a vendor local code where the competing
|
|
392
|
+
alignment reads it as the **coding scheme**. Each raised only the tolerable
|
|
393
|
+
`ASTM_UNKNOWN_ESCAPE_SEQUENCE` before, so the widest gate-legal profile plus `{ strict: true }`
|
|
394
|
+
accepted all three. **Consuming a triple is not interpreting one.**
|
|
395
|
+
|
|
396
|
+
**Exactly one tail is still excluded, and it is the one that carries the whole over-refusal
|
|
397
|
+
argument.** Where the escape character heads a sequence this reader **recognizes**, the reading
|
|
398
|
+
taken interprets a construct and nothing is reported: under a set naming the field separator `F`,
|
|
399
|
+
`28.6&F&F&F&U/L` is that separator escaped, written, and escaped again, entirely well formed. That
|
|
400
|
+
is the **only** tail on which a stream's escaping can be clean at all, so it is the only exclusion
|
|
401
|
+
that could ever protect conformant traffic, and refusing it is the over-refusal that sank a
|
|
402
|
+
criterion measured and rejected for this same family. The widened predicate **cannot** refuse an
|
|
403
|
+
escape-clean stream: every position it now rejects already raises an escape deviation of its own.
|
|
404
|
+
|
|
405
|
+
**That remaining silence is a TRADE, and it is not a claim that nothing was lost there.** On the
|
|
406
|
+
excluded tail the gained boundary is exactly as real, and it is not merely under-reported: it is
|
|
407
|
+
`warnings: []`, so a lenient consumer sees nothing at all. On the canonical set,
|
|
408
|
+
`R|1|^^^687|28.6&F&|&F&U/L||||F` reads **nine** fields against the competing alignment's eight and
|
|
409
|
+
hands back a status of **`final`**; `28.6&S&\&S&U/L` reads a value of `28.6^` and `^U/L` leaves
|
|
410
|
+
every modeled slot; `&F&^&F&GLU^L^687` reads one component more, so `687` is a vendor local code
|
|
411
|
+
under one reading and the **coding scheme** under the other. All three are `PRE-EXISTING`, all
|
|
412
|
+
three are now pinned by tests rather than by prose, and **read the raw line when an escape
|
|
413
|
+
character sits next to a delimiter, whether or not anything fired.**
|
|
414
|
+
|
|
415
|
+
**Additive in behaviour terms and still a report, not a repair.** No split changed, every decoded
|
|
416
|
+
byte is identical, and no tolerable code was struck off and no code became tolerable. **What the
|
|
417
|
+
three codes CLAIM about their role did have to move, and that is recorded rather than glossed**:
|
|
418
|
+
see the entry below. It still does not survive a re-emit:
|
|
419
|
+
the serializer rewrites the preserved characters into recognized mnemonics, so a second-generation
|
|
420
|
+
read is silent and correct about its own bytes. Catch it on the first read of the wire bytes.
|
|
421
|
+
|
|
422
|
+
**Measured against the corpus constants committed with the tests**, on the same
|
|
423
|
+
strict-accepted-under-a-gate-legal-profile tier as every measurement in this family. On the shared
|
|
424
|
+
**864-tuple** corpus each of the three codes now fires on **192** where it fired on 96, moves
|
|
425
|
+
**64** from accepted to refused where it moved 32, moves **0** back, and fires on **0** of the 96
|
|
426
|
+
escape-clean tuples exactly as before. The two reported tails contribute equally, so neither half
|
|
427
|
+
of a column carries the other.
|
|
428
|
+
|
|
429
|
+
**The axis that corpus holds fixed is swept beside it**, because a criterion measured only there
|
|
430
|
+
inherits its blind spot: that corpus varies whether the tail is bare, recognized or unrecognized,
|
|
431
|
+
but uses a single character as the unrecognized body. `test/records/alignment-unrecognized-tail.test.ts`
|
|
432
|
+
crosses `DECLARATION_ALPHABET`, `SPLITTING_ROLES` and `BODY_ALPHABET` with the same alphabet as the
|
|
433
|
+
**tail** body: **3,456 tuples**, of which **2,304** fire, **528** move from accepted to refused,
|
|
434
|
+
**0** move back, and **384** are escape-clean with **0** of them refused. Every tuple's observed
|
|
435
|
+
disposition is asserted against a predicate derived from those alphabets rather than against a
|
|
436
|
+
count, and the harness is failed on purpose against a perturbed mnemonic set in both directions.
|
|
437
|
+
|
|
438
|
+
**One universal that sweep refuted, recorded because it is the obvious thing to write.** "The
|
|
439
|
+
reading taken reads exactly one more segment than the competing alignment" is **false**. Where the
|
|
440
|
+
tail sequence's body is the delimiter the split is being taken on, the reading taken keeps that
|
|
441
|
+
character inside an opaque atom while the competing alignment splits on it, so the two readings
|
|
442
|
+
produce the **same** number of segments in **different** places: under `H~\^&`, `28.6&F&~&~&U/L`
|
|
443
|
+
reads `28.6&F&` + `&~&U/L` against the competitor's `28.6&F&~&` + `&U/L`. That is 144 of the 2,304
|
|
444
|
+
firing tuples. What holds on all of them is that the two readings **disagree** and that both
|
|
445
|
+
consume every byte, so neither is forced.
|
|
446
|
+
|
|
447
|
+
This is a **narrowing on a published package**, on the strict path only: a lenient parse of the
|
|
448
|
+
same stream returns the same records, with the same values, and one more warning on them. **No new
|
|
449
|
+
public surface**: no code, type, factory or parameter was added, removed or renamed.
|
|
450
|
+
|
|
451
|
+
- **The three alignment tail reports now state the one class in which nothing moves index**
|
|
452
|
+
(`ASTM-FRAME-RESIDUALS`, defect 17, the claim correction the widening above forced). Widening the
|
|
453
|
+
predicate widened the firing population past the point where each code's own cost claim held
|
|
454
|
+
everywhere, and the claims did not move with it. They do now. Where a surface states the cost, it
|
|
455
|
+
names the exception: the three sink docs in `src/common/escapes.ts`, the three code entries, the
|
|
456
|
+
three factory leads and the three **runtime warning messages** in `src/common/warnings.ts`,
|
|
457
|
+
`src/profiles/safety.ts`, the `referenceCorpus` rationale string, `README.md`,
|
|
458
|
+
`docs-content/quickstart.md`, `docs-content/limitations.md` and
|
|
459
|
+
`docs-content/troubleshooting.md`. Where a surface only paraphrased a cost, the paraphrase was
|
|
460
|
+
**removed** rather than re-qualified, so there is one place to keep true: the `@param` text on
|
|
461
|
+
`splitEscapeAware` in `src/common/escapes.ts` and on `tokenizeRecord` in `src/records/tokenize.ts`
|
|
462
|
+
now defers to the sink docs instead of restating them.
|
|
463
|
+
|
|
464
|
+
**The class, named rather than left to be found.** Where the sequence past the contested boundary
|
|
465
|
+
carries **the splitting delimiter itself** as its body, the reading taken holds that character
|
|
466
|
+
inside an opaque atom while the competing alignment splits on it, so the two readings return the
|
|
467
|
+
**same number** of segments in **different places**. Nothing moves index, and what differs is the
|
|
468
|
+
contents. On the canonical set `R|1|^^^687|28.6&F&|&|&U/L||||F` reads **nine** fields under both,
|
|
469
|
+
with the status `F` in field 9 under both and units of `&|&U/L` against `&U/L`; `28.6&F&\&\&U/L`
|
|
470
|
+
reads **two** repeats under both; `DOE&F&^&^&JANE^A` reads **three** components under both, with
|
|
471
|
+
`A` the middle name under both. So "every later field is one place further right", "the field is
|
|
472
|
+
read as more repeats than the other reading gives it" and "every later component sits one place
|
|
473
|
+
further right" were each false on that class, in `dist/index.d.ts`, in the immutable docs tarball,
|
|
474
|
+
and in three runtime messages.
|
|
475
|
+
|
|
476
|
+
**The class is bounded, and the bound is measured against constants committed with the test, not
|
|
477
|
+
argued.** `test/records/alignment-unrecognized-tail.test.ts` crosses `DECLARATION_ALPHABET`,
|
|
478
|
+
`SPLITTING_ROLES`, `BODY_ALPHABET` and `TAIL_BODY_ALPHABET` with two further constants,
|
|
479
|
+
`PAYLOAD_PREFIXES` and `PAYLOAD_SUFFIXES`, so the carrier shape is not held fixed the way every
|
|
480
|
+
earlier corpus in this family held it: **55,296 tuples**, of which **36,864** fire and **2,304**
|
|
481
|
+
are the class. **0** of those lack `ASTM_RECORD_DELIMITER_SWALLOWED_BY_ESCAPE` and **0** were
|
|
482
|
+
accepted before this release, because that tail body is a splitting delimiter in force. So the
|
|
483
|
+
three codes over-report relative to the indexes there and never under-report, and the class costs
|
|
484
|
+
no stream its disposition. The class is exactly "the tail body is the delimiter being split on",
|
|
485
|
+
with **0** counterexamples in either direction. Every figure is derived from those six constants
|
|
486
|
+
inside its own assertion; **never quote one against a different sha or a different corpus.**
|
|
487
|
+
|
|
488
|
+
**What holds on every firing tuple**, and it is what the claims now lead with: the two readings
|
|
489
|
+
**disagree**, and both **consume every byte**, so neither reading is forced. No code, message
|
|
490
|
+
identity, type, factory or parameter changed, and no stream's disposition moved.
|
|
491
|
+
|
|
207
492
|
### Fixed
|
|
208
493
|
|
|
209
494
|
- **A header that is not short no longer reports that it is too short** (`ASTM-FRAME-RESIDUALS`,
|
|
@@ -527,10 +812,13 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
527
812
|
unchanged, still `test/fixtures/**` and `src/**.ts`, so a staged link outside it is still not
|
|
528
813
|
looked at; narrowing what a scope admits is not widening the scope. That scope also bounds the
|
|
529
814
|
gitlink half: a submodule staged at `test/fixtures/nested` is refused, while one at `src/nested` is
|
|
530
|
-
a directory name that fails the `.ts` suffix and is not looked at.
|
|
531
|
-
still
|
|
815
|
+
a directory name that fails the `.ts` suffix and is not looked at. **`R` (rename) and `C` (copy)
|
|
816
|
+
are still not enumerated by `--staged` at all, so a staged rename that also appends PHI passes that
|
|
532
817
|
route; pre-existing, unchanged here, and admitting it needs the two-path record shape handled,
|
|
533
|
-
which is a scope decision rather than this one
|
|
818
|
+
which is a scope decision rather than this one.** That last sentence was true when it was written
|
|
819
|
+
and is **superseded later in this same release**: the entry below closes it, and it turned out to
|
|
820
|
+
need no two-path record shape at all. It is kept rather than rewritten because the measurement it
|
|
821
|
+
records is what the later entry starts from. A tracked file **absent from the worktree** is
|
|
534
822
|
still caught at `git add` time only.
|
|
535
823
|
|
|
536
824
|
**And this scanner still has no rule that a sweep observing ZERO targets must refuse**, which is
|
|
@@ -555,6 +843,87 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
555
843
|
is not, which makes closing it a separate slice with its own argument rather than a rider on a PHI
|
|
556
844
|
blindness fix. It is a different defect and it fails in the safe direction.
|
|
557
845
|
|
|
846
|
+
- **The PHI scanner's staged route no longer lets git decide what it is safe not to look at, so a
|
|
847
|
+
`git mv` into a fixture directory can no longer carry PHI past the pre-commit gate**
|
|
848
|
+
(`PHI-SCAN-RENAME-BLIND-AT-PRECOMMIT`). Development tooling only: `scripts/phi-scan.ts` ships in no
|
|
849
|
+
tarball and the package's public surface is unchanged.
|
|
850
|
+
|
|
851
|
+
Reproduced on `427af85` before any fix, on git 2.39.5, with the same synthetic name-bearing payload
|
|
852
|
+
the symlink cases use (an ASTM `P` record carrying a patient name, a mother's maiden name and a
|
|
853
|
+
birthdate, plus a dashed SSN and an email at a non-test domain). Four ways the enumeration could be
|
|
854
|
+
emptied before any of the refusals above were reached, each measured as its own throwaway index:
|
|
855
|
+
- **A staged rename.** `git mv test/fixtures/original.astm test/fixtures/renamed.astm` on a file
|
|
856
|
+
carrying the payload paired as a single record with **two** paths and a status letter of `R`, and
|
|
857
|
+
`--diff-filter=AMT` returned nothing for it, so `pnpm phi-scan --staged` printed `OK: no hits`
|
|
858
|
+
and exited **0**. The same move with the payload **edited into the destination in the same
|
|
859
|
+
staging** passed identically, because git still pairs it when enough of the old content survives.
|
|
860
|
+
No similarity score is quoted here: it moves with how much survives, so a number taken from one
|
|
861
|
+
fixture is wrong in the next, and what is load-bearing is that the record was paired at all.
|
|
862
|
+
- **A staged rename of a symbolic link.** `git mv` of a tracked link into `test/fixtures/` put a
|
|
863
|
+
mode-`120000` entry under a scan root. The mode refusal added by the previous entry already knew
|
|
864
|
+
how to refuse that; the pairing deleted the record before the mode could be read, and the run
|
|
865
|
+
exited **0**.
|
|
866
|
+
- **A gitlink the caller's config hid.** `diff.ignoreSubmodules = all` in the caller's git config
|
|
867
|
+
dropped a staged gitlink under `test/fixtures/` from the raw output entirely. The **same index**
|
|
868
|
+
refused at exit 2 without that config and reported `OK: no hits` at exit **0** with it.
|
|
869
|
+
- **An unmerged path.** An in-scope path recorded unmerged, with the payload on one side, was
|
|
870
|
+
returned by neither `AM` nor `AMT`, and the run printed `OK: no hits` and exited **0** over an
|
|
871
|
+
index it could not read: such a path is recorded at one or more of stages 1/2/3 and never at
|
|
872
|
+
stage 0, and stage 0 is exactly what the `:<path>` form this route reads with names, so there is
|
|
873
|
+
no one blob for it to read. **Nothing rests on what `git show` does with such a path**: the route
|
|
874
|
+
refuses before it would call it, and an earlier draft that pinned that exit code did not
|
|
875
|
+
reproduce across git versions.
|
|
876
|
+
|
|
877
|
+
**The remedy is entirely in the argv, and it is one rule: stop trusting the caller's git config,
|
|
878
|
+
and stop letting the filter decide what is safe not to look at.** The route now reads
|
|
879
|
+
`git diff --cached --raw -z --no-renames --ignore-submodules=none --diff-filter=AMTUB`.
|
|
880
|
+
`--no-renames` makes a paired record impossible, so the destination arrives as an ordinary
|
|
881
|
+
single-path `A` and the source as a `D` the filter drops; verified under `diff.renames` at
|
|
882
|
+
`true`, `copies` and `false` and under `diff.renameLimit=1`, every one yielding the same
|
|
883
|
+
single-path record, so the two-field record stride is now **structural** rather than conditional on
|
|
884
|
+
the caller's config. `--ignore-submodules=none` restores a record the config could delete.
|
|
885
|
+
`U` is in the filter **so it can be refused, not scanned**, with its own message: an unmerged
|
|
886
|
+
record's destination mode is `000000`, so the existing refusal would have described it with a
|
|
887
|
+
sentence about symbolic links and gitlinks that is false for it. That case builds the unmerged
|
|
888
|
+
index directly, with `git update-index --index-info`, rather than provoking it with a real merge:
|
|
889
|
+
whether a merge conflicts depends on the merge machinery and on the caller having a git identity,
|
|
890
|
+
and the index state is the thing under test. Staged as a real conflict it did not reproduce on
|
|
891
|
+
every git version. None of the four costs the record
|
|
892
|
+
stride anything, because each carries a single path.
|
|
893
|
+
|
|
894
|
+
**Be exact about what that changes.** The new enumeration is a **superset** of the old one, not a
|
|
895
|
+
strictly larger set: the two are **equal** whenever nothing is renamed, copied, unmerged or hidden
|
|
896
|
+
by that config, and larger only when something is. Nothing the old argv enumerated stops being
|
|
897
|
+
enumerated. The `--staged` path scope is **unchanged**, still `test/fixtures/**` and `src/**.ts`, so
|
|
898
|
+
an unmerged or renamed path outside it is still not looked at, and both of those are pinned.
|
|
899
|
+
|
|
900
|
+
**`B` is in the filter because `-B` is not inert, and the mechanism is sharper than "a `B` record
|
|
901
|
+
the filter drops".** A complete rewrite under `-B` prints a **single-path record whose status
|
|
902
|
+
letter is `M`**, with a break score, which the record regex parses happily, so a reader checking
|
|
903
|
+
the raw output concludes the record is an `M` and that `AMTU` therefore keeps it. It does not:
|
|
904
|
+
`--diff-filter` classifies a broken pair as `B` **whatever letter it prints**. Measured on one
|
|
905
|
+
index, over a wholly rewritten in-scope fixture carrying a dashed SSN: `-B --diff-filter=AMTU`
|
|
906
|
+
returns empty while `-B --diff-filter=B` and `-B --diff-filter=AMTUB` each return the same record,
|
|
907
|
+
and a copy of the scanner with `-B` injected exits **0** with `OK: no hits` on the superseded
|
|
908
|
+
filter and **1** with the hit on the shipped one. `B` costs the enumeration nothing today, since
|
|
909
|
+
git only breaks a pair when `-B` is given, so it is there to stop the flag ever being a silent
|
|
910
|
+
blindfold. `-M`, `-C` and `--find-copies-harder` each turn detection back on over the top of
|
|
911
|
+
`--no-renames` and empty the route again, which is measured beside it; none of the four may be
|
|
912
|
+
added.
|
|
913
|
+
|
|
914
|
+
**6 of the file's 41 cases are red on `427af85`**, and the premises each rests on are pinned beside
|
|
915
|
+
it, so a case cannot quietly stop testing anything. Refusal messages still name only the entry's own
|
|
916
|
+
repo-relative path and an engine-owned reason, never a link target or any stream content, and that
|
|
917
|
+
is asserted on the new refusal too.
|
|
918
|
+
|
|
919
|
+
**What this does not cover, stated narrowly.** The all-mode walk is untouched: it has no concept of
|
|
920
|
+
a rename, and nothing here widens or narrows what it enumerates. The scan roots' own paths are still
|
|
921
|
+
outside the `--staged` scope, so a staged blob, link or gitlink at exactly `test/fixtures`, `test`
|
|
922
|
+
or `src` is still not looked at by that route. Neither route reaches `test/**/*.test.ts`, where this
|
|
923
|
+
package keeps most of its inline fixtures, so those are still read by hand. And this scanner still
|
|
924
|
+
has no rule that a sweep observing zero targets must refuse, exactly as recorded above. Each of
|
|
925
|
+
those is a scope decision with its own argument, not a consequence of anything here.
|
|
926
|
+
|
|
558
927
|
- **The frame encoder no longer writes a different character than the one it was handed**
|
|
559
928
|
(`ASTM-FRAME-BYTE-RESIDUALS`, closing known defect 7). `composeAstmFrames` turned a record given
|
|
560
929
|
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,12 +240,77 @@ 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
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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.
|
|
261
|
+
|
|
262
|
+
**And where that gained boundary is a REPEAT boundary, nothing shifts and the field can still be
|
|
263
|
+
read short.** What the report says is that the field is read as more repeats than the competing
|
|
264
|
+
alignment gives it. A field's modeled value and components are taken from its **first repeat
|
|
265
|
+
alone**, so where the gained boundary is the **first** one everything past it stays on the wire,
|
|
266
|
+
stays in `repeats`, and leaves every modeled slot. On the canonical set, `R|1|^^^687|28.6&S&\&U/L|U/L||||F` reads a value of `28.6^`
|
|
267
|
+
and drops `&U/L`, and `R|1|&F&\&687|28.6|U/L||||F` reads a Universal Test ID of one component
|
|
268
|
+
holding a decoded field separator, so the local code `687` is in no modeled slot and the identity
|
|
269
|
+
comes back as a LOINC candidate nobody wrote. A patient name loses its given and middle names the
|
|
270
|
+
same way. That raises `ASTM_RECORD_ALIGNMENT_TRUNCATED_FIELD`, which **no profile may tolerate**;
|
|
271
|
+
before it existed the only warnings on those streams were tolerable ones, so a strict parse under a
|
|
272
|
+
legal profile accepted a truncated value and an emptied test identity. The reading is unchanged: it
|
|
273
|
+
reports the gained boundary rather than repairing it. **At a LATER boundary it fires and no modeled
|
|
274
|
+
slot moves**, because the first repeat is then the same under both readings; that is over-reporting
|
|
275
|
+
relative to the modeled slots and never under-reporting, and it is measured rather than assumed. Its
|
|
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.
|
|
244
314
|
|
|
245
315
|
### A header that names one character in two delimiter roles
|
|
246
316
|
|
|
@@ -337,9 +407,17 @@ outside it:
|
|
|
337
407
|
where the leftmost alignment lets a delimiter split that a competing alignment would have held,
|
|
338
408
|
gains a boundary instead of losing one and raises `ASTM_RECORD_AMBIGUOUS_ESCAPE_ALIGNMENT`, also
|
|
339
409
|
not tolerable. Where that gained boundary is a **field** boundary and the reading taken resumes on
|
|
340
|
-
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
|
|
341
412
|
and status are read out of slots the other alignment does not put them in: that raises
|
|
342
|
-
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, not tolerable either.
|
|
413
|
+
`ASTM_RECORD_ALIGNMENT_SHIFTED_FIELDS`, not tolerable either. Where it is a **repeat** boundary
|
|
414
|
+
nothing shifts, but the field is read out of its first repeat alone, so a gained first boundary
|
|
415
|
+
truncates a value and costs a test identity or a patient name the components that sat after it:
|
|
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.
|
|
343
421
|
|
|
344
422
|
All are accepted limits, for two different reasons: widening the field-separator check would mean
|
|
345
423
|
deciding which set a record ought to have had, which is the same guess the parser declines to make
|