Version Packages - #24
Merged
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR was opened by the Changesets release GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or setup this action to publish automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated.
Releases
@cosyte/cli@0.0.2
Changelog
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog,
and this project adheres to Semantic Versioning.
Versions and publishing are managed with Changesets;
this file is maintained by hand (Changesets handles the version bump and publish only).
Unreleased
Fixed
0.0.1is published and uninstallable (ASSETS-P8).0.0.1published on 2026-07-29 with all ten
file:vendor/*.tgzdependency specifiers intact.vendor/isnot in
filesand there is nobundledDependencies, so every install route (npm i,npm i -g,npx) fails withENOENTonnode_modules/@cosyte/cli/vendor/cosyte-fhir-0.0.0.tgz. Reproducedin a clean directory. A published version is immutable (ADR 0001), so
0.0.1stays broken and thefix must ship as a later version.
README.mdgains a "Known issue" section stating what fails, the exact error, why, and thata source checkout is the only workaround.
docs-content/installation.mdgains the same under"Installing fails today". Both copies of the broken
npx-based MCP registration snippet areannotated (
README.mdanddocs-content/mcp.md).README.mdsaid "not yet published to npm"; it ispublished. (2) It described swapping the vendored sibling deps for real
@cosyte/*npm ranges asa step still to come "at that flip"; the flip already happened without the swap, which is the
defect. (3) The same "not yet published" claim appeared in three
docs-content/pages. (4)The
redact/deidterminal diagnostic, the--helptext, three JSDoc blocks that compile intodist/*.d.ts, and five docs pages said@cosyte/deidwas unpublished, unshipped, or unbuilt;it is published at
0.0.2, and the accurate statement is that the CLI does not wire it yet. Everycount here was re-derived by census of the base tree, not estimated.
CosyteFormatandOP_SUPPORTbothenumerate exactly eight.
RELEASING.mdrecords that the documented dependency-swap step was skipped, that a greennpm publish --dry-runcannot catch this (it packs a tarball but never resolves its deps from aregistry), and adds a checklist step to install the published version from outside the repo.
RELEASING.mdand verified against theregistry.
@cosyte/hl7(0.0.3),@cosyte/terminology(0.0.4) and all six breadth parserswould swap to real ranges today.
@cosyte/fhiris unpublished (FHIR-NPM-NAME, an npm E403name-similarity rejection) and
@cosyte/transform@0.0.2failsE404on its@cosyte/fhirpeer,so neither can. An installable release is nonetheless reachable before that unblocks, because npm
tolerates an
optionalDependencythat fails to resolve (measured). It needs a code change first:@cosyte/fhirand@cosyte/transformare imported with a rawawait import(), so they must berouted through a guarded loader to degrade to
CLI_PARSER_UNAVAILABLE(exit69) rather thancrash.
loadOptional()cannot be reused unchanged: it takes aCosyteFormat, and"transform"isnot one, and its diagnostic hardcodes the word "parser". Not undertaken here.
Added
README.mdnow opens with the shared Cosyte lockup in a<picture>block, above the H1. Thedark-ground tile (
cosyte-lockup-tile-on-dark-1200x300.png) sits behind aprefers-color-scheme: darkmedia query and the light-ground tile(
cosyte-lockup-tile-on-light-1200x300.png) is the inner<img>fallback, so the mark is read ona ground that matches the page it is read on. The
# @cosyte/cliheading and the blockquote underit are unchanged: the artwork reads "Cosyte" and the heading names the package, so nothing on the
page is duplicated. Both URLs were re-checked immediately before the push and returned
200 image/png(10513 bytes dark, 10455 light). The block was copied byte for byte out of@cosyte/hl7'sREADME.mdrather than retyped and diffed against it, because a transcriptionerror in one of these URLs is a broken image on a public package page.
This replaces the per-package banner added earlier in the same unreleased window, and the reason
it replaces it is recorded rather than dropped. That banner was a plain markdown image, chosen
over
<img>or<picture>on the stated ground that whether npm's markdown sanitizer preserves a<picture>element was unverified. That was an accurate account of what was known when it waswritten, and it has since been measured: GitHub honours the
prefers-color-schemeswitch(observed on
@cosyte/astmin dark mode, where the rendered image'scurrentSrcresolves to theon-dark tile and its parent element is
PICTURE), and on the npm package page the<img>ishoisted out of its
<picture>by the anchor wrapper rather than the element being stripped, sothe light cut renders, which is the correct one there because npmjs.com has no dark mode. Those two
measurements were taken on
astmand reported into this repo rather than re-taken here; what wasre-checked directly for
cliis that both tile URLs return200 image/png. The failure mode issafe either way: a renderer that strips
<source>renders the inner<img>, so the worst case isa light-ground mark on a dark page, never a missing or broken image.
Why this is corrected rather than annotated in place.
0.0.1published on 2026-07-29, a daybefore the banner landed, so no
@cosyte/clitarball has ever carried the banner or the sentenceannouncing it. Annotating an entry no consumer received would publish an addition and its
replacement as two changes when only one was ever visible, so the banner entry is removed from this
release and its reasoning is carried here instead of being silently reversed.
The alt text describes the artwork, a plus mark set in two overlapping rounded squares beside the
Cosyte wordmark, rather than the package. It is what a screen reader on the npm page reads out and
what a reader gets when the image fails to load, so repeating the
@cosyte/cliheading below itwould be a wasted line. The wording is the one eight sibling packages already carry, confirmed
against both rendered PNGs here rather than copied on trust.
Phase 7: release hardening (the final roadmap phase; the CLI is feature-complete). No new
runtime command surface: this phase is publish-readiness.
test/fuzz.property.test.tsfuzzes the terminal(
run, over arbitrary argv vectors + stdin bytes) and the agent surface (dispatchTool, over anarbitrary tool name + arguments), asserting neither ever throws an unhandled exception, always
resolves to a documented exit code, and never leaks a raw stack frame onto a secondary channel. The
case count scales via
CLI_FUZZ_RUNS; a scheduled Fuzz workflow (.github/workflows/fuzz.yml)runs it nightly at a high count, and
pnpm test:fuzzruns it on demand.test/exit-code-matrix.test.tslocks one representative invocation forevery code in the
0/1/2/65/66/69/70contract, driven end-to-end throughrun, so a regression thatturns an invalid-input exit
1into a0(or renumbers a code) fails CI. The exit-code map and thestable
CLI_*diagnostic codes are a stability surface: renaming one is a breaking change.smokegate (scripts/smoke.mjs, wired intoverify.sh)exercises the built package (the dual ESM/CJS
.and./mcpsubpath exports, and bothcosyte/cosyte-mcpbins undernode) andnpm publish --dry-runassembles a clean tarball(
dist+README/LICENSE/CHANGELOG).attwremains a publish gate.docs-content/limitations.md(wraps-not-implements, the non-goals, thehonest per-(format, operation) support matrix, the PHI-default posture), a man-page-style
docs-content/reference-commands.md, andRELEASING.md(the one-package-two-bins publish,provenance/OIDC, the vendored-
file:→npm dep swap, and the two standing founder stops).Phase 6: six more formats + streaming + shell completion (ADR 0025). The
cosyteCLI now wrapsall eight cosyte formats, routed through a single lazy per-format adapter registry
(
src/core/parsers.ts) that replaces the old per-commandhl7 ? : fhirbranches and makes supportper (format, operation). An unsupported (format, op) is a value-free
CLI_FORMAT_UNSUPPORTED,never a fake (ADR 0018).
x12,astm,ncpdp(SCRIPT) → parse · inspect ·fmt · validate;
ccda→ inspect · fmt (XML re-serialize) · validate (parsedeferred, nolibrary-blessed JSON model; XML is the canonical form);
dicom→ inspect · validate (parse/fmtdeferred. The model is binary);
mllp→ parse · inspect (a transport container the CLI de-framesto its enclosed HL7 message(s)). Content autodetection now covers all eight (conservative + disjoint:
a leading
0x0BVT byte routes tomllp,ISA→x12, anH-record→astm,<ClinicalDocument>→ccda,a
<Message>in the NCPDP namespace→ncpdp,DICM@128→dicom);--formatacceptsmllp.parseemits NDJSON with per-record isolation for inherentlymulti-record inputs: an MLLP stream (one record per frame) and any input under the new
--ndjsonflag (one record per non-empty line: the FHIR bulk-data convention). A record thatfails to parse becomes a value-free
{ record, error }line and the stream continues; the overallexit is a data error (
65) if any record failed. A single message is unchanged (one pretty, or--jsoncompact, envelope + a value-free warning-count note).cosyte completion <bash|zsh|fish>prints a static, value-free completionscript generated from the command tree.
optionalDependencies, lazy-loaded per format and outside the hard-runtime-dep closure(ADR 0025, mirroring the MCP SDK isolation of ADR 0024). An absent optional parser degrades to a
value-free
CLI_PARSER_UNAVAILABLE(exit69), never a crash. Pinned sibling commits: dicomd1ed590, x120c60606, ccda3753216, ncpdp184eecc, astm92ac210, mllpaecff75(allv0.0.1 except astm records-layer). Third-party CLI-core runtime deps stay zero.
CLI_PARSER_UNAVAILABLE(exit69); the exit-code contract is otherwiseunchanged (
0/1/2/65/66/69/70). New value-free inspect summaries per format; new programmaticexports (
OP_SUPPORT,supportsOp,formatsSupporting,parseFormat,inspectFormat,fmtFormat,validateFormat,deframeMllp,loadOptional,valueFreeLocator,DETECTABLE_FORMATS,completionCommand, and the result/summary types). The publicWIRED_FORMATSset is removed infavour of the per-op
OP_SUPPORTmatrix (pre-alpha0.0.xsurface change).Phase 5: the
cosyte-mcpMCP server (the agent front door). A stdio Model Context Protocolserver that exposes the shared command core to an LLM/agent as callable tools: the second adapter
over one core (ADR 0022, 0024). Reachable three ways: the new
cosyte-mcpbin, thecosyte mcpsubcommand, and the
@cosyte/cli/mcpsubpath export. Tools:parse,validate,inspect, andconvert, each a thin wrapper that calls the same command handler the terminaluses (with
--json), socosyte parseand the MCPparsetool agree by construction; the CLIre-implements nothing.
no
--unsafe-show-valuesdoor on the agent surface. A tool result carries the requested data(the parsed model / converted Bundle: the explicit request); a tool error carries only the value-
free diagnostic (a stable code + positional context), never an input value. A parsed-but-invalid
validateverdict is a successful call reporting the verdict, not a tool error; only a hardfailure (unparseable / no input / usage) sets
isError.@modelcontextprotocol/sdk(the CLI'sfirst and only third-party runtime dependency) is declared in
optionalDependencies(pinned1.29.0) and imported only insrc/mcp/server.ts, reachable solely via the./mcpboundary(the subpath, the
cosyte-mcpbin, and a dynamicimport()on thecosyte mcpbranch). Acosyte parseinvocation never loads it; the core works with the SDK absent (--omit=optional). Because itis not part of the hard runtime closure, the umbrella
verify-policy.jsoncap oncliruntimedependenciesstays 4: unchanged. A static isolation test proves nocore/commandsmoduleimports the SDK.
@cosyte/cli/mcpand newcosyte-mcpbin; new programmatic exports(
createMcpServer,startStdioServer,dispatchTool,TOOL_DEFS, and the MCP result types) on the./mcpsubpath.redact/deid(gated on@cosyte/deid) andmap-codesare deliberately notexposed as tools yet.
Phase 4:
convert/map-codes(the consumer-of-consumers commands). Two commands that wrapthe higher-layer libraries; the CLI adds no mapping or terminology logic of its own.
convert <file|-> --to fhir [--json] [--quiet]: HL7 v2 → FHIR R4 via@cosyte/transform. Parses the input with@cosyte/hl7, hands the parsed message totransform.toFhir, and emits the serialized FHIR messageBundle(the library's canonicalserialization) on stdout:
cosyte convertequalstransform's programmatic output. Theconversion's value-free issues (a stable code + a v2-index → FHIRPath locator, never a field value)
render on stderr (or as a JSON envelope under
--json);--quietsuppresses them. Theload-bearing rule mirrors
validate: an error-severity transform issue drives exit1,never
0.--to fhiris required (the only target); a non-HL7 source (e.g. a FHIR document) isa value-free
CLI_FORMAT_UNSUPPORTEDdata error (65), never a fake conversion; an unparseableHL7 input is
CLI_PARSE_FAILED(65).map-codes <conceptmap|-> --code <code> [--system <uri>] [--version] [--display] [--json] [--quiet]: translate a single source coding through a BYO FHIR R4 ConceptMap via@cosyte/terminology($translate). The positional is the ConceptMap document; the sourcecoding is named by flags. A ConceptMap and a code are reference data, not PHI, so the
translation result goes to stdout: a match → the target coding(s) + exit
0; anunmapped code → the never-fabricate
TERM_TRANSLATE_UNMAPPEDsignal + exit1. A map thatis not valid JSON or not a loadable ConceptMap is the new value-free
CLI_MAP_INVALIDdataerror (
65), surfacing the stable terminology-loader code (e.g.TERM_CONCEPTMAP_MALFORMED),never the map's bytes.
CLI_MAP_INVALIDdiagnostic code. New programmatic exports:convertCommand,convertOutcome,mapCodesCommand. New runtime dependencies (ADR 0023):@cosyte/transform(
e6c4531, v0.0.0) and@cosyte/terminology(e5ed368, v0.0.1) as hard, first-party,lazy-loaded deps: vendored as
pnpm packtarballs undervendor/until PUB-FLIP(
pnpm vendor:refresh; umbrella ADR 0008). The umbrellaverify-policy.jsoncap oncliruntimedeps was raised 2 → 4; third-party CLI-core runtime deps stay zero (both siblings are
lazy-loaded per command, so the
parsefast path never loads them).0023: wire@cosyte/transform+@cosyte/terminology; the deliberate 2 → 4 cap raise(amends ADR 0021).
Phase 3:
validate/inspect/fmt. Three commands over the two wired parsers(HL7 v2 + FHIR R4), each a thin wrapper that re-implements no library logic.
validate <file|-> [--profile] [--json] [--quiet]: parse + run the wrapped parser's ownvalidation surface, with the verdict in the exit code:
0valid,1invalid (parseablebut non-conformant),
65unparseable,66no input,2usage. The load-bearing rule: avalidation failure is never exit
0; "unparseable" (65) is a distinct signal from "parsedbut invalid" (
1). Findings are value-free: a stable code, a severity, and a positionallocator (a FHIRPath, or an HL7 segment/field index), on stderr by default, or as value-free JSON
on stdout under
--json;--quietmakes the exit code the whole signal. The CLI invents noverdict: FHIR validity is
@cosyte/fhir'svalidateResource().valid(plus any error-severityread issue); HL7 validity is "parseable" (its warnings are non-fatal by the library's design:
surfaced, never failing).
--profileis gated to an honestCLI_NOT_IMPLEMENTED(exit69):the CLI bundles no profiles yet, so it never fakes or silently drops a profile verdict.
inspect <file|-> [--json]: a value-free structural summary: HL7 message type, version,per-segment-type counts, and a warning count; FHIR
resourceType, Bundle entry counts by type, anda read-issue count. Counts and structural type codes only, never a field value.
fmt <file|->: canonical re-serialization via the wrapped library's spec-cleanserializer (
Hl7Message.toString()/serializeResource); its stdout is the data channel. Anunparseable input is a data error (
65) with no partial emit.EXIT.INVALID(1): thevalidateverdict code (the exit-code contract is now0/1/2/65/66/69/70). All four commands share one input + format front door (core/resolveInput)and one value-free parser-failure boundary (
core/wrap), so the value-free-by-default posture andthe
--unsafe-show-valueschokepoint apply uniformly;parsewas refactored onto the sharedhelpers (behavior-preserving). New programmatic exports:
validateCommand,inspectCommand,fmtCommand,resolveInput,parseFailureResult,formatHl7Position,errorResult. No newruntime dependencies: stays within the cap of 2.
Phase 2: PHI posture hardened +
redact/deid+--unsafe-show-values.--unsafe-show-values: a global, opt-in, PHI-exposing flag, resolved once and order-independently and funnelled through a single chokepoint (
core/phi.ts), so the "a valuereaches a secondary surface iff the flag is set" property holds in one place. Off by default;
with it set, a
CLI_PARSE_FAILEDdiagnostic appends a bounded, single-line excerpt of theoffending input. Every other surface stays value-free, and a successful parse still puts values
only on the stdout data channel.
redact/deid(<file|->,--format): the de-identification command, shipped as anhonest, typed
CLI_NOT_IMPLEMENTED(new exit code69,EX_UNAVAILABLE). It is gated on@cosyte/deid(unpublished,DEID-1in flight), never reads the input, and never emits apartial Safe-Harbor scrub presented as de-identified. A built-in redactor is deliberately
withheld: a partial scrub over only the obvious PHI loci would leave PHI behind and present a
false-safety impression (the cardinal hazard). It delegates to
@cosyte/deidvia a documentedseam (
core/deid.ts) when that library ships and is vetted.working directory) and by design (commands return a
RunResult; only the thinbinwrites toprocess streams).
CLI_NOT_IMPLEMENTEDdiagnostic code andEXIT.UNAVAILABLE(69); new programmatic exports(
PhiPosture,VALUE_FREE/SHOW_VALUES,extractPhiPosture,unsafeInputSuffix,deidStatus,redactCommand).Phase 1: the
cosyte parsefoundation. Reshaped the scaffold from a library skeleton into abinpackage:package.json#binmapscosyte→dist/bin/cosyte.mjs(a shebang entry over atestable
core), argument-parsed with Node's built-inutil.parseArgs+ a hand-rolled subcommanddispatcher (no third-party CLI framework).
cosyte parse <file|->: reads a file argument or stdin (-); autodetects the format bycontent (HL7 v2
MSHframing, FHIR JSONresourceType): conservative and fail-safe (a confidentsingle match parses; ambiguity/no-match is a data error asking for
--format, never a guess); routesto the wrapped parser (lazy-loaded per format); emits the parsed model as typed JSON on
stdout. Flags:
--format,--json,--quiet,--no-color.The exit-code contract (
sysexits.h-grounded, documented, tested):0success ·2usage ·65data/parse error (EX_DATAERR) ·66no input (EX_NOINPUT) ·70internal (EX_SOFTWARE).The CLI never exits
0on input it could not handle.Value-free diagnostic channel with stable
CLI_*codes (CLI_FORMAT_UNDETECTED,CLI_FORMAT_AMBIGUOUS,CLI_FORMAT_UNSUPPORTED,CLI_NO_INPUT,CLI_EMPTY_INPUT,CLI_PARSE_FAILED,CLI_USAGE,CLI_INTERNAL). stdout is the data channel; every stderr line isvalue-free: code + position only, never a field value. No temp files, no file logging.
Programmatic
coreAPI (the.subpath):detectFormat/classifyCandidates/detectionError,EXIT,CLI_CODES/CliError,run,parseCommand,VERSION.Runtime dependencies (ADR 0021):
@cosyte/hl7(46d50eb, v0.0.1) and@cosyte/fhir(7a099b2,v0.0.0) as hard, first-party deps (an
npxbin cannot peer-depend) vendored aspnpm packtarballs under
vendor/until PUB-FLIP (pnpm vendor:refresh; umbrella ADR 0008). Capped at 2by the umbrella
verify-policy.json; third-party CLI-core runtime deps stay zero.ADRs:
0021(developer-tooling tier is abinthat hard-depends on first-party siblings;third-party runtime deps minimized) and
0022(one repo, two bins: the CLI and the futurecosyte-mcpMCP server over one core; the web playground is out of scope).Changed
The documented per-(format, operation) support matrix now states its negatives in words. In
docs-content/limitations.mdthe table used a bareU+2014as a cell value meaning "notsupported". The em-dash sweep below rewrote it as punctuation, so
dicomfmt,mllpfmtandmllpvalidaterendered as a stray colon: "support absent" silently became "support unstated",on the page whose whole job is honest capability disclosure, in a form a reader would read as a
rendering artifact rather than as a claim. Caught by the conformance refuter, not by CI, and nothing
in this repo's CI could have caught it (
test/docs-content.test.tsonly executes runnabletsblocks, and Prettier's glob does not cover
docs-content/). Those three cells now readnot supported. The support facts are unchanged; the page now says them. Before sweeping anyrepo for this character, find the places it is a value rather than punctuation and convert those by
hand, to a word, first.
No cosyte surface in this repo uses an em dash any more, and a CI gate keeps it that way. The
brand rule (
knowledgebase/06-brand/voice-and-tone.md, "No em dashes. Ever.") bansU+2014outright and names commit messages explicitly. Measured byte-level over all 124 tracked files,
not over markdown alone: 659 occurrences across 87 files, all as the literal character and none
in an encoded form. 61 of the 87 are not markdown (26 are), and one is
package.json, whosedescriptionis published to npm and rendered on the package page. Every occurrence was rewrittenin place with a period, a colon, a comma, or parentheses, never re-encoded. Consumer-visible text
that changed wording: the npm description,
README.md,docs-content/,cosyte --help, thegenerated
bash/zsh/fishcompletion scripts, and theredact/deidand reserved--profileunavailability messages. No stable code, exit code, flag name, or JSON field changed, so nothing
branching on an exit code or parsing
--jsonis affected. Commit subjects already onmainareleft alone: the message half runs on
pull_requestonly, and history is not rewritten.scripts/check-no-emdash.sh(pnpm check:no-emdash) plus.github/workflows/no-emdash.ymlscan both halves the rule covers: every tracked file, and thepull request title, body, and commit messages, on the non-default
editedtrigger so retitling apull request re-checks it. The new
no-emdashcheck-run context is required by theci-required-checksruleset.clitracks eleven binaries (the tenvendor/cosyte-*.tgzpacked sibling dependencies andtest/__fixtures__/sample.dcm), so the script partitions on an explicit NUL-byte rule rather thanon
grep -I's heuristic, which would silently skip a genuine text file with a broken encoding.The reason is measured rather than precautionary:
vendor/cosyte-hl7-0.0.0.tgzalreadycontains the byte sequence
E2 80 94by compression coincidence, so a text-only scan reds on ittoday, naming a compressed byte stream nobody wrote and offering no way to fix it.
holding a NUL byte is exempt and its em dash would be missed (there is none today; the excluded
count on the OK line is the tell). Encoded-form matching is literal, so lowercase
%e2%80%94, acapital-X
—, and a semicolon-less—pass. The scan reads file contents, neverfile names.
The published type declarations no longer carry internal project bookkeeping.
dist/index.d.tsand
dist/mcp.d.ts(and their.d.ctstwins) are compiled from the JSDoc on every exported symbol,and that JSDoc was citing item identifiers (
CLI-6), ADR numbers (ADR 0018,0021,0022,0025), the meta-repo roadmap in prose and by path (cli roadmap §7,operations/roadmaps/cli.md), and phase language (§Phase 4,Phase-5, "in a later phase"). Allof it rendered on hover for anyone who installed the package. Measured on
62fba77with the ruleset that ships alongside: 61 occurrences across 23 tracked
src/files, producing 56 across thethree declaration files a consumer receives (43 in
dist/index.d.ts, 12 indist/mcp.d.tsand 1in the shared
dist/io-*.d.tschunk both entries import), each mirrored in its.d.ctstwin. Now0 on every one of them. (The shared chunk is easy to miss and was missed once here: a count that
scans only the named entry points under-reports.) Every removal is a cut, not a rewrite: the
surrounding guarantees (value-free stderr, the never-a-fake
CLI_FORMAT_UNSUPPORTED, the gatedredact, the exit-code contract) are worded exactly as strongly as before. Internal traceabilitystays where the convention puts it: this file, the changesets, the commits and the roadmap.
A dependency-budget figure left
src/core/deid.tstoo. The module said wiring@cosyte/deid"would breach the CLI's runtime-dep cap (2)". Only the number was stale: the cap has been 4 since
convert/map-codeslanded, and the constraint itself still holds, because the package declaresexactly 4 hard runtime dependencies and a fifth would indeed breach it. The clause went because a
dependency-budget figure is internal bookkeeping a consumer cannot act on, not because the
constraint lapsed. The reasons that actually ground the refusal to ship a built-in redactor are
untouched:
@cosyte/deidis unpublished, the wrapped parsers expose no de-identification API, anda partial scrub would present a false-safety impression.
CosyteFormat's documentation no longer understates autodetection. It read as though contentdetection recognised only HL7 v2 and FHIR, with the other six accepted by
--formatbut "not yetwired", which has not been true since all eight formats gained signatures. The stale sentence is
removed rather than restated.
A gate now enforces the public-surface rule, which is why the class stops regrowing.
scripts/check-no-internal-refs.sh(pnpm check:no-internal-refs, on theverify.sh cliladder)plus
.github/workflows/no-internal-refs.ymlport the shape ofhl7's gate(hl7#62, hl7#64)
and
ncpdp's (ncpdp#36) rather than the file. Fourpasses: the public markdown surface line by line and paragraph-joined, the npm metadata,
src/doc comments, and
src/string literals (the pass that would have caught the two identifiers thispackage printed to a user's terminal). Seven rules. Re-derived for this repo: the scan surface, a
standards-designation exclusion list covering all eight formats (this is the package where
HL7-V2,FHIR-R4,DICOM-SR,NCPDP-SCRIPT,X12-837P,CCDA-R2.1,MSH-2,NM1-03,439-E4andICD-10-CMare live at once, so theWORD-Ntrap is at its widest), and a seventhrule no sibling has, a prose roadmap citation, which was 30 of the 61 and which
hl7'spath-keyed rule cannot see. Both self-test halves run on every invocation: positive samples prove
each rule still matches, negative samples prove none has been widened into the
WORD-Nshape thatwould delete the reference material the CLI's docs exist to provide.
CHANGELOG.mdis excluded onpurpose, as it is in
hl7andncpdp: it ships inside the npm tarball, yet the convention names itas one of the places identifiers belong. That contradiction is ecosystem-wide, and it is recorded
here rather than settled by one repo.
redact/deidno longer names an internal tracking identifier on any consumer surface. TheCLI_NOT_IMPLEMENTEDtext printed when de-identification is unavailable carried an internal workitem that means nothing to anyone running the command, and the same identifier reached the
published type declarations (
dist/index.d.ts/dist/index.d.cts), where it surfaces in editortooltips. Both now state only the consumer-observable fact: the command delegates to
@cosyte/deid, which is unpublished, and the CLI ships no built-in redactor because a partialscrub would present a false-safety impression. The stable
CLI_NOT_IMPLEMENTEDcode and the exit69are unchanged.CLI_PARSER_UNAVAILABLE's message no longer cites an internal decision record. The errorraised when an optional per-format parser is not installed pointed at an ADR number, which is
meaningless to a caller; it now just names the package to install. The stable
CLI_PARSER_UNAVAILABLEcode and the exit69are unchanged. The same sweep removed theremaining roadmap-phase language from
README.mdanddocs-content/troubleshooting.md.The CI checks are now binding on
main.ci / verify (22, ubuntu-latest),ci / verify (24, ubuntu-latest),ci / actionlintandcodeql / analyze (javascript-typescript)are required status checks, each pinned to the
github-actionsapp, alongside branch deletion andforce-push protection. They were advisory before: a red check could not stop a merge. Dependabot
now watches the npm and GitHub Actions dependency surfaces weekly, which nothing did previously.
Reshaped the package from the parser-library scaffold to a
binpackage. Removed the archetypestubs (
parseCli,WARNING_CODES,FATAL_CODES); replaced the librarysrc/index.tsand theround-trip property test with the command tree, the programmatic
coreAPI, and command-contract /autodetection / PHI-leak / equivalence tests. Rewrote
docs-content/andREADME.mdfor the CLI.Deprecated
Removed
Fixed
README.mdanddocs-content/guides-overview.md"Status" blurbs read as a Phase-1-forward roadmap ("Phase 1 shipsparse…"); they now state the current surface directly (parse/validate/inspect/fmtand the gated
redact/deid) over the two wired parsers (HL7 v2 + FHIR R4). The pre-alpha,not-yet-published-to-npm status is unchanged (accurate), and the
npx/npm install -gexamples nowcarry a "not on npm yet" caveat (docs-only; README-ORG-SWEEP).
phi-scannow scans the real fixture directory. The scanner's fixture root pointed at anonexistent
test/fixtures/; it now walkstest/__fixtures__/(and the same path in the stagedfilter), so the PHI commit-gate actually covers the CLI's synthetic fixtures.
Security