release(0.2.0): the contract gained a MUST, so the tag has to move

A runtime that conformed to v0.1.0 does not conform to this one until it declares the
set of commons data files it implements (spec section 1.1). In 0.x that is a minor
bump by the rules, but it is breaking in substance, and the CHANGELOG entry says so
rather than leaving a consumer to discover it in a failing suite.

Both consumers were notified by coord before this tag existed, which is the wrong
order in one respect: they were told to expect a contract change they could not yet
vendor. README says to vendor a tag and never main, so the notice was unactionable
until now. Tagging closes that gap rather than opening a new one.

Version sync: README's subtree-add and clone examples pinned v0.1.0 and now pin
v0.2.0; the subtree-pull example is a placeholder rather than a version, since it
demonstrated "move to a newer tag" while naming the current one. The two remaining
v0.1.0 mentions are historical statements about what that release promised and stay
as they are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CVouC9nsfrfV5jRSejxbvQ
This commit is contained in:
Kjell Tore Guttormsen 2026-08-11 06:27:23 +02:00
commit d843935ec7
2 changed files with 15 additions and 5 deletions

View file

@ -11,6 +11,16 @@ case ids, disposition semantics). Each JSON file additionally carries its own
## [Unreleased] ## [Unreleased]
## [0.2.0] — 2026-08-11
The contract gained a normative MUST, which is why this is a release rather than a
metadata commit: **a runtime that conformed to `v0.1.0` does not conform to this one until
it declares the set of commons data files it implements.** In 0.x that is a minor bump by
the rules, but it is breaking in substance, and a consumer reading only the version number
should learn that here rather than from a failing suite.
Everything below this heading was previously listed as unreleased.
### Retracted ### Retracted
- **The claim that the Python guard's port cites `severity.mjs` for hybrid severity.** It is - **The claim that the Python guard's port cites `severity.mjs` for hybrid severity.** It is

View file

@ -24,17 +24,17 @@ As a `git subtree` (recommended: history is preserved and upgrades are a single
```bash ```bash
git subtree add --prefix vendor/commons \ git subtree add --prefix vendor/commons \
https://git.fromaitochitta.com/open/llm-security-commons.git v0.1.0 --squash https://git.fromaitochitta.com/open/llm-security-commons.git v0.2.0 --squash
# later, to move to a newer tag # later, to move to a newer tag
git subtree pull --prefix vendor/commons \ git subtree pull --prefix vendor/commons \
https://git.fromaitochitta.com/open/llm-security-commons.git v0.2.0 --squash https://git.fromaitochitta.com/open/llm-security-commons.git <newer-tag> --squash
``` ```
Or pin a tag and copy — `fork-and-own` is an explicitly supported path: Or pin a tag and copy — `fork-and-own` is an explicitly supported path:
```bash ```bash
git clone --depth 1 --branch v0.1.0 \ git clone --depth 1 --branch v0.2.0 \
https://git.fromaitochitta.com/open/llm-security-commons.git https://git.fromaitochitta.com/open/llm-security-commons.git
``` ```
@ -60,14 +60,14 @@ and keeping it that small is the point.
| [`schema/finding.schema.json`](schema/finding.schema.json) | **Normative.** The finding contract — closed against its producer, ten properties — plus the SARIF output profile. The JSONL profile is recorded as `not applicable`, with the reason. | | [`schema/finding.schema.json`](schema/finding.schema.json) | **Normative.** The finding contract — closed against its producer, ten properties — plus the SARIF output profile. The JSONL profile is recorded as `not applicable`, with the reason. |
| [`spec/conformance-corpus.md`](spec/conformance-corpus.md) | **Normative.** How to read the corpus: what a case is, why `input.txt` is bytes rather than text, and what `exact-within-scope` requires of a runtime. | | [`spec/conformance-corpus.md`](spec/conformance-corpus.md) | **Normative.** How to read the corpus: what a case is, why `input.txt` is bytes rather than text, and what `exact-within-scope` requires of a runtime. |
| [`conformance/`](conformance/) | 89 cases. One directory per case: `input.txt` in, `expected.json` out. Ground truth. 83 cover the injection lexicon, one per pattern, and both seeding runtimes were measured producing the same verdict on all 83. Six cover active content and are measured against the one runtime that implements that table — `not-applicable` for the other, not failing. See [`conformance/manifest.json`](conformance/manifest.json). | | [`conformance/`](conformance/) | 89 cases. One directory per case: `input.txt` in, `expected.json` out. Ground truth. 83 cover the injection lexicon, one per pattern, and both seeding runtimes were measured producing the same verdict on all 83. Six cover active content and are measured against the one runtime that implements that table — `not-applicable` for the other, not failing. See [`conformance/manifest.json`](conformance/manifest.json). |
| `spec/decode-pipeline.md` | **Planned, not in v0.1.0.** The decode order, in RFC 2119 language. Two runtimes that decode in different orders will disagree on identical input. Writing it needs the decode implementation, which is engine code and has not been supplied — and a normative spec guessed from a data dump would be worse than an absent one. | | `spec/decode-pipeline.md` | **Planned, still not shipped as of v0.2.0.** The decode order, in RFC 2119 language. Two runtimes that decode in different orders will disagree on identical input. Writing it needs the decode implementation, which is engine code and has not been supplied — and a normative spec guessed from a data dump would be worse than an absent one. |
| [`docs/extraction-plan.md`](docs/extraction-plan.md) | Informative: where each file was seeded from, and what v0.1.0 promised. | | [`docs/extraction-plan.md`](docs/extraction-plan.md) | Informative: where each file was seeded from, and what v0.1.0 promised. |
| [`docs/lexicon-port-divergence.md`](docs/lexicon-port-divergence.md) | Informative: a measured disagreement between two ports of the injection lexicon — 13 patterns that behave differently, in both directions, and why no data file was changed because of it. | | [`docs/lexicon-port-divergence.md`](docs/lexicon-port-divergence.md) | Informative: a measured disagreement between two ports of the injection lexicon — 13 patterns that behave differently, in both directions, and why no data file was changed because of it. |
Every JSON file carries a top-level `version`. Every normative specification carries a Every JSON file carries a top-level `version`. Every normative specification carries a
`Status: normative` marker. Rows marked **Planned** are named here because the layout is `Status: normative` marker. Rows marked **Planned** are named here because the layout is
part of the contract, but the file does not exist yet — they are not links, and nothing in part of the contract, but the file does not exist yet — they are not links, and nothing in
v0.1.0 depends on them. v0.2.0 depends on them.
Each data file records its own provenance and, in `verified`, how strongly it is backed. Each data file records its own provenance and, in `verified`, how strongly it is backed.
`calibration/calibration.json` is currently the one file that says `false`: it was `calibration/calibration.json` is currently the one file that says `false`: it was