# 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. ## [Unreleased] ## [0.3.1] — 2026-08-11 **No pattern changed value. One shipped table is disclosed as stale, and the repository gains the reporting route it did not have.** Nothing in `patterns`, `expected.json` or any id moved, so a runtime that passes `0.3.0` passes `0.3.1` unchanged. Read the first entry anyway if you vendor `signatures/secret-egress.json`: it now says, in the file, that it under-matches its own source by one entry. ### Added - `SECURITY.md` — the reporting route for a repository whose attack surface is **data**. It answers the question an ordinary security policy does not have to: how to report that a *detection-table entry is wrong*, and why a confirmed defect in extracted data is decided in the runtime it was extracted from before it is changed here. Names what is in scope (a silent false negative, a fixture that sanctions a miss, an unsafe normative clause, a secret in the history, data gone stale against its source), what is a documented boundary rather than a vulnerability, and the two classes that skip the routing — a real secret, and data authored here rather than extracted. States plainly that fix latency is bounded by the owning runtime's schedule and the consumer's pull, not by this repository's. Written because `org-ops` recorded the file as missing against the org standard on 2026-08-11, and because four files here are detection data where a mistake is a detector that looks like it works. `CONVENTIONS.md`, recorded in the same message, is not in this release. - `README.md` — a short **Reporting a wrong entry** section pointing at it. Without it the policy is a file nobody looking at the front page would know to open. ### Changed - `signatures/secret-egress.json` `0.1.0` → `0.2.0` — **a staleness disclosure, not a data change.** All 18 patterns are byte-identical to `0.1.0`; one entry is added to `provenance.evidence_limits`. `llm-security` reports having taken the source `SECRET_PATTERNS` from 18 to 19 by adding `OpenAI Legacy API Key`. That is recorded as their report and explicitly **not** reproduced here — the commit carrying it is not on their public remote, which was measured at `b1ba1fb` on 2026-08-11. What *was* measured here: none of the 18 patterns matches a legacy `sk-…T3BlbkFJ…` key shape. So a consumer vendoring this file under-matches the seed hook by one entry, on a live credential shape, and now reads that in the file rather than inferring it. It will be closed by re-extraction from a pinned public commit, never by authoring the entry here from a message. - `conformance/manifest.json` `0.3.0` → `0.3.1` — the `scope_planned.blockers` text for `signatures/secret-egress.json` is corrected. Through `0.3.0` it ended by naming `gcp-service-account-json` and `openai-api-key-legacy` together as ids "absent here". They are two different kinds of fact, and one of them was misleading. Measured 2026-08-11, by running this file's own 18 patterns in `order` over a service-account document, against the guard at commit `e671edb`: a **complete** GCP service-account key file *is* matched here, at order 11 (`Private Key PEM Block` — its `(?:RSA |EC |DSA |OPENSSH )?` prefix group is optional, so the bare PKCS#8 header such a file carries matches). The same document with `private_key` removed matches nothing here while the guard's marker pattern still fires. That is a **cut-point** difference — the guard detects the document marker, this table detects the key material — which is what the blocker is about, and not a missing entry. `openai-api-key-legacy`, by contrast, is a real hole here today, and is now recorded as one. The correction is folded into the existing blocker string rather than added as a sibling key: `blockers` is a map from table path to text, and a second key under a table path would read as a second table to anything iterating it. - `docs/lexicon-port-divergence.md` (informative) — the residual `[^>]` vs `[^><]` row gains a fuller witness set. `llm-security` measured the three forms as **totally ordered** by what they match, each a strict superset of the next, and named two input classes the guard's narrower class drops (`` 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 `