feat(p19): a cost code must have a FORM where the input offers forms

P18's round 2 ended with two VALIDATED proposals whose affected_item codes
were ordinary words from a road standard's prose -- impulsventilator (4 of
270 N500 documents) and bituminoest baerelag (4 of 1133 N200). Both are
grounded in P7's sense and neither is inert in P18/B1's sense; they are simply
not identifiers of a cost line, and the gate had no stage that could say so.
Known positive MEASURED, not asserted: replayed offline against the bases
those runs were given, both come back Rejection naming the denominator.

IDENTIFIER_FORMS moved from generate.py to validator.py: they now drive both
P8's report and this gate, and two copies of "what an identifier looks like"
would let the two disagree about one run's own input.

B1 -- two new forms, transcribed from measurement. R761's requirement numbers
are bare dotted numbers and all six refs in kontrakt-sorasen's fasit are of
that shape, which neither pre-P19 form matched: r761's whole offer was 3
identifiers over 6.5 MB, and is now 2332. The FIRST form was widened in the
same pass because B2 made these forms decide prose vs identifier, and this
repo's own ENERGI-TOTAL-EL matched none of them -- a gate may only be wrong in
the direction that admits too much.

Three things keep the gate from being a rule about shapes: the generality
guard (it fires only where the input offers forms), the baseline exemption
(stage 0 has already ruled that code real), and full-matching.

Honesty limit, measured and given its OWN arm: a decimal and an R761 process
number are typographically identical, so the form counts both. P8's existing
"bare numbers" arm is narrowed to bare INTEGERS accordingly.

Measured over all nine round-1+2 outboxes: 26 of 36 codes are prose.

Load-bearing measured (22 arms), four mutations all red against the whole
suite (16 / 10 / 1 / 2), green control 1734/5 and the golden byte-unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-15 02:11:29 +02:00
commit d74f32dc1c
15 changed files with 446 additions and 36 deletions

View file

@ -2641,6 +2641,55 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
verktøyet (structured-output-grensens klasse); debattens erklæring er RUN-nivå, så en approach
uten eget krav tilskrives kjøringens erklæringer og raden SIER at den gjør det; og feltet når
ikke `ProvenanceStamp` (stempelet beskriver gaten som dømte ÉN kandidat).
- **En «kostkode» må ha en FORM når inputen tilbyr former — og formene bor ÉTT sted, i validatoren
(P19 DEL B, 15.09):** P18s runde 2 endte med TO `validated` forslag hvis `affected_item`-koder var
vanlige ord fra en vegnormals prosa: `impulsventilator` (4 av 270 N500-dokumenter) og
`bituminøst bærelag` (4 av 1 133 N200). Begge er GRUNNET i P7s forstand (de står ordrett i
inputen) og ingen av dem er INERT i P18/B1s forstand (langt under 5 %-andelen) — de er bare ikke
identifikatorer for en kostlinje, og gaten hadde ikke noe stadium som kunne si det. **KJENT-POSITIV
MÅLT, ikke påstått:** begge spilt av offline mot basene kjøringene faktisk fikk gir `Rejection`
med nevner («…offers 391 identifiers of its own» / «…offers 1359»).
**`IDENTIFIER_FORMS` FLYTTET fra `generate.py` til `validator.py`** — de driver nå BÅDE P8s
rapport og P19/B3s gate, og to kopier av «hvordan en identifikator ser ut» ville latt rapporten og
gaten være uenige om ÉN kjørings egen input (kø-(p)); importretningen tvang det uansett
(`generate` importerer `validator`, aldri omvendt).
**B1 — to nye former, transkribert fra måling:** R761s kravnumre er bare punktum-tall (`12.1`,
`52.11`), ALLE SEKS `ref`-verdier i `contexts/kontrakt-sorasen-2027/fasit.json` er av den formen,
og INGEN av de to pre-P19-formene matchet én av dem — r761s hele tilbud var **3 identifikatorer
over 6,5 MB**. Målt etter: **2 332** (n100 435→578, n200 982→1 512, n500 272→391). Den fjerde
formen er `65 ASFALTDEKKER`. **Den FØRSTE formen ble UTVIDET i samme slengen, og dét er en
måling:** B2 gjorde de samme formene til `prose`/`identifier`-avgjørelsen, og repoets EGEN
`ENERGI-TOTAL-EL` matchet ingen av dem (form 1 krevde siffer etter separatoren) — klassifisereren
kalte altså en ekte kostkode prosa, og den nye gaten nektet den. En gate får bare ta feil i
retningen som slipper for mye inn. Form 2 fikk et valgfritt `_<n>`-suffiks fordi ÉN av de 26
fasit-referansene er `Krav 3.3.2—1_1`.
**TRE ting holder gaten fra å bli en regel om FASONGER:** (1) **generalitetsvernet** — gaten fyrer
KUN når inputen beviselig tilbyr identifikator-former; en base som ikke bruker dem kan ikke svares
i dem (M B-i → 16 røde, spredt over syv eldre testfiler); (2) **baseline-unntaket** — en kode
`CostBaseline` bærer nektes ALDRI her, fordi stadium 0 alt har dømt den en ekte linje av dette
prosjektet og det svakere stadiet skal ikke overprøve det sterkere (samme setning `_grounding_text`
bærer om sin tredje kilde); (3) **`has_identifier_form` FULL-matcher** — `impulsventilator 12.1`
gjør ikke ordet til en kostkode, og en delstrengregel ville latt enhver prosa-kode smugle med seg
en. **ÆRLIGHETS-GRENSE, MÅLT OG UTTALT SOM EGEN ARM:** et desimaltall og et R761-prosessnummer er
TYPOGRAFISK IDENTISKE (`42.5` vs `12.1`) og ingen regel skiller dem, så formen teller begge —
P8s eksisterende «bare tall telles ikke»-arm er derfor SNEVRET til bare HELTALL (K2s målte klasse,
46 394 forekomster) og desimal-tvetydigheten står i en egen, navngitt arm i stedet for i en
docstring. **B2:** `ProvenanceStamp.code_forms` er PÅKREVD uten default (`cost_baseline_anchored`s
grunn, skjerpet av formen: en tom map ville lest som «forslaget har ingen koder», og
`affected_items` KAN ikke være tom), og dommeren rapporterer `prose_codes` per approach — lest av
artefaktets eget `code_forms` når kjøringen skrev ett, og RE-DERIVERT med samme klassifiserer når
den ikke gjorde det, så runde 1 og 2 kan dømmes med samme instrument. **MÅLT over alle ni
runde-1+2-utbokser: 26 av 36 koder er prosa** (runde 1: 11 av 15 · runde 2: 15 av 21).
Load-bearing MÅLT (`tests/test_prose_code_form_loadbearing.py`, 22 armer), **fire mutasjoner alle
røde mot HELE suiten** + grønn kontroll **1734/5** (fra 1711/5, supersett, 0 fjernet) og golden
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` av INNHOLDET =
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): B-i gaten uten tilbuds-vilkåret (16) · B-ii
prosessnummer-formen droppet (10) · B-iii `prose` rapporteres aldri (1) · B-iv gaten detachet (2).
**Ærlighets-grenser, uttalt:** ingen betalt kjøring bekrefter at gaten endrer utfallet levende før
DEL E; formene er transkribert fra FIRE korpus og et femte kan bære en femte form (en ukjent form
er en kode som telles som prosa — feilretningen er nekt, ikke aksept, og dét er hva
generalitetsvernet og baseline-unntaket finnes for); og `assumptions`-nøkler er fortsatt utenfor
(økt 109s grense).
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.