@cosyte/mllp 0.0.9 → 0.0.11

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
@@ -1,20 +1,221 @@
1
1
  # Changelog
2
2
 
3
- All notable changes to this project will be documented in this file.
4
-
5
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
- and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
- Versions and publishing are managed with [Changesets](https://github.com/changesets/changesets);
8
- this file is maintained by hand (Changesets handles the version bump and publish only).
9
-
10
- ## [Unreleased]
11
-
12
- `@cosyte/mllp` is published on npm and stays on the `0.0.x`-until-first-alpha ladder, so the API can
13
- change with no deprecation cycle. The sentence this replaces said the first pre-alpha release "will
14
- ship" the surface below, which stopped being true the day the package first published and had been
15
- false ever since. **No version literal is written here, on purpose**: what is published is a fact
16
- about the registry (`npm view @cosyte/mllp version`) and about `package.json`, and a number copied
17
- into prose here is a claim that goes stale on its own.
3
+ ## 0.0.11
4
+ ### Patch Changes
5
+
6
+ - aa8ea34: Gate this repository's two-file guidance contract, so a dangling pointer or an emptied section cannot land unnoticed.
7
+
8
+ `scripts/check-agent-notes.ts` (`pnpm check:agent-notes`) checks three things: that
9
+ `documentation/agent-notes.md` is tracked, that every section under a heading has a body, and that
10
+ every pointer at it, in a file the check opened, resolves to an anchor GitHub would actually mint.
11
+ A heading immediately followed by a deeper one is a container whose body is its subsections, so it
12
+ is not reported; the obligation moves down to the deeper heading, which means an emptied leaf is
13
+ still a finding and a trailing heading is never a container.
14
+ Splitting the guidance in two moved the reasoning behind a link, which made the link load-bearing,
15
+ and nothing
16
+ checked it. A rename, an emptied section, or an anchor edited on one side of the pair and not the
17
+ other would all have been silent.
18
+
19
+ The check is named for what it checks and asserts no universal. Measured across the sibling
20
+ repositories on 2026-08-06, three carry no such file at all, so a check written as though every
21
+ repository kept the same contract would assert something those three disprove. This one asserts this
22
+ repository's promise only, and it runs in this repository's own tests.
23
+
24
+ It refuses rather than reporting clean over a corpus it never opened. There is no declared root to
25
+ be wrong about: the file list comes from `git ls-files`, every path in it is opened or refused, and
26
+ the only silent skip is a file containing a NUL byte, which is counted and named on the success
27
+ line as a disclosed miss rather than a pass. It exits 2 when nothing is tracked, when a tracked
28
+ path is missing, unreadable, a symlink or not a regular file, when a path is unmerged, and when it
29
+ finds no pointers at all. A finding exits 1 and a refusal exits 2, so a broken checker can never be
30
+ read as a list of real findings.
31
+
32
+ Each tracked path is resolved exactly once, by a single open, and the symlink and regular-file
33
+ questions are asked of the resulting descriptor rather than of the path a second time. Checking a
34
+ path and then reading it again is a time-of-check/time-of-use race, and the refusal it threatens is
35
+ the symlink one, which is what stops bytes from outside the tree being scanned under a tracked
36
+ path's name. Opening with the no-follow flag makes that refusal part of the open itself, and the
37
+ non-blocking flag is what keeps a tracked path replaced by a FIFO from hanging the check instead of
38
+ refusing it.
39
+
40
+ Both heading shapes that defeat a naive leading-hash rule are handled and reproduced end to end: a
41
+ heading indented by a space, and one underlined rather than hashed. Both are false-red bypasses,
42
+ because a missed heading is a missing anchor. So is the opposite direction, where a hash line inside
43
+ a code fence, or a YAML front-matter key, must not mint an anchor or a dangling pointer would pass.
44
+ The slug transformation is measured against github-slugger rather than assumed, including four
45
+ shapes that diverge from the obvious implementation: a dropped leading character leaves a leading
46
+ hyphen behind, a repeated heading is re-suffixed until its slug is free, a space separator other
47
+ than the plain one is deleted, and a heading wrapped across two lines has the break deleted rather
48
+ than turned into a hyphen.
49
+
50
+ Measured against the parent commit by running it there: zero violations. This changes no content and
51
+ exists to stop a regression. `test/scripts/agent-notes.test.ts` runs it against this tree, seeds each
52
+ violation class into throwaway repositories, pins each disclosed miss that has anything to execute in
53
+ the direction it actually fails, and includes the control that a check pointed at nothing must refuse
54
+ rather than report clean. Two of the disclosed misses are statements of scope rather than behaviour,
55
+ so there is nothing to run for them and none is claimed.
56
+
57
+ Repository tooling and guidance only. No runtime code, no public API, no warning code, and no
58
+ framing or transport behaviour changed.
59
+ - 4cc0776: The README lockup now links to cosyte.com (`ASSETS`).
60
+
61
+ The `<picture>` block above the H1 is wrapped in an anchor to https://cosyte.com, per the founder
62
+ requirement of 2026-08-06. Nothing inside the block moved: the `<source>`, the `<img>`, the alt text
63
+ and both tile URLs are byte-identical.
64
+
65
+ What the anchor does was measured on both surfaces by `fhir`, not assumed, because fourteen READMEs
66
+ carry this shape. On GitHub the anchor works and the colour-scheme switch keeps working, because the
67
+ `<img>` stays a direct child of `<picture>`, which is the condition the HTML spec puts on `<source>`
68
+ applying at all. On an npm package page the anchor is lost: npm wraps a README image in its own
69
+ anchor to the image file, a nested anchor is not representable, so the parser closes ours early and
70
+ the image ends up linked to the image file rather than to cosyte.com. Shipped anyway by founder
71
+ decision of 2026-08-07: on npm that is no worse than the unlinked lockup it replaces, and GitHub is
72
+ where these READMEs are read.
73
+
74
+ ## 0.0.10
75
+ ### Patch Changes
76
+
77
+ - 5dbc828: `CHANGELOG.md` ships inside the tarball and is now written by the release rather than by hand, so it stops describing this package's already-published code as unreleased and starts carrying a version heading for every release.
78
+
79
+ `.changeset/config.json` set `"changelog": false`, which is a legal value that makes Changesets bump
80
+ the version and write no changelog at all. So no release ever wrote a version heading here, and
81
+ nothing ever rolled `[Unreleased]` over. Every published version of this package carried a changelog
82
+ with **no version headings in it**: one `[Unreleased]` heading spanning the entire history, and a
83
+ preamble in the future tense about a release that had already gone out. `CHANGELOG.md` is listed in
84
+ `package.json` `files`, so that was text on the disk of everyone who installed the package, not
85
+ internal bookkeeping.
86
+
87
+ **The flag is what changed, not the prose.** Correcting the sentence by hand leaves the mechanism
88
+ that wrote it, and the next release drifts the same way. `changelog` now names the generator that
89
+ ships with Changesets, so a release writes its own version heading and its own entry from the
90
+ changesets it consumed, and **the changeset summary is now the changelog entry**. Nothing new is
91
+ depended on: the generator is an entry point of `@changesets/cli`, already a dev dependency.
92
+
93
+ **The file's shape changed with it, deliberately.** Changesets prepends a release by replacing the
94
+ first newline in the file, so exactly one line can sit above generated output. The hand-written
95
+ preamble sat on line 3, which means a release would have inserted itself between the heading and the
96
+ preamble and split the header in two. The hand-maintained history has therefore moved under a
97
+ `## Released before this file was generated` heading, with an accurate preamble in place of the old
98
+ one. Four pieces of hand-workflow scaffolding were dropped and no entry was reworded or re-sorted:
99
+ the `[Unreleased]` heading, its link definition at the foot of the file, and the two empty section
100
+ stubs waiting for the next hand-written entry. The history is left as it was written rather than
101
+ split into version sections, because the file never recorded which release any entry went out in
102
+ and that text is already on disk in published copies.
103
+
104
+ **The release's Prettier pass is turned off, and that is specific to this package.** Changesets runs
105
+ the document it writes through Prettier unless `"prettier": false` says otherwise. This repo's
106
+ markdown is deliberately not Prettier-managed: `.prettierignore` lists `*.md`, so the archived
107
+ history has never been Prettier-canonical, and leaving the pass on would not tidy it but would
108
+ rewrite already-published text on every release. Measured on this archive: emphasis markers, list
109
+ bullets and continuation indents all move, and one paragraph whose bold span contains an inline code
110
+ literal ending in `**` comes back with the spaces around that literal eaten, which is corruption
111
+ inside a shipped tarball rather than a formatting preference. A sibling package that does
112
+ Prettier-manage its markdown needs the opposite setting, so this is not a value to copy between
113
+ repos.
114
+
115
+ Pinned by `test/scripts/changelog-generation.test.ts`, which runs the real `changeset version`
116
+ against the real `CHANGELOG.md` and the real config in a throwaway package rather than
117
+ reimplementing where the tool inserts text, and which builds that config from the committed one so a
118
+ setting added later is exercised instead of silently diverging. Ten of its thirteen cases are red
119
+ against the previous state, measured on the tree this change was written against rather than
120
+ recalled. **The rule it enforces is that nothing but the H1 sits above the first heading, and it is
121
+ asserted on the released document as well as the committed one**: a rule phrased as "the archive
122
+ heading comes second" holds only until the first release writes its own version heading there, which
123
+ would have redded the first Version PR this configuration ever opened, and `prepublishOnly` runs the
124
+ same suite under `changeset publish`. Version headings are compared as whole headings and never as
125
+ substrings, which the suite demonstrates rather than asserts: on a released document whose only
126
+ version heading is `## 0.0.10`, a substring search for `## 0.0.1` answers yes to a heading that is
127
+ not there. Three controls: the same inputs with `"changelog": false` must write no version heading
128
+ at all, so the flag is proved load-bearing rather than incidental; the same inputs with the Prettier
129
+ pass back on must change the archived history, so that setting is proved load-bearing too; and the
130
+ old file shape must reproduce the split header, so the shape rule is demonstrated rather than
131
+ asserted.
132
+
133
+ One upstream behaviour is worth knowing before debugging a release, and is recorded in that file:
134
+ Changesets wraps the changelog write in a try/catch that only warns. A tree whose declared Prettier
135
+ config cannot be resolved bumps the version, consumes the changeset, and writes no changelog at all.
136
+ Reproduced here on this repo's inputs. A release that publishes with an unchanged changelog is that
137
+ failure, not a setting that quietly reverted.
138
+
139
+ `.changeset/README.md` said to keep the release notes in `CHANGELOG.md` by hand and now says to
140
+ write them in the changeset. No runtime code, no public API, no warning code, no framing or
141
+ transport behaviour changed.
142
+ - b713572: Scan the test sources the PHI commit-gate was never opening, and stop one healthy scan root
143
+ vouching for an empty one. Tooling and documentation only: no runtime code, no public API and no
144
+ published artifact changes.
145
+
146
+ **A `.ts` source under the fixture root was scanned by neither route.** The walk covered all of
147
+ `test/`, but the read filter then dropped every `.ts` file under it, and this repo's fixture corpus
148
+ is overwhelmingly `.ts`: 72 of the 76 tracked files under `test/` were removed from the gate,
149
+ leaving it standing on three framed binaries, and twelve of the 72 carried inline `PID|` literals.
150
+ This is an HL7 transport package, so a message written straight into a test source is the most
151
+ common fixture shape here, not an exotic one.
152
+
153
+ **The remedy is two-sided, and either half alone would have shipped nothing.** Widening the
154
+ enumeration is the visible half: a blanket extension rule cannot tell a file that carries violator
155
+ literals on purpose from one that carries them by accident, so it is replaced by an explicit
156
+ per-path exemption, which is a reviewed act exactly like adding an allow-list token. The half that
157
+ actually finds anything is recognition. Every detector in the scanner assumed the file **is** the
158
+ document and worked from a segment id at the start of a line, so in a source whose lines start with
159
+ `const` or with a quote it matched nothing: a probe carrying a full patient identity in a string
160
+ literal exited 0 "OK, no hits" even when named explicitly on the command line, which bypasses the
161
+ enumeration entirely, while the identical payload written to a message file reported all five
162
+ fields. Embedded segments are now recovered out of string literals, with TypeScript escapes
163
+ resolved so the encoding characters an HL7 header declares are the ones the scanner reads, and with
164
+ a template placeholder neither guessed at nor dropped. The recogniser anchors on the HL7 field
165
+ separator: anchoring on any delimiter turns prose and identifiers into segments and would have
166
+ driven the name backstop over English words, and a gate that reds on prose is a gate someone turns
167
+ off. Five synthetic tokens the widening made visible for
168
+ the first time, including a deliberate leak canary, are now declared in the allow-list.
169
+
170
+ **A sweep that observed nothing under one root still reported OK.** The guard that refuses an
171
+ empty sweep counted every root together, so it could only fire when all of them came back empty and
172
+ one healthy root masked an empty one indefinitely: with the fixture root emptied, the run printed
173
+ "OK, no hits" and exited 0 on the strength of the source root alone, under a comment asserting the
174
+ sweep "always reaches" the fixture corpus that nothing checked. A denominator would not have caught
175
+ it and would have looked like a fix, because a count counts the roots that did exist. The check is
176
+ now per root and keyed on the roots the walk actually entered, so an absent root stays legitimate
177
+ while an emptied one refuses.
178
+
179
+ **An unmerged path was enumerated by nothing.** It has no single staged blob, so the status filter
180
+ deletes the record and the pre-commit route printed "OK, no hits" over a conflicted fixture whose
181
+ both sides carried live-shaped identifiers. It now refuses, because neither side of a conflict is
182
+ what a commit would contain, so reading one would be a claim about content that may never exist.
183
+ Minor, and honestly so: the commit itself is refused earlier by git, so this is not a route by
184
+ which anything reaches a commit. What it fixes is the gate answering a question it cannot answer.
185
+
186
+ 17 new cases, 11 of which run red against the base scanner; the other six are controls that pin
187
+ the absence of false positives, the `src/` decision that this work deliberately does not reverse,
188
+ and the recogniser's disclosed limits.
189
+
190
+ ## Released before this file was generated
191
+
192
+ Every release section above this heading is written by
193
+ [Changesets](https://github.com/changesets/changesets) from the changesets in `.changeset/`, newest
194
+ release first. The release writes its own version heading, so nothing above this line is maintained
195
+ by hand, and a change is recorded by adding a changeset rather than by editing this file.
196
+
197
+ Everything below this heading was maintained by hand. It sat under a single `[Unreleased]` heading
198
+ that no release ever rolled over, which is why a published tarball went on describing its own
199
+ already-shipped contents as unreleased. That is also why the sections below repeat: entries were
200
+ appended in waves under a fresh set of `### Added` / `### Fixed` headings, and the file never
201
+ recorded which release any wave went out in. The history is therefore left exactly as it was
202
+ written rather than re-sorted into version sections, because there is nothing in it to sort by, and
203
+ because this is the text that installed copies already carry on disk.
204
+
205
+ Only four things were dropped, all of them scaffolding for the hand-written workflow that no longer
206
+ runs: the `[Unreleased]` heading itself, its link definition at the foot of the file, and the two
207
+ empty section stubs (`### Deprecated` and the final `### Security`) that were waiting to receive the
208
+ next hand-written entry. A note that used to sit under `[Unreleased]`, explaining that an earlier
209
+ future-tense sentence had been corrected in place, is replaced by this heading and these
210
+ paragraphs. No entry was reworded.
211
+
212
+ The entries below follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); the generated
213
+ sections above use the format Changesets writes, which is a version heading and a list of the
214
+ changes that release consumed. Versions follow the cosyte pre-alpha ladder, `0.0.x` until first
215
+ alpha, rather than [Semantic Versioning](https://semver.org/spec/v2.0.0.html) alone, so the API can
216
+ change with no deprecation cycle. **No version literal is written in this preamble, on purpose**:
217
+ what is published is a fact about the registry (`npm view @cosyte/mllp version`) and about
218
+ `package.json`, and a number copied into prose here is a claim that goes stale on its own.
18
219
 
19
220
  ### Added
20
221
 
@@ -80,6 +281,65 @@ into prose here is a claim that goes stale on its own.
80
281
 
81
282
  ### Security
82
283
 
284
+ - **The PHI scanner reads the test sources it was never opening, and refuses per walk root
285
+ (`PHI-SCAN-WALK-ROOT-SCOPE`, `PHI-SCAN-OBSERVED-NOTHING-IS-GLOBAL`).** Both reproduced on
286
+ `3daf2e9` before anything was touched. This repo's scope was **re-derived, not ported**: its walk
287
+ roots are `test/` and `src/` (a sibling roots at `test/fixtures/` instead), and the two refusal
288
+ defects a sibling still carries are **not open here**, because a regular-file root, a dangling
289
+ link at a root and a missing allow-list already exit **2**.
290
+
291
+ **The `.ts` hole.** `test/` was walked in full and then every `.ts` file under it was dropped by
292
+ the read filter, so a tracked test source reached **neither** route. Measured: `test/` tracked 72
293
+ `.ts` files, 3 `.frame.bin` and 1 `.md`, so the exclusion removed **72 of 76** tracked files and
294
+ left the gate standing on three framed binaries; **twelve** of the 72 carried inline `PID|`
295
+ literals. For a transport package whose subject matter is HL7 v2, a message written straight into
296
+ a test source is the ordinary fixture shape.
297
+
298
+ **The remedy is two-sided, and the enumeration is the half that finds nothing on its own.** A
299
+ blanket extension rule cannot distinguish a file carrying violator literals **on purpose** from
300
+ one carrying them **by accident**, so it is replaced by an explicit per-path exemption. But every
301
+ detector assumed **the file is the document** and worked from a segment id at the START of a
302
+ line, which in a source beginning with `const` or a quote matches nothing: measured, a probe
303
+ carrying a full `PID` in a string literal exited **0** `OK, no hits` even when **named explicitly
304
+ on argv**, which bypasses the enumeration entirely, while the identical payload written to a
305
+ `.hl7` file reported all five fields (`PID-3` / `PID-5` / `PID-7` / `PID-11` and the address).
306
+ Segments are now recovered from string literals with TypeScript escapes resolved, so a doubled
307
+ backslash in a header yields the real encoding characters, and a template placeholder is replaced
308
+ by a non-letter, non-digit token rather than guessed at or dropped. The recogniser anchors on the
309
+ HL7 **field separator**: anchoring on any delimiter, which is what the file-level rule does,
310
+ matched prose and identifiers (`ack-from-hl7`, `ERR_MODULE_NOT_FOUND`, `net.createConnection`),
311
+ which would have driven the unknown-segment name backstop over English words. **No match count is
312
+ recorded, on purpose:** that figure was wrong four times and is not reproducible by an independent
313
+ reader, because raw anchor matches are an upper bound on extractor runs rather than the same
314
+ quantity. The derivation command is in `documentation/agent-notes.md`. On this corpus every
315
+ recovered id is a real segment id,
316
+ **which is an observation about the tree and not a property of the rule**: the recogniser cannot
317
+ tell a comment from a literal, and a clean Z-segment written in one does red. The anchor covers
318
+ four spellings, including a **real newline**, without which the idiomatic multi-line template
319
+ literal yielded only its header run, and header segments are skipped, so a full patient identity
320
+ on the following lines reported clean. A run ends on the quote that OPENED it, because ending on
321
+ any quote truncated a name at an apostrophe and silently dropped every field after it. Five
322
+ synthetic tokens surfaced for the first time, including a deliberate leak canary, are now declared
323
+ in `scripts/phi-allow-list.txt`. Disclosed cost, three things rather than one: a custom field
324
+ separator is not recovered at all, an escape-anchored run still ends at the first quote of any
325
+ kind, and a segment split across a concatenation is recovered only as far as the first operand.
326
+
327
+ **The observed-nothing guard was global.** It counted every root together, so it could fire only
328
+ when all of them came back empty and one healthy root masked an empty one indefinitely. Measured:
329
+ with `test/` emptied and `src/` intact, the sweep printed `OK, no hits` and exited **0**, under a
330
+ comment asserting that `all` mode "always reaches" the fixture corpus, which nothing checked. **A
331
+ denominator is not the remedy and would have looked like one**, because a count counts the roots
332
+ that did exist. The check is now per root and keyed on the roots the walk actually entered, so an
333
+ **absent** root stays legitimate while an **emptied** one refuses (exit 2, naming the root).
334
+
335
+ **An unmerged path was enumerated by nothing.** It has no single staged blob, so
336
+ `--diff-filter=AMT` deletes the record; measured, `--staged` exited **0** over a conflicted
337
+ fixture whose both stages carried live-shaped `PID-3` / `PID-5` / `PID-7` values. It refuses now,
338
+ and reading a stage is refused as the remedy because neither side of a conflict is what a commit
339
+ would contain. Scoped honestly as **minor**: `git commit` refuses an unmerged index before the
340
+ hook runs, so this is not a route by which anything reaches a commit, and what it fixes is the
341
+ gate answering a question it cannot answer.
342
+
83
343
  - **The PHI scanner enumerates a staged rename, reads a blob that replaced a walk root, and
84
344
  refuses a root that is not a directory (`PHI-SCAN-RENAME-BLIND-AT-PRECOMMIT`).** Three holes, two
85
345
  of them on `--staged`, which is this repo's **pre-commit** gate. All three were reproduced on
@@ -1170,8 +1430,6 @@ into prose here is a claim that goes stale on its own.
1170
1430
 
1171
1431
  - **`mitata` benchmark dependency and the `bench` script**: the script had no benchmark files.
1172
1432
 
1173
- ### Deprecated
1174
-
1175
1433
  ### Fixed
1176
1434
 
1177
1435
  - **Bind errors no longer crash a server with no `'error'` listener (Phase 8 residuals, MLLP-8.1).**
@@ -1201,7 +1459,3 @@ into prose here is a claim that goes stale on its own.
1201
1459
  `'listening'`/`'securityWarning'` handler is deliberately **too late**: the bind wins and
1202
1460
  `listen()` resolves (use `close()` to shut down), so aborted-mid-emit can never strand
1203
1461
  `listening: true` on a closed socket.
1204
-
1205
- ### Security
1206
-
1207
- [Unreleased]: https://github.com/cosyte/mllp/commits/main
package/README.md CHANGED
@@ -1,7 +1,9 @@
1
- <picture>
2
- <source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
3
- <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">
4
- </picture>
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/mllp
7
9
 
package/dist/index.cjs CHANGED
@@ -3438,7 +3438,7 @@ async function createStarterClient(opts) {
3438
3438
  }
3439
3439
 
3440
3440
  // src/index.ts
3441
- var VERSION = "0.0.9";
3441
+ var VERSION = "0.0.11";
3442
3442
 
3443
3443
  exports.Connection = Connection;
3444
3444
  exports.FrameReader = FrameReader;