# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Versioning note: the repository tag versions **the contract** (file set, key names, case ids, disposition semantics). Each JSON file additionally carries its own `"version"` field, bumped when that file changes. ## [0.4.0] — 2026-08-11 **Two detection values changed, by two different mechanisms, and the difference between those mechanisms is the point of the release.** One pattern table was **re-extracted** from a pinned upstream commit, the way every value in this repository has moved until now. Two lexicon rows were **authored here at the source owner's direction**, which has never happened before and required a reason that is not "we measured it and we were right." A runtime that vendors this repository will see findings change. Any consumer asserting byte-identity against `v0.3.0` goes red by construction — `lexicon/injection-lexicon.json` changed pattern text. Ids, labels, aliases, family membership, case ids and every count are unchanged. ### Changed - `lexicon/injection-lexicon.json` `0.7.0` → `0.8.0` — **`hybrid-xss:script-tag` and `hybrid-xss:iframe-src` narrow their unbounded negated class from `[^>]*` to `[^><]*`.** Both forms are quadratic in scan length on input that repeats the tag prefix and never supplies a `>`: each occurrence is a match start and `[^>]*` scans to end of input from each one. Measured in Node v25.8.2 at 16k / 32k / 64k / 128k / 256k chars — script-tag 32.65 / 113.36 / 479.02 / 1988.83 / **7772.25** ms, iframe-src 39.23 / 131.76 / 574.94 / 2469.55 / **9449.94** ms, ×4 per doubling for both. Under `[^><]*` the same inputs cost 0.08–0.66 ms and 0.10–1.00 ms: flat, not merely faster. **Why this is not commons correcting seed data.** The dependency direction inverted. As of `llm-security` `be14867` their four injection tables are built from this file and hold zero local regex literals — measured on their published surface at `47905da`, with their vendored copy of the lexicon confirmed byte-identical to `0.7.0`. So re-extraction was not available as a mechanism: there is no upstream literal left to re-read. They re-measured the finding rather than accepting it, rejected `[^>]{0,256}` because a bound is paddable and `[^>]{1,256}` because it drops bare `` matches the pattern under *both* forms, so it passes either way. Reverting the pattern to its `0.6.0` form fails this case and only this case — mutation-verified in both directions across all 90. **The first case input authored in this repository** rather than reproduced verbatim from a runtime's payload set, recorded in the new `authored_payloads` block rather than folded into `payload_provenance`, whose value is precisely the claim that its inputs are verbatim upstream. That claim stays exactly as strong as it was: 83 of 83. Both witnesses for this axis were named by `llm-security` on 2026-08-10; this is the first of the two. Findings measured through the guard's public API at `0dce50f` / `0.5.0`, with the existing case's committed bytes and digest reproduced by the same harness in the same run as a control. - **`schema/conformance-declaration.schema.json` (`0.1.0`)** — the shape a runtime publishes alongside a conformance result, satisfying the §1.1 MUST that `v0.2.0` created and left without a form. Requested by `llm-security` in those terms (runtime, commit measured, implemented file paths) with the stated reason that two runtimes publishing free-form declarations makes `83/83 + 6 not-applicable` unparseable by anyone but its author. Carries the two arithmetic invariants §1.1 implies but cannot state unambiguously in prose: the four verdict counts MUST sum to the total, and the total MUST equal the corpus case count at the commit measured. Requires the enumeration arrays whenever their counts are non-zero, which turns §1.1's "MUST still be enumerated" from prose into a schema failure. Keeps `error` and `not_applicable` structurally distinct, per §1.1. Records `declaration_source` — whether the declared set is derived from the runner's own constant or hand-maintained beside it — because only the derived form makes the anti-narrowing fence structural. **Deliberately not a gate:** nothing in this repository runs, and no validation was asked for. Mutation-tested: the example validates, and five distinct defect classes are rejected. - **`spec/conformance-corpus.md` §1.1** — normative pointer to that schema, plus a SHOULD that a runtime derive its declared set from the constant its runner uses to accept or reject a `scope`, and record which it did. ### Fixed - `docs/lexicon-port-divergence.md` — the `hybrid-xss:script-tag` row is closed on the closing-tag axis, having reversed twice in three days (guard-diverges → commons-diverges → converged). What remains open is the one-byte span difference: the guard excludes `<` from its negated class and the other two do not, so `