@cosyte/x12 0.0.12 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -9,6 +9,227 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
9
9
 
10
10
  ### Added
11
11
 
12
+ - **🩺 `X12_837_SERVICE_SEGMENT_REPEATED`, the 33rd Tier-2 warning code, plus the public factory
13
+ `serviceSegmentRepeated(position)`** (`X12-837-SV1-OVERWRITE`). A **second** `SV1` / `SV2` / `SV3`
14
+ arriving inside an **already-open** Loop 2400 now says so, at that repeated segment. Through
15
+ `0.0.13` it said nothing at all.
16
+
17
+ **🩺 What that cost, and it is money and a procedure code.** A service line carries **one** service
18
+ segment's worth of slots, and every decoder writes **all** of the slots its kind writes. Under an
19
+ `ST-03` of `005010X222A2`, `SV1*HC:99213*8500*UN*4***1~` followed by `SV1*HC:99999*12*UN*1***1~`
20
+ inside one `LX` left ONE line reading `charge` **`12`** and `procedureCode` **`99999`**, with
21
+ `warnings: []`. `8500` became `12`, CPT `99213` became `99999`, and nothing was raised on any
22
+ channel. **The worst corner is a repeat whose own charge element is ABSENT:** it writes `undefined`
23
+ over the amount the first one stated, and `X12_837_SERVICE_LINE_NOT_DECODED` does **not** fire
24
+ there, because a service segment did decode.
25
+
26
+ **🛑 THIS CLOSES ONLY THE SILENCE, AND THE RESTRAINT IS THE POINT.** The decode is **not** narrowed:
27
+ last-wins is unchanged, element for element, so which values a document decodes to are byte-for-byte
28
+ what they were at `0.0.13`. This reader cannot tell a stray service segment from a conformant one,
29
+ so choosing the first would be inventing; and changing which occurrence wins changes how
30
+ **already-published documents decode**. That is the same call made for the `SVx` variant fallback.
31
+
32
+ **It fires on a repeat of ANY kind, decoded or not.** One whose kind does not match the resolved
33
+ variant is read into nothing and overwrites nothing - what it carries reaches no part of the typed
34
+ model - and it is reported the same way, before or after the matching one.
35
+
36
+ **Anchored at the repeated service segment**, with **no `elementIndex`**: what is reported is a
37
+ second occurrence of the segment, not a defect in an element of it. **Once per repeat**, and the
38
+ count is scoped to the LINE and never latched, so a first service segment under a later `LX` is a
39
+ first. It can never name the same segment as `X12_837_SERVICE_SEGMENT_WITHOUT_LX`, which requires
40
+ that no Loop 2400 be open.
41
+
42
+ **It is additive and nothing moved onto it**, pinned by committed tests asserting the whole warning
43
+ channel with the new code filtered out. **No consumer predicate written against any existing code
44
+ changes meaning.** Read that as invariance, not as a list of what else you will see.
45
+
46
+ **🛑 The package's own documentation was a consumer, and it was blind.** The cookbook's "gate before
47
+ you post a line amount" recipe named four codes and **none** of them fires on the overwrite
48
+ document, so a consumer following it posted `12` for a line the sender also sent as `8500`. The
49
+ cookbook, the troubleshooting table and `KNOWN-LIMITATIONS.md` now name this code beside the other
50
+ four, and a committed test pins that the four-code gate misses what the five-code gate catches.
51
+ **The recipe was not the only page:** `spec-notes-money` named `X12_837_SERVICE_LINE_NOT_DECODED`
52
+ as "the known instance" of an 837 charge reading `undefined` from a slot no reader read, and the
53
+ repeat corner is a second instance by a different route on which that code does not fire. It now
54
+ says so.
55
+
56
+ - **🩺 `X12_837_AMBIGUOUS_VARIANT`, the 32nd Tier-2 warning code, plus the public factory
57
+ `ambiguous837Variant(position)`** (`X12-837-RESIDUALS`). An 837 whose variant was decided by the
58
+ `SVx` fallback, in a transaction body that carries service segments for **more than one variant**,
59
+ now says so at the `ST`. Through `0.0.13` it said nothing at all about that resolution.
60
+
61
+ **What that cost.** Variant resolution runs before the walk as
62
+ `explicitType ?? variantFromIcr ?? variantFromSegment`. Absent a caller `type` option, and where
63
+ `ST-03` names no implementation convention this reader recognises, the reader falls back to the
64
+ **first** `SV1` / `SV2` / `SV3` in the body, orphans included. One stray `SV2` ahead of a conformant
65
+ Professional claim therefore re-types the whole submission Institutional: `submission.variant` reads
66
+ `"I"`, and a consumer routing on that field sends a Professional claim down an Institutional path.
67
+ The line-level consequences were reported; **the submission-level typing that produced them was on
68
+ no channel**, so `submission.variant` carried a confident value with nothing to contradict it.
69
+
70
+ **🛑 THIS CLOSES ONLY THE SILENCE, AND THE RESTRAINT IS THE POINT.** The fallback is **not**
71
+ narrowed and first-wins is unchanged, so on every document that reaches the fallback the variant
72
+ resolved and the lines decoded are byte-for-byte what they were at `0.0.13`, and this code is added
73
+ beside whatever the walk already raised. (`X12-VARIANT-ICR-UNGROUNDED`, in this same release,
74
+ changed WHICH documents reach the fallback; read its entry for what decodes differently.) Excluding orphans from
75
+ the fallback would change how already-published documents decode and is its own slice.
76
+
77
+ **🩺 Which service segment is the stray one is NOT decided.** This reader cannot tell a stray
78
+ service segment from a conformant one, and the fallback takes the first whether or not a Loop 2400
79
+ was open at it. Reporting the conflict is honest; picking a winner would be inventing. Re-read with
80
+ `get837Claims(delimiters, tx, { type })` to decode against a variant you trust.
81
+
82
+ **It reports the RESOLUTION, never the document.** A caller `type`, or an `ST-03` naming a known
83
+ convention, means no guess was made and this code is **not** raised however mixed the body is.
84
+
85
+ **Anchored at the `ST`** (`tx.segments[0]`, which carries `ST-03`), with **no `elementIndex`**: the
86
+ conflict is a property of the body rather than of an element, and one route into it is an `ST-03`
87
+ that is absent altogether. Raised **once per transaction**, because there is one resolution per
88
+ transaction, and it can never travel with `X12_837_UNKNOWN_VARIANT`, which is the other outcome of
89
+ that same resolution.
90
+
91
+ **🩺 It is additive, and nothing moved onto it.** `X12_837_SERVICE_LINE_NOT_DECODED`,
92
+ `X12_837_SERVICE_SEGMENT_WITHOUT_LX` and `X12_837_SERVICE_LINE_DROPPED` fire on exactly the
93
+ documents they fired on before **this code was added**, in the same positions, pinned by committed
94
+ tests that assert the whole warning channel with the new code filtered out. **No consumer predicate
95
+ changes meaning because of this code.** (`X12-VARIANT-ICR-UNGROUNDED`, in this same release, DID
96
+ change which documents reach the `SVx` fallback; read its entry above.) Read that as **invariance and not as a list of what else you will
97
+ see** on a contested document: it does not promise that any particular loss on one is reported at
98
+ all, and one that is not was not reported before this code existed either. A stray `LX` that opened
99
+ no line, for one, already suppressed `X12_837_SERVICE_SEGMENT_WITHOUT_LX` for the service segments
100
+ inside it, and still does.
101
+
102
+ - **🩺 `X12_STATED_AMOUNT_DISCARDED`, the 31st Tier-2 warning code, plus the public factory
103
+ `statedAmountDiscarded(position)`** (`X12-STATED-AMOUNT-DISCARDED`). The entry below reports a row
104
+ whose amount this library could not read. This one is the opposite case: **the reader discarded
105
+ the row for a reason that is not about the amount at all**, so the amount was never the problem
106
+ and, on one of the two routes, was never even looked at. The bytes stay verbatim on
107
+ `tx.segments[…].raw`; decode them yourself before posting. Through `0.0.12` both routes were
108
+ silent on every channel.
109
+
110
+ **The two routes, enumerated.** An **820 `RMR`** under an open remittance loop whose `RMR-01` and
111
+ `RMR-02` are **both empty** while `RMR-04` or `RMR-05` is populated: `decodeRmr` refuses the open
112
+ item on identity **before** either amount element is read, so `RMR****150.00*150.00~` gave
113
+ `openItems: []` and `warnings: []`, taking a stated payment, a stated amount due and `RMR-03`'s
114
+ payment action code together. And an **837 `AMT`** arriving while a Loop 2430 line adjudication
115
+ (`SVD`) is open, whose `AMT-02` decoded: the v1 adjudication model carries no amount row, and
116
+ attaching one to this submission's own service line would put another payer's figure on it, so
117
+ `AMT*EAF*75.00~` (Remaining Patient Liability) was skipped in silence.
118
+
119
+ **🩺 That second route is where the channel read BACKWARDS, and squaring it is the point.** Under
120
+ an open `SVD`, `AMT*EAF~` raised `X12_AMOUNT_ROW_DROPPED` and `AMT*EAF*75.00~` raised nothing: the
121
+ report was present exactly where **less** was lost. Both report now, and which code arrives is
122
+ what says which loss it was.
123
+
124
+ **It carries no `position.elementIndex`.** On the `RMR` route the loss spans `RMR-04` and
125
+ `RMR-05` and no single element names it; on the `AMT` route the element is fixed by the segment.
126
+
127
+ **🩺 It is additive, and nothing moved onto it.** `X12_AMOUNT_ROW_DROPPED` and
128
+ `X12_UNPARSEABLE_DECIMAL` fire on exactly the documents they fired on before, pinned by committed
129
+ tests. **The two amount-row codes are disjoint and can never name the same segment**, because this
130
+ one requires an amount element the sender populated and the other requires one that decoded no
131
+ value, so the code you get is the discriminant. **Gate on both.** `KNOWN-LIMITATIONS.md`, the
132
+ money spec-note and the troubleshooting table were corrected with the code.
133
+
134
+ **Bounds, stated as properties of the READ.** What is reported is a segment that populated its
135
+ amount element and arrived **while the loop that would carry its row was open**. One reaching a
136
+ reader with no such loop open is a different loss and is **still silent**: the 834's `AMT` with no
137
+ `HD` open, the 820's `ADX` with no remittance open, and the 835's and the 837's `AMT` before any
138
+ claim or service line. A bare `RMR~` states nothing and is silent, as is an identity-less `RMR`
139
+ carrying only a payment action code. And this code says **nothing** about whether the amount would
140
+ have decoded: the `RMR` route refuses the row before attempting the decode, so it is raised on
141
+ `RMR****1,234.56~` exactly as on `RMR****150.00*150.00~`, with **no `X12_UNPARSEABLE_DECIMAL`
142
+ beside it in either case**, on this release and on every earlier one. **Do not read an
143
+ unaccompanied instance as evidence the bytes are postable.** Only the `AMT` route guarantees a
144
+ decodable amount, because there `AMT-02` decoded before the row was skipped.
145
+
146
+ - **🩺 `X12_AMOUNT_ROW_DROPPED`, the 30th Tier-2 warning code, plus the public factory
147
+ `amountRowDropped(position)`** (`X12-AMT-ADX-ABSENT-AMOUNT`). An `AMT` or `ADX` is not a slot on a
148
+ bigger record: each one **is** a record, carrying an amount plus the thing the amount is about. So
149
+ when the amount element (`AMT-02`, `ADX-01`) decodes no value there is no row to build, and
150
+ `AMT-01`'s qualifier or `ADX-02`'s adjustment reason code is dropped with it. Through `0.0.12`
151
+ that happened with **no diagnostic on any channel**: `AMT*B6~` gave `claim.amounts: []` and
152
+ `warnings: []`, which reads exactly like a document that never carried the segment. It is now
153
+ reported, at the `AMT` / `ADX` itself.
154
+
155
+ Raised by four surfaces: the 835's `AMT`, the 837's `AMT`, the 834's coverage `AMT` and the 820's
156
+ `ADX`. The 834's lands on that **member's** own `warnings`, the same per-member scoping the
157
+ decimal sink beside it already used, because a roster-level report would say a premium was lost
158
+ without saying whose. **On the 835 and the 837 the `AMT` attaches to the open SERVICE LINE first
159
+ and to the claim only when there is none, so the row lost may be a line-level one** - an unchanged
160
+ `claim.amounts` is not evidence the warning is stale.
161
+
162
+ **It carries no `position.elementIndex`**, deliberately. One of its two routes is an absent
163
+ element, and an absent element has no index to name; the segment fixes which element was being
164
+ read anyway.
165
+
166
+ **🩺 It is additive, and no case moved onto it.** An amount that is present and does not decode
167
+ still raises `X12_UNPARSEABLE_DECIMAL` at its own `elementIndex`, now **alongside** this code
168
+ rather than instead of it, so a gate you already wrote against that code fires on exactly the
169
+ documents it fired on before. Whether one accompanies this code at the same `position.segmentIndex`
170
+ is what separates the absent route from the unparseable one, since this code is raised for both
171
+ and discriminates neither. What a one-code gate never caught, on any release, is the
172
+ absent-amount row: **gate on both.** `KNOWN-LIMITATIONS.md`, the money spec-note and the
173
+ troubleshooting table were corrected with the code, and a committed test pins both halves - that
174
+ the one-code gate misses the absent-amount document, and that it still fires where it always did.
175
+
176
+ **Bounds, stated because the wider reading is the tempting one, and stated as properties of the
177
+ READ rather than of the walker's control flow.** What is reported is a row whose amount was read
178
+ and decoded no value. A segment discarded **before** its amount is read is not on this channel
179
+ (the 834's `AMT` with no `HD` open, the 820's `ADX` with no remittance open), and neither is one
180
+ whose amount decoded and then found nothing open to attach the row to. **Do not shorten that to
181
+ "nothing open means silent"** - the 835 and the 837 decode first, so an `AMT` with an absent
182
+ amount and no claim open does raise this code. An 820 `RMR` is not on this channel either, and
183
+ **not** because its row survives: `decodeRmr` drops on open-item identity (`RMR-01` and `RMR-02`
184
+ both empty) **before** `RMR-04` is read, so an `RMR` stating an open item and no amount keeps its
185
+ row with `amountPaid` left `undefined`, while one stating an amount and **no** open item is
186
+ dropped whole. That second case, and the 837's Loop 2430 `AMT` which an open `SVD` discards
187
+ outright, are separate losses: nothing failed to decode in either, so neither is this code's
188
+ shape, and `X12_STATED_AMOUNT_DISCARDED` above is what reports them.
189
+
190
+ - **🩺 `X12_835_BALANCE_NOT_EVALUABLE`, the 29th Tier-2 warning code, plus the public factory
191
+ `balanceNotEvaluable(position, invariant)`** (`X12-837-SV-UNDEFINED-DECIMAL`). Raised where a term
192
+ of one of the three TR3 005010X221A1 §1.10.2 balance equations is `undefined` on the model, so the
193
+ equation has nothing to compare on one side. `invariant` is the same library-owned
194
+ `BALANCE_INVARIANTS` discriminant `X12_835_REMIT_BALANCE_MISMATCH` takes, and the message names the
195
+ equation and carries no amount, like every other warning in this registry.
196
+
197
+ **It is a different code from the mismatch on purpose.** The mismatch asserts a computed inequality
198
+ between amounts the sender supplied; this one asserts only that the comparison could not be made.
199
+ Through `0.0.12` an undecoded term collapsed to `X12Decimal.ZERO` and the equation then reported a
200
+ mismatch between the payer's own amounts and a zero this library invented, which is the reading
201
+ that made an absent `CLP-03` indistinguishable from a claim submitted at zero.
202
+
203
+ **An EMPTY list of adjustments is NOT an absent term** and does not raise it: a claim carrying no
204
+ `CAS` really did state no adjustments, so its sum is `X12Decimal.ZERO`. Only a term this library
205
+ decoded no value from stops the equation. `build835` reaches the same verdict as
206
+ `X12_835_BUILD_INVALID_SPEC` rather than as its balance-mismatch code, and is unreachable there
207
+ from TypeScript because every balance term on `Build835Spec` is a required `X12Decimal`.
208
+
209
+ **🩺 GATE ON BOTH CODES.** A posting gate written against `X12_835_REMIT_BALANCE_MISMATCH` alone
210
+ **stops firing** on these documents when you upgrade, because through `0.0.12` the undecoded term
211
+ collapsed to zero and raised the mismatch. This library warns either way; your gate has to look
212
+ for both. The recipes in `docs-content/quickstart.md` and `docs-content/cookbook.md` and the
213
+ triage table in `docs-content/troubleshooting.md` were corrected with the code, and a committed
214
+ test pins that the one-code gate misses a document the two-code gate catches.
215
+
216
+ - **🩺 `X12_837_PAY_TO_ADDRESS_REPEATED`, the 28th Tier-2 warning code, plus the public factory
217
+ `payToAddressRepeated(position)`** (`X12-PAY-TO-FUSION`). Raised at the **second and each
218
+ subsequent `NM1*87` within one Loop 2000A**, where the TR3s allow Loop 2010AB at most once.
219
+ `position.segmentIndex` names the repeated `NM1*87` itself and carries no `elementIndex`: what is
220
+ reported is a second occurrence of the segment, not a defect in any element of it. The counter
221
+ resets at the Loop 2000A `HL`, beside the pay-to slot it guards, so a first `NM1*87` under a later
222
+ billing provider is a first and not a repeat.
223
+
224
+ The model has **one** pay-to address slot, so it cannot carry two, and this code is the only thing
225
+ that says the document named two. It reports that the **document** repeated the loop; it does not
226
+ report that anything was mis-read, and it says nothing about the subjects of the other 837 codes.
227
+ An `NM1*87` arriving while a `CLM` is open never reaches the pay-to route at all, so it neither
228
+ warns nor arms the warning. **Where it lands instead is not stated as one destination, because two
229
+ were measured:** with a Loop 2400 open it joins that line's `serviceLine.providers` (TR3 Loop
230
+ 2420), and with a claim but no line open it joins `claim.providers` (Loop 2310). Both are
231
+ pre-existing and identical at `0.0.12`.
232
+
12
233
  - **🩺 `X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX`, the 27th Tier-2 warning code, plus the public
13
234
  factory `entitySegmentDiscardedAfterLx(position)`** (`X12-DISCARD-AFTER-STRAY-LX`). Raised for
14
235
  each 837 `N3` / `N4` / `PER` / `REF` that reached **no party at all** because an earlier `LX` with
@@ -139,6 +360,130 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
139
360
 
140
361
  ### Changed
141
362
 
363
+ - **🩺 `get837Claims` now recognises every published `ST-03` implementation-convention reference for
364
+ the three 837 guides, and some already-published files therefore decode differently**
365
+ (`X12-VARIANT-ICR-UNGROUNDED`). This is a grounding unit: the table `VARIANT_BY_ICR` had three keys
366
+ and they were grounded against nothing.
367
+
368
+ **🩺 What that cost, measured at `668afea`, which is `main` at published `0.0.13`.** The table held exactly
369
+ `005010X222A2`, `005010X223A3` and `005010X224A2`. **It contained none of the identifiers HIPAA
370
+ adopts at 45 CFR 162.1102** (`005010X222`; `005010X223` with its `005010X223A1` Type 1 errata;
371
+ `005010X224` with `005010X224A1`), **and it was missing `005010X222A1` and `005010X223A2`**, which
372
+ CMS and state Medicaid companion guides require in ST-03 and GS-08 on production professional and
373
+ institutional claims. So a conformant, HIPAA-mandated 837P declaring `005010X222A1` resolved to no
374
+ variant at all, fell through to the `SVx` scan, and one stray `SV2` anywhere in the body re-typed
375
+ the whole submission Institutional. **The `SVx` fallback was the NORMAL path on production
376
+ professional and institutional traffic rather than the exception**, and `X12_837_UNKNOWN_VARIANT`
377
+ on such a file was a fabricated non-conformance claim about a document that was not
378
+ non-conformant. The sources for every key are named beside the table and in
379
+ `documentation/agent-notes/x12-variant-icr-ungrounded.md`; the three later published errata guides
380
+ are the weakest leg and say so in place.
381
+
382
+ **🛑 This IS a behaviour change on already-published decoding, and it is disclosed rather than
383
+ buried.** On a file whose `ST-03` is now recognised: `submission.variant` can differ from what
384
+ `0.0.13` read, wherever the first `SVx` in the body disagreed with the declaration; and
385
+ **`X12_837_AMBIGUOUS_VARIANT` and `X12_837_UNKNOWN_VARIANT` no longer fire on it at all**, because
386
+ no guess was made. **A predicate written against either code goes quiet on such a file.**
387
+
388
+ **🩺 And a service line whose `SVx` kind disagrees with the declaration is no longer DECODED, so a
389
+ code STARTS firing on a document that may have carried `warnings: []`.** Under an `ST-03` of
390
+ `005010X222A1` with a body whose only service segment is an `SV2`, `0.0.13` read `variant` `"I"`,
391
+ `charge` `7300`, `units` `2` and `warnings: []`; this release reads `variant` `"P"`, `charge` and
392
+ `units` `undefined` with the rest of the service segment undecoded, and
393
+ `X12_837_SERVICE_LINE_NOT_DECODED` at that line's `LX`. **Read only the decimal slots as
394
+ `undefined`:** an undecoded line SEEDS its identity fields, so `procedureCode` is `""` on a P or D
395
+ line and `revenueCode` is `""` on an I one. A predicate of `procedureCode === undefined` does NOT
396
+ detect this.
397
+ A mis-stamped envelope is an ordinary vendor variant and this reader can no more tell one from a
398
+ conformant document than it can tell a stray `SVx` from a conformant one, so the loss is **warned
399
+ rather than silent**, and the cookbook's post-a-line-amount gate already names that code first.
400
+
401
+ **🛑 Read all of that as ONE property and never as a closed list of consequences:** where `ST-03`
402
+ is now recognised, **the document's own declaration decides the variant instead of its first
403
+ service segment**, and everything downstream follows from that single substitution. A first draft
404
+ of this entry published a census of three and a refuter measured it false by finding a fourth.
405
+
406
+ That is
407
+ the hazard a widening onto a new code carries, taken here in the opposite direction from the two
408
+ slices before it: they refused to narrow a fallback because the reader had no evidence beyond the
409
+ segments, and here the reader had the evidence in ST-03 and was ignoring it. Re-check any routing
410
+ driven off `submission.variant` for 837s you read on `0.0.13` or earlier.
411
+
412
+ **🛑 The `SVx` fallback is NOT narrowed.** First-wins still takes the first `SV1` / `SV2` / `SV3` in
413
+ the body, orphans included, on every document that still reaches it, and precedence is unchanged:
414
+ a caller `type` still wins ahead of ST-03, and ST-03 ahead of the segments. What changed is which
415
+ documents reach the fallback.
416
+
417
+ **It is a LIST of cited identifiers, never a pattern.** A reference outside the set, in a different
418
+ case, or carrying leading whitespace still falls through exactly as before. The set is **not
419
+ claimed exhaustive and no count of it is published**: both variant-resolution messages named the
420
+ three old keys literally, so both were wrong the moment the table was corrected, and neither
421
+ enumerates the set any more. A committed tripwire reds if any registry message quotes a TR3
422
+ identifier again.
423
+
424
+ **🩺 OPEN, and deliberately not fixed here: the EMIT side still stamps the old three.** `build837P`
425
+ / `build837I` / `build837D` write `005010X222A2` / `005010X223A3` / `005010X224A2` and a caller
426
+ cannot override them, so **a partner that requires `005010X222A1` or `005010X223A2` will reject an
427
+ 837 this builder emits.** Which published guide identifier a partner accepts is a partner fact
428
+ rather than a spec fact, and changing bytes this library already emitted would break the partners
429
+ it works with today. `KNOWN-LIMITATIONS.md` carries it as an open residual.
430
+
431
+ - **🩺 BREAKING (read model): every monetary, percent and quantity slot the readers used to fill with
432
+ a fabricated `X12Decimal.ZERO` is now `X12Decimal | undefined`** (`X12-837-SV-UNDEFINED-DECIMAL`).
433
+ Through `0.0.12` an **absent** `SV1-02` - a monetary field on an 837 claim service line - read back
434
+ as `X12Decimal.ZERO`, so a consumer could not tell "the sender stated zero" from "the sender stated
435
+ nothing" and this library presented the second as the first. Fourteen model slots carried that
436
+ fabrication: `X12Claim.totalCharge` (CLM-02), a service line's `charge` and `units` (SV1-02 /
437
+ SV2-03 / SV3-02 and SV1-04 / SV2-05 / SV3-06), `X12LineAdjudication.amountPaid` (SVD-02),
438
+ `X12RemitClaim.totalChargeAmount` / `totalPaymentAmount` / `patientResponsibilityAmount` (CLP-03 /
439
+ 04 / 05), `X12RemitServiceLine.chargeAmount` / `paymentAmount` (SVC-02 / SVC-03),
440
+ `X12RemitPaymentHeader.totalActualPayment` and `X12PremiumPaymentHeader.totalPremiumAmount`
441
+ (BPR-02), `X12PremiumOpenItem.amountPaid` (RMR-04), `X12RemitAdjustment.amount` (a `CAS` triple,
442
+ read by both the 835 and the 837) and `X12RemitProviderAdjustment.amount` (a `PLB` pair).
443
+
444
+ **`undefined` means "this library decoded no value from that element", NOT "the element was
445
+ absent."** A present element holding bytes that do not decode lands there too, and
446
+ `X12_UNPARSEABLE_DECIMAL` at that `position.elementIndex` is the only thing that separates them.
447
+ Read it in exactly that direction. A **stated** zero is untouched: it still decodes, still reads
448
+ `0`, and keeps its lexical form, so `0.00` is still `0.00`.
449
+
450
+ **Migrating:** `x.toString()` becomes `x?.toString()` at every one of those slots, and each site is
451
+ a decision rather than a rewrite - an `undefined` amount is not zero, and a `?? 0` reinstates the
452
+ defect. If you posted cash off a slot that read `0` on `0.0.12` or earlier without also gating on
453
+ `.warnings`, re-read those files.
454
+
455
+ Two things this did **not** do. `elementDecimalOrZero` is a public export and is unchanged: it
456
+ still substitutes `X12Decimal.ZERO`, because that is its documented behaviour and a consumer
457
+ walking segments itself may still want it. What changed is that no reader in this library calls it
458
+ any more. And the 837's `X12_837_SERVICE_LINE_NOT_DECODED` is still the only thing that says WHY a
459
+ line's `charge` and `units` are empty: `undefined` alone does not separate "no `SVx` was decoded
460
+ onto this line" from "an `SVx` was decoded and carried no charge element".
461
+
462
+ - **🩺 BREAKING (emit side): `Build837ServiceLineSpec.units` is now required, and `build837P` /
463
+ `build837I` / `build837D` refuse a service line without it** (`X12-837-SV-UNDEFINED-DECIMAL`).
464
+ Through `0.0.12` an omitted `units` was emitted as the literal `0` into SV1-04 / SV2-05 / SV3-06,
465
+ so the builder stated a service unit count no caller supplied - the read-side fabrication above,
466
+ running the other way. Measured on `0.0.12`: a Professional line with no `units` emitted
467
+ `SV1*HC:99213*8500*UN*0*11**1~`. The refusal is `X12_837_BUILD_INVALID_SPEC`, naming the structural
468
+ locator (`billing[0].subscriber[0].claim[0]`) and no caller value.
469
+
470
+ **Refusing rather than emitting an empty element is the stance, and it is not new here:**
471
+ `build277` already refuses a service line without `SVC-07` on the same grounds. The parser is
472
+ liberal and the serializer conservative, and a count is not something a serializer may leave for
473
+ the receiver to guess at. A caller that supplies `units` is unaffected in every respect, including
474
+ one that supplies a zero, which is emitted because the caller did state it.
475
+
476
+ - **`build835` refuses a JS caller's `undefined` balance term as an invalid spec rather than throwing
477
+ an untyped `TypeError`** (`X12-837-SV-UNDEFINED-DECIMAL`). Unreachable from TypeScript: every term
478
+ of the three §1.10.2 invariants is a required `X12Decimal` on `Build835Spec`. Through `0.0.12` a JS
479
+ caller passing `undefined` reached `undefined.add` inside the balance guard. It is now
480
+ `X12_835_BUILD_INVALID_SPEC` with the registry text, and deliberately **not**
481
+ `X12_835_BUILD_BALANCE_MISMATCH`: nothing was measured out of balance, a required amount is simply
482
+ missing. Passing a raw `number` into a balance term still throws **an** untyped `TypeError` from
483
+ the same guard, so the documented dichotomy did not move; on some slots the message is now
484
+ `X12Decimal`'s own tampering text where it used to be `.add is not a function`, which the pinned
485
+ regex already admitted. Still disclosed in `KNOWN-LIMITATIONS.md`.
486
+
142
487
  - **`KNOWN-LIMITATIONS.md` now ships in the published tarball** (`X12-837-LOOP-RESIDUALS`). Two
143
488
  shipped warning messages, `X12_837_SERVICE_LINE_DROPPED` and
144
489
  `X12_837_ENTITY_SEGMENT_DISCARDED_AFTER_LX`, name that document ("see KNOWN-LIMITATIONS.md", each
@@ -556,6 +901,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
556
901
 
557
902
  ### Fixed
558
903
 
904
+ - **🩺 An `AMT` / `ADX` row whose amount decodes nothing is no longer dropped in silence**
905
+ (`X12-AMT-ADX-ABSENT-AMOUNT`). Reproduced on the base tree at `9db104b` across all four readers,
906
+ and the report it now raises is the `X12_AMOUNT_ROW_DROPPED` entry under **Added** above. The
907
+ filing that named this defect stated the bound precisely and it is repeated here because the wider
908
+ form is false: an **absent** amount element was silent on every channel, while a **present** one
909
+ holding undecodable bytes was already reported by `X12_UNPARSEABLE_DECIMAL` at its own
910
+ `elementIndex`. Both now also raise the new code; nothing moved off the old one.
911
+
912
+ - **A cookbook sentence said an undecoded 837 service line ships `charge` and `units` as `0`.** It
913
+ reads `undefined` on the same release that made the rest of that page true, so the page contradicted
914
+ the money spec-note it links to. Corrected in passing while sweeping that paragraph for the new
915
+ code. This is a documentation correction only; no behaviour changed with it.
916
+
917
+ - **🩺 A repeated `NM1*87` in one 837 Loop 2000A no longer fuses two pay-to addresses into one the
918
+ sender never sent** (`X12-PAY-TO-FUSION`). Reproduced at `0.0.12`: `payToAddress` is a bare
919
+ accumulator with no entity object to own it, cleared only at the next Loop 2000A `HL`. Every other
920
+ party gets a fresh object at its `NM1`, so a trailing `N3` / `N4` found `address === undefined` and
921
+ its write replaced; the two pay-to arms had nothing replaced under them and wrote onto whatever the
922
+ previous `NM1*87` left, `withLines` appending and `mergeAddress` falling back. Two `NM1*87`s each
923
+ with an `N3` and an `N4` read back **a street line from each of two addresses** plus a
924
+ `countryCode` taken off the **first** `N4`, on an address whose own `N4` names no country, with
925
+ `warnings: []`. Re-emitted through `build837P` that became a single Loop 2010AB stating a payment
926
+ destination no sender had stated.
927
+
928
+ **The fix is the missing object identity, not a clear.** Each `NM1*87` now opens a fresh
929
+ accumulator that the address arms read and write, so values from two occurrences can never meet.
930
+
931
+ **🛑 Two earlier remedies were refuted, both for one reason, and it is why the emit side was in
932
+ scope from the start: on this slot an emptied value is not a neutral absence, because the emit side
933
+ reads it.** `build837P/I/D` gates Loop 2010AB on `payToAddress !== undefined` and `emitAddress`
934
+ writes `N3` only for non-empty `lines` and `N4` only for a defined field. Clearing the accumulator
935
+ at the `NM1*87` therefore erased an address a repeat carrying no `N3` / `N4` did state, re-emitting
936
+ as **no pay-to loop at all**; a flag consumed by the first write after it did the same on a
937
+ valueless `N3` or `N4`, re-emitting as a **bare `NM1*87`**. Both are positive statements about
938
+ where a payment goes that no sender made. So the rule is stated in the emit side's own terms: the
939
+ current occurrence takes the slot **only when it states an address the emit would write at least
940
+ one segment for**, and an occurrence that states none leaves a stated one alone. The predicate is a
941
+ single shared module (`src/transactions/claim/address-segments.ts`) that `emitAddress` also asks,
942
+ so the reader and the writer cannot drift apart. This is the discipline the 835 already uses, where
943
+ the emit guard reuses the read side's own balance validators.
944
+
945
+ **A document with at most one `NM1*87` per Loop 2000A is byte-for-byte unaffected**, warning
946
+ channel included, and that is pinned both ways.
947
+
948
+ **🩺 The cost, stated rather than argued away: a repeat that states only part of an address now
949
+ re-emits only that part.** A second `NM1*87` followed by an `N4` and no `N3` reads back that `N4`
950
+ with `lines: []` and re-emits a Loop 2010AB with no `N3`. Keeping the earlier occurrence's street
951
+ lines there **is** the fusion: one sender's street under another sender's city. The loop is still
952
+ present, the warning is on the channel, and what is gone is the fabricated street.
953
+ `KNOWN-LIMITATIONS.md` records it.
954
+
559
955
  - **🩺 `build278Request` / `build278Response` refuse a review whose HL-03 level code is outside `EV`
560
956
  and `SS`, instead of emitting a review its own reader cannot decode**
561
957
  (`REFUSAL-MESSAGE-PHI-ECHO`). `Build278ReviewSpec.levelCode` is the **one** caller-supplied HL-03 in
@@ -633,8 +1029,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
633
1029
  are both the line charge, so reading a service segment into a line the walker never opened
634
1030
  mis-reads money. Refusing to read is the safe half; doing it silently was the defect. **This says
635
1031
  nothing about how the variant resolved**, and `KNOWN-LIMITATIONS.md` now discloses why: a caller's
636
- `type` option wins first, and absent one, where `ST-03` names none of the three known
637
- implementation conventions, the reader falls back to the first `SVx` in the transaction body,
1032
+ `type` option wins first, and absent one, where `ST-03` names no implementation convention this
1033
+ reader recognises, the reader falls back to the first `SVx` in the transaction body,
638
1034
  orphans included, so a stray `SV2` re-types the whole submission. That is pre-existing behaviour, measured identical at `0.0.10`, and is deliberately
639
1035
  not narrowed here.
640
1036