# 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). ## [Unreleased] Nothing yet. ## [1.1.0] — 2026-08-13 ### Fixed — a mapping construct in OKF frontmatter no longer degrades into a string `okf.parse_frontmatter` gives the mapping *class* no expressible form by design (T2). Two routes escaped that: they parsed "successfully" into the wrong **type** instead of raising. Both are closed, and both now `FAIL_SECURE` through `okf.import_bundle` (door C). | route | was | now | |---|---|---| | `sources:`
` - uri: https://e.com/a` | string `'uri: https://e.com/a'` — WARN | `OKFFrontmatterError` — FAIL_SECURE | | `sources:`
` - uri:` | string `'uri:'` — WARN | `OKFFrontmatterError` — FAIL_SECURE | | `attester: resource: attesters/x.py` | string `'resource: attesters/x.py'` — WARN | `OKFFrontmatterError` — FAIL_SECURE | The security consequence was the same in each: a pointer parked in a degraded mapping rides through in a key the `resource` allowlist never inspects, and mode-b `import_bundle` writes the merged concept verbatim. The first route was documented at `docs/LIMITATIONS.md:43`; the inline second colon was **found by measurement while closing it**, and is the reason this release names two routes rather than one. Neither shape is conformant OKF — a well-formed bundle does not produce them; a malformed or hostile one can. **The boundary is where YAML puts it**, ground-truthed against PyYAML 6.0.3 rather than reasoned: `": "` and a trailing `":"` are exactly the two shapes where a plain scalar becomes a mapping, and they are refused. A colon carrying neither a space nor a line end opens no mapping — `domain:security` and `https://e.com:8443/a` still parse — and a quoted scalar (`- "uri: x"`) is still a scalar. Quotes are retained rather than stripped; that divergence from YAML is unchanged and now pinned. **This is a behaviour change inside the freeze, not a break of it.** No exported name moved. A document that disposed `WARN` on `1.0.0` may dispose `FAIL_SECURE` here — the `1.0.0` entry says exactly this is a fix, not a break. A consumer whose bundles carry an unquoted `": "` in a frontmatter value will now see those concepts refused at import; quote the value, and it parses. Suite 792 → **802** (11 rows added, 1 retired). 129/129 classes, 6/6 documented gaps, 35 limitations — all unchanged. ## [1.0.0] — 2026-08-13 ### Changed — the exported Python surface is frozen under semver No code changed in this release. `1.0.0` is a governance promise, not a claim that the library is finished: **no name exported from `llm_ingestion_guard` is removed, renamed or given a different meaning without a `2.0.0`.** Measured before the tag, the surface has been stable in form since `0.3.4` — four names added, none removed or renamed — while behaviour moved across five releases (`0.4.0` … `0.7.0`). **Detection behaviour is deliberately outside the freeze.** Severities, thresholds, lexicon entries and the dispositions they produce are calibration, and calibration moves in minor and patch releases. A payload that disposes `WARN` here may dispose `FAIL_SECURE` in a later `1.x`; that is a fix, not a break. Assert on the disposition your policy requires, not on a severity you observed. The behaviour changes this freeze rests on are not repeated here — see `[0.3.0]` for the active-content gate and the OKF adapter, and `[0.3.1]` for the ordinary-link/image calibration that the two consumer promises pin. ### Changed — three limitations are conceded for `1.x` rather than deferred `docs/LIMITATIONS.md` no longer says "deferred" or "pending" about any of them: - `Severity` still carries disposition intent on the detection side. Separating *what was seen* from *how bad it is* changes `Finding` and `Severity`, so it is a `2.0.0` change. Read a finding's `id` for the capability. - The input-cap asymmetry at `MAX_INPUT_CHARS` is permanent in `1.x`: surfaces that return content raise `OversizeInputError`, surfaces that return findings truncate and emit `active:oversize-input`. - The multilingual homoglyph false positive is conceded more narrowly — no fix is promised, but it is calibration, so one may land in any `1.x` release. `SECURITY.md` carries all three as documented boundaries and states the support window for a `1.x` line. ### Known at the freeze, deliberately not blocking it `docs/LIMITATIONS.md` §`:43` — an OKF block sequence with exactly one key per element misparses silently in `okf.import_bundle`, so a pointer can ride through in a key the `resource` allowlist never inspects. Closing it tightens what the adapter admits: behaviour, not form, and shippable in a `1.x` minor. It is recorded here because "we knew, and froze first" is a defensible position and "we forgot" is not. Runtime coverage at the freeze: `llm-ingestion-okf` has measured `0.3.4` and run a `0.3.4`→`0.6.1` differential on its own door across two Python versions; `llm-security-commons` differentially tested its independent reconstruction of the raw-HTML classifier against ours over 42 probe tags with 0 disagreements. **No external consumer has run the `0.7.0` runtime**; the four symbols added since `0.3.4` are additive, so a caller that does not invoke them is unaffected. ## [0.7.0] — 2026-08-13 ### Added — `active:raw-html-link`, a click-required carrier class for raw HTML Raw HTML graded on activity alone: every active tag was HIGH. So the *same URL* was LOW as `[t](https://example.com/guide)` and HIGH as `` — an asymmetry produced by syntax, not by affordance. Following an anchor needs a human, exactly like the markdown inline link that has been MEDIUM since 0.3.1. `` and `` now report as **`active:raw-html-link` at MEDIUM**. Everything a renderer fetches or executes unattended keeps `active:raw-html` at HIGH, and the event-handler test runs *first*, so `` is graded as the execute-class carrier it is rather than downgraded with the anchors. The URL-attribute branch deliberately stays on the HIGH side: a name outside the active set has unknown rendering, and `href` is not the only URL attribute it may carry. Grading `` as a link would be reasoning, not measurement. **This is a new label, and labels are a contract surface consumers pin against.** A document that previously produced one `active:raw-html` finding may now produce two findings, one per carrier class. ### Changed — a tag whose whole affordance is a URL it does not carry is inert ``, ``, `