feat(active-content): the seventh case, and the classifier it needed came with it
Adopting `active:raw-html-link` was one id. Publishing it honestly was the whole of `active_tag_class` — one function, three branches, no way to state the split without the no-URL narrowing and the 0.6.0 external-target rule. On the old predicate a bare `</a>` is active by name, so a consumer implementing from the hybrid would emit the new label where the seed runtime emits nothing. The file is now two pins, stated as two: v0.3.4/0bf0729 everywhere except the raw-HTML classifier, v0.7.0/be9759b there. The drift between them was measured field by field against the imported module rather than assumed, after stripping inline-flag rendering and applying the file's own declared quote normalisation so a spelling difference could not masquerade as drift. Exactly one published field had moved, and not the one this release was about: `html.active_tags` carried the MUTATOR's 23-name set where the gate means the SCANNER's 22. Correct at the 0.3.4 pin, wrong from 0.6.0 on. Kept as `html.mutator_tags`. The sweep covered 93 cases, not the 6 obvious ones. The narrowing can silence an `active:` finding inside the `observed_out_of_scope` evidence of a LEXICON case, and that field is guarded by no test anywhere — stale entries there survive forever. One case moved: html-obfuscation__aria-label, whose `<a aria-label=…>` carries no URL attribute. Its fixture is deliberately not rewritten; the residue is true at the commit `measurement` pins, and rewriting one of 83 would leave two commits under a header naming one. Recorded, dated and pinned in the manifest. The strongest check is not the digest: the checker rebuilds the published classifier from the JSON alone, importing nothing from the runtime, and differential-tests it against `active_tag_class` over 42 probe tags. 0 disagreements. That is what licenses shipping a classifier as data. No `aliases.llm_security` published, on this file or on carriers. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LTTaT4quwNPwBYVqmgAt8t
This commit is contained in:
parent
8f08e9ac73
commit
e6ca5ae5ee
7 changed files with 393 additions and 22 deletions
37
CLAUDE.md
37
CLAUDE.md
|
|
@ -74,6 +74,18 @@ Ingen. Data + prosa. Filformater: JSON (data + schema), Markdown (spec), rå tek
|
|||
- **Generatoren verifiserer aldri seg selv.** En mint krever en separat sjekker som leser
|
||||
fixturene tilbake fra disk og utleder alt på nytt (digest, id fra case-id, scope,
|
||||
exact-within-scope). Ligger i scratchpad, aldri i repoet.
|
||||
- **En innsnevring måles mot HELE korpuset, ikke mot casene den handler om.** Endrer en
|
||||
oppdatering hva en runtime *slutter* å rapportere, kan den tømme et `observed_out_of_scope`
|
||||
hvor som helst — også på caser scopet til en helt annen tabell. Det feltet er evidens
|
||||
(spec §5), så **ingen testsuite noe sted vokter det**: en foreldet oppføring består hver
|
||||
kjøring for alltid. Kjør hver committet case gjennom sitt eget scopes inngangspunkt og
|
||||
sammenlign mot BEGGE stedene fixturen fører en runtime-label — `findings` (mappet via
|
||||
tabellens aliases) og `observed_out_of_scope`. Målt 2026-08-13: seks «åpenbare» caser
|
||||
flyttet seg ikke, én lexicon-case gjorde det.
|
||||
- **En foreldet residue-oppføring skrives ikke om — den pinnes.** `observed_out_of_scope` er
|
||||
sann ved commiten `measurement` pinner. Retter du én av 83, står 82 målinger ved én commit
|
||||
og én ved en annen, under en header som navngir én. Før avviket i manifestet med dato og
|
||||
commit i stedet. Å re-pinne hele korpuset er en egen beslutning.
|
||||
|
||||
### Id-rom: adoptert vs. navngitt
|
||||
|
||||
|
|
@ -106,6 +118,31 @@ Unicode-standarden, publisert leverandør-doc). Aldri fra egen hukommelse, aldri
|
|||
«fylt ut for konsistens». Kan en oppføring ikke verifiseres: utelat den, eller marker
|
||||
den eksplisitt uverifisert i `$comment`.
|
||||
|
||||
### Å adoptere én ny id fra en runtime som har flyttet seg
|
||||
|
||||
Standardtilfellet er additivt: en ny nøkkel, en ny id, ferdig. Det holder bare når
|
||||
**produsenten** av den nye id-en er uendret. Er den nye id-en et nytt utfall av en
|
||||
klassifiserer runtimen har skrevet om, må hele klassifisereren adopteres — en publisert id
|
||||
oppå den gamle prediktoren er en kontrakt som ser komplett ut og er feil, og en konsument
|
||||
som implementerer fra den divergerer fra dag én. Test: kan du skrive den nye id-ens gate
|
||||
uten å røre de andre grenene i samme funksjon? Kan du ikke, er scope hele funksjonen.
|
||||
|
||||
To ting som følger av det:
|
||||
|
||||
- **En datafil kan ha TO provenance-pins, og da skal begge stå.** Én pin over en fil som er
|
||||
halvt gammel og halvt ny beskriver ingen av halvdelene. Skop re-pinnen til de blokkene den
|
||||
faktisk dekker, og si hvilke.
|
||||
- **Mål drift felt for felt før du re-pinner, ikke etterpå.** Importer modulen ved taggen og
|
||||
sammenlign hvert regex, hver severity, hver liste og hvert tallgulv — etter å ha strippet
|
||||
Pythons inline-flagg-rendering og anvendt filas egne deklarerte normaliseringer, ellers
|
||||
rapporterer du staveforskjeller som drift. Målt 2026-08-13 over 0.3.4 → 0.7.0: 24 felt
|
||||
holdt, **ett** hadde driftet, og det var ikke det oppgaven handlet om.
|
||||
|
||||
Og den sterkeste kontrollen når du publiserer en klassifiserer som data: **bygg den opp
|
||||
igjen fra JSON-en alene** — ingen import fra runtimen — og differensialtest mot runtimens
|
||||
funksjon over et probe-korpus som treffer hver gren. Består den, er fila bevist tilstrekkelig
|
||||
som spesifikasjon. Består den ikke, mangler fila noe prosa aldri ville avslørt.
|
||||
|
||||
### Behaviour preservation (v0.1.0-invariant)
|
||||
|
||||
v0.1.0 er en **ekstraksjon**, ikke en revisjon. Data som er hentet ut av en konsument
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue