Skip to content

Release notes

Eugene Lazutkin edited this page Aug 21, 2026 · 4 revisions

Release notes

0.2.0 (2026-08-20)

A footgun the first external consumer found within hours of 0.1.0, fixed additively.

  • parseSidecar(text).version — the format version as a number. 0.1.0 exported SIDECAR_VERSION as the number 1 while readFrontmatter returned every frontmatter value as a string, so fm.sidecar === SIDECAR_VERSION — the comparison the package's own two exports invite — was false for every valid sidecar, and a consumer reading it that way saw a valid file as unversioned. Nothing was broken internally (sidecarProblems coerces before comparing) and parse.d.ts already declared Record<string, string> honestly; the defect was the pair of exports, which no single-file check can see. The fix is purely additive: a typed version rides on the object parseSidecar returns, readFrontmatter stays a generic reader that knows nothing about any key's semantics, and the published frontmatter: Record<string, string> contract is untouched — so nothing written against 0.1.0 changes meaning. Coercing inside readFrontmatter was considered and rejected: a reader that knows one key's semantics is a reader that will eventually be asked to know a second, and the argument for the second is always the first one. Reported by the nano-binary-search project.
  • npm page links: repository, homepage, and bugs were absent from package.json since the first publish, so the npm page had no link back to the source and provenance had nothing to attest against. Added.
  • Internal: the mutating-predicate hazard lifted into the bundled sorted-array family artifact, which now covers every claim in nano-binary-search's published INVARIANTS.md. Tests run on Node, Bun, and Deno; CI is 3 OS × Node 22/24/26.

0.1.0 (2026-08-20)

The §6.6 sidecar format: a file now has to say it is a sidecar, and a consumer can find one without being told where it lives.

  • Frontmatter is the discriminator: sidecar (integer format version, exported as SIDECAR_VERSION), package (must equal the host package's name, which catches a vendored copy) and binds (the versions the claims are asserted for) are required; export and verified: ci stay optional. INVARIANTS.md is a common filename for things that are not sidecars — human prose recording "properties that must hold", agent guardrails at .claude/INVARIANTS.md — and reading either as a contract would promote somebody's sentences to axioms. isSidecar(text) answers "is this one at all" without throwing, because the common case is a file a consumer should skip in silence; readFrontmatter and sidecarProblems expose the reasons for logging, and parseSidecar now fails with a located error naming every missing key.
  • Sidecar discovery: resolveSidecar(read) (subpath invariants-sidecar/resolve.js) resolves an installed package's sidecar — the manifest's invariants key first, since there the author states the file is a sidecar, then a root INVARIANTS.md, provisional until its frontmatter validates. It takes a reader over the package directory instead of importing node:fs, so the module stays as runtime-agnostic as the rest of src/ and the zero-dependency shape survives discovery. null means the package has no sidecar and {path, skipped} means one existed and was refused, with the reason, so a consumer can tell those two apart. The bundled satisfies(version, range) covers the exact, ^, ~ and * forms binds actually uses and returns null for anything else — unknown rather than false, because guessing at a range it does not implement would silently downgrade a live sidecar to stale.
  • Oracle ingestion gated on verified: ci: oracleInputsFromSidecar yields axioms only from a sidecar the package's own CI keeps true, returning {axioms: [], declares: [], unverified: true} otherwise — empty and marked rather than thrown, so a caller sweeping many packages needs no try/catch. Pass {allowUnverified: true} to read the proposals anyway. An unverified sidecar is a proposal, not provenance.
  • Parser hardened against ReDoS: two rounds of js/polynomial-redos findings in parse.js were fixed rather than dismissed, because parseSidecar is the advertised safe-on-untrusted-input step — tools read sidecars straight out of node_modules. The surviving $-anchored pump in CLAIM_RE measured 5.8 s on a 160 KB adversarial line and was rewritten to a single-quantifier head plus lastIndexOf, verified differentially against the old regex over a structured corpus and 200k fuzzed lines before it landed; the same line now parses in 1.0 ms. Pre-splitting input on \n is not a defence here, which is the calibration worth keeping: JS . also excludes \r, so a lone carriage return still fails the $ anchor and arms the rescan. The restructure delivered a real diagnostic in passing — a claim head that does not close with : is now named.

Internal: tape-six 1.16.4, prettier 3.9.6. Tests run on Node, Bun, and Deno; CI is 3 OS × Node 22/24/26.

0.0.1 (2026-08-07)

The initial release — the experiment's static-land core, end to end:

  • Dictionary completer: complete() derives every missing derivable method to fixpoint from the static-land derivation lattice (DERIVATIONS), reports provenance, and lists the consistency obligations of hand-provided derivable methods.
  • Law/consistency property-test generator: LAWS records for Setoid, Semigroup, Monoid, Functor, Applicative, Chain, and Monad; makeLawTests / makeConsistencyTests produce t.prop()-ready tests — consistency checks every applicable derivation path, since the paths that avoid a corrupted method are what refute it; runLaws drives them through tape-six-fast-check.
  • Sidecar parser and check compiler: parseSidecar reads the moonshot §6.5 artifact format into inert data (it never evaluates); compileCheck / compileChecks are the explicit trust step; lawTestsFromSidecar bridges a Laws section to the law suite. The worked binary-search artifact is verified end-to-end against the real published nano-binary-search, including the complexity bound via an instrumented comparator and a deterministic hazard witness.
  • Call-site guards: guardsFromSidecar (subpath invariants-sidecar/guards.js) turns check-bearing pre: claims into tape-six-invariant guards — counted assertions when a tape-six run exercises the call site, the configured absent behavior otherwise.
  • Oracle bridge: oracleInputsFromSidecar / instantiateAxioms / declareFromSidecar emit apodictum wire shapes from blessed law: / effects: claims; the oracle is not a dependency — the integration tests load it dynamically (installed package first, fleet sibling second) and skip cleanly without it.

A drafted family sidecar for nano-binary-search's ten sibling exports ships in the repo (sidecars/), all claims surviving refutation. Tests run on Node, Bun, and Deno; CI is 3 OS × Node 22/24/26.

Clone this wiki locally