feat(prepass,explore): padding dies at the prompt; a refusal the model can act on is a return value [skip-docs]
Order 20260908T195801Z. Findings 4 and 5 from the S7 acid test, then the two things finding 99 measured and deliberately did not fix (D3, D2). No user-facing surface changes: no new flag, no new command, no changed output contract. Both seams are internal (the pre-pass rendering, and the shape a tool answers a model with), so [skip-docs] rather than a README edit that would describe nothing an operator can do differently. FINDING 4 -- MEASURED, NOTHING BUILT. K2's price schedule IS readable without guessing (8 column spans, 71 of 91 non-blank rows give >= 2 cells, the split stable for K = 2..64). But 0 of 92 rows name all three of code/quantity/unit_cost -- also under a looser substring match -- and 0 of 91 data rows carry code + quantity + amount. The triple is not formatted away; it is not in the document. It is a price SUMMARY plus nine rate cards whose unit-price columns are empty (pre-award). The order's binding decision rule therefore falls against building: --derive-cost-baseline keeps refusing, and MAJOR-4's own honesty limit holds. FINDING 5 -- BUILT. Measured on the actual rendering path (concept_text, not the raw file): the delivered excerpt is 104 lines / 67 245 chars, carrying 208 interior whitespace runs, 117 of them >= 100 and the longest 887 -- 56 806 of 67 245 characters = 84.5 %, over 72 of 104 lines. collapse_padding, called from _data_blocks (the one renderer both arms share, and therefore AFTER verify_against_bundle -- collapsing in concept_text would break every payload's own digest), gives -72.4 %: line count invariant, non-whitespace byte-identical, leading indentation untouched, no number changed. F99-D3 -- read_file / read_dir / read_bundle now RETURN their refusal. MAF turns a tool raise into "Error: Function failed." (_tools.py:1410-1432, :1427) and counts it against DEFAULT_MAX_CONSECUTIVE_ERRORS_PER_REQUEST = 3, so everything the refusing arm knows is destroyed on the way out. The gates are unchanged; the property they exist for -- the reason travels, the bytes never do -- is now asserted explicitly on the returned value. The arm is keyed on named classes, never bare Exception, because ExplorationError is itself a RuntimeError subclass. F99-D2 -- the invariant row, plus one for finding 5 (a stated deviation from "one row only": finding 5 is a separately built seam and the ledger's standing rule requires its own row). 19 existing arms rewritten, never deleted and never weakened: where the class carried a distinction, the refusal KIND carries it now. 13 mutations, all red against the whole suite (W1-W5, M1-M8), each restored from scratchpad with shasum -c. Control 1543 passed / 5 skipped (from 1529/5, a strict superset, 0 removed). Golden demo-transcript.stdout unchanged (shasum -a 1 of the CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f). Measurement: docs/2026-09-08-funn-4-5-og-read-nekt.md Co-Authored-By: Claude <Opus 5>
This commit is contained in:
parent
078a099898
commit
eb4137415c
13 changed files with 975 additions and 69 deletions
65
CLAUDE.md
65
CLAUDE.md
|
|
@ -1839,6 +1839,71 @@ Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 —
|
|||
nekten er nøklet på den ENE erklærte typen ExpeL-folden eier, aldri på et bredere
|
||||
«ikke i `context_files`»-komplement, som M4 viser ville rammet `index.md` — navigasjon, ikke en
|
||||
dom. Måling: `docs/2026-09-04-s2c-debatt-k2.md` § 8.
|
||||
- **En nekt modellen skal kunne RETTE SEG ETTER er en RETURVERDI, aldri et raise — og dét gjelder
|
||||
HVERT verktøy, ikke bare det ene funn 99 rakk (funn 99 D2+D3, 08.09):** MAF gjør en tools
|
||||
exception om til den ugjennomsiktige strengen `"Error: Function failed."`
|
||||
(`agent_framework/_tools.py:1410-1432`), undertrykker detaljen med mindre `include_detailed_errors`
|
||||
er satt (`:1427`), og teller den mot `DEFAULT_MAX_CONSECUTIVE_ERRORS_PER_REQUEST = 3` (`:96`,
|
||||
`:2718-2731`). Alt den nektende armen VET — hvilke base-id-er som finnes, hvilket dokument stien
|
||||
egentlig navngir, hvilken rung som leser det — tilintetgjøres altså på vei ut, og tre av dem
|
||||
avslutter forespørselen. MÅLT live: modellen gjettet på JSON-formatet, som var riktig hele veien,
|
||||
og trippelen rett foran en Azure-400 var tre `read_file`-nekter på samme dokument. `quick_validate`
|
||||
returnerer `{"decision":"refused",…}` og hver gren — refusal inkludert — står i `quick_validations`;
|
||||
`read_bundle`/`read_dir` returnerer `{"refused", "refusal"}` (ingen nøkkel en vellykket listing
|
||||
har), `read_file` en streng med sentinel-prefikset `REFUSED (<kind>): ` fordi en mapping DER ville
|
||||
endret formen på hver VELLYKKET lesning og dermed hver prompt-byte S2c målte. **`refusal`-KINDEN er
|
||||
ikke pynt:** klasse-distinksjonen `pytest.raises` bar før (F3s «dette er et dokument» vs «dette
|
||||
finnes ikke») ville forsvunnet med raisen, så den bæres som felt, avledet av `type(exc).__name__` —
|
||||
ÉN kilde, aldri en andre tabell. **GATENE ER URØRT** — verdict-laget og §4.1a-dimensjonen nekter
|
||||
nøyaktig det de nektet før — og egenskapen de finnes for asserteres nå EKSPLISITT på returverdien:
|
||||
**grunnen reiser, bytene aldri.** **Armen er nøklet på KLASSEN, aldri bar `Exception`:**
|
||||
`_RETURNABLE_REFUSALS` navngir hver klasse én for én, fordi `ExplorationError` selv er en
|
||||
`RuntimeError`-subklasse, så `except RuntimeError` ville slukt en feil ingen har navngitt og gjort
|
||||
en ukjent svikt om til et selvsikkert svar — verre enn den ugjennomsiktige strengen. Load-bearing
|
||||
MÅLT (`tests/test_explore_read_tools_refusal.py`, 7 armer), åtte mutasjoner alle røde mot HELE
|
||||
suiten + grønn kontroll 1543/5 og golden BYTE-UENDRET: M1 `read_file` raiser igjen (12) · M2
|
||||
`read_dir` (10) · M3 `read_bundle` (3) · M4 armen fanger bar `Exception` (2 — kjent-negativen PLUSS
|
||||
et eldre, uavhengig vitne, `test_a_nonexistent_sibling_is_still_an_os_error`) · M5 nekten dropper
|
||||
grunnen (10) · M6 dropp `refusal`-kinden (9) · M7 `read_file` mister sentinel-prefikset (12) ·
|
||||
M8 nekten bærer det nektede dokumentets bytes (8, fem i eldre tester). **Ærlighets-grenser,
|
||||
uttalt:** et dokument hvis første tegn ER sentinelen ville vært uskillbar fra en nekt — en ekte
|
||||
luke, skrevet ned i stedet for bortforklart; en `OSError` (f.eks. `FileNotFoundError` på en sti
|
||||
som ikke finnes) står UTENFOR settet og propagerer fortsatt, gatet av en eksisterende arm; og at en
|
||||
LEVENDE modell faktisk RETTER seg etter nekten er ikke bevist (structured-output-grensens klasse).
|
||||
Måling: `docs/2026-09-08-funn-4-5-og-read-nekt.md` § 3.
|
||||
- **Et regneark-renders PADDING dør på vei inn i prompten; ingenting annet gjør det (funn 5, 08.09):**
|
||||
S7c hadde alt FELT den åpenbare kuren — begge låser åpnet LEVERTE K2s prisskjema (rang 10, bytene i
|
||||
2 av 11 prompter) og modellen siterte `5647500`/`prissammenstilling`/`prisskjema`/`pris` i **0 av 4**
|
||||
og **0 av 11** svar. Blindsonen hadde flyttet seg fra RANGERINGEN til LESNINGEN, og dét po eier der
|
||||
er rendringen. **MÅLT PÅ DEN FAKTISKE RENDRINGS-STIEN** (`prepass.concept_text`, ikke råfila): det
|
||||
leverte utdraget er **104 linjer / 67 245 tegn** — hvilket AVSTEMMER de to nevnerne (119 linjer /
|
||||
101 188 B på disk, samme dokument før frontmatter-splitten og produsentens per-linje `rstrip`) — og
|
||||
bærer **208** indre mellomrom-løp ≥ 2, **117** av dem ≥ 100 og det lengste **887**, til sammen
|
||||
**56 806 av 67 245 tegn = 84,5 %** over **72 av 104 linjer**. Etikett og beløp nådde altså modellen
|
||||
hundrevis av tegn fra hverandre (`82` · 593 mellomrom · beskrivelse · 250 mellomrom · `5647500.0`).
|
||||
Etter: **67 245 → 18 531 tegn (−72,4 %)**, linjeantall uendret, ikke-whitespace byte-identisk, og
|
||||
linja leser `82 Prosjektering (tiltransport av prosjekterende) 5647500.0`. **INGEN SKJØNN, og det er
|
||||
fire løfter med hver sin gate:** indre løp av mellomrom/tab kollapser til ÉN (K = 2, altså finnes
|
||||
det ingen terskel å forsvare); **LEDENDE whitespace er URØRT** (indentering er markdown-STRUKTUR —
|
||||
nestede lister, indentert kode — og en regel som flatet den ville omskrevet dokumenter i stedet for
|
||||
å av-padde dem; dét er hva `(?<=\S)` kjøper); **ingen newline røres**, så linjeantallet er invariant
|
||||
og ingen rad slås sammen med naboen (en naiv `re.sub(r"\s+", " ", text)` gjør en tabell til et
|
||||
avsnitt); **ingen ikke-whitespace-tegn røres**, så intet tall kan endres — asserten er den
|
||||
PER-LINJE ikke-whitespace-SEKVENSEN, ikke «sifrene finnes fortsatt et sted». **HVOR den bor er det
|
||||
bærende valget:** i `_data_blocks`, den ENE rendreren begge armer deler (kø-(p) — en fiks i
|
||||
`render_context` alene ville latt utforskningen lese padding), og derfor ETTER
|
||||
`verify_against_bundle`. `concept_text` er re-utledningen en payloads bytes bindes til, så en
|
||||
kollaps DER ville fått hver payload noensinne skrevet til å feile sin egen digest og opphevet gaten
|
||||
som stopper en payload fra å levere bytes basen ikke holder. Load-bearing MÅLT
|
||||
(`tests/test_prepass_padding_collapse_loadbearing.py`, 7 armer), fem mutasjoner alle røde mot HELE
|
||||
suiten + grønn kontroll 1543/5 og golden BYTE-UENDRET: W1 detach kollapsen fra `_data_blocks` (2) ·
|
||||
W2 kollaps ledende whitespace også (1, indenterings-armen ALENE) · W3 kollaps newlines også (4,
|
||||
inkl. kontrollen) · W4 skriv om tall (2) · W5 kollaps inne i `concept_text` (1, separasjons-armen
|
||||
alene). **Ærlighets-grenser, uttalt:** kollaps til ett mellomrom mister CELLE-GRENSEN — `Post SUM`
|
||||
blir uskillbar fra prosa med de to ordene — og å finne opp en skilletegn ville vært nøyaktig det
|
||||
skjønnet regelen unngår, uten en måling som sier hvilket en modell leser bedre; at en modell så
|
||||
BRUKER tallene er IKKE vist (det krever en betalt kjøring, som ikke er bestilt).
|
||||
Måling: `docs/2026-09-08-funn-4-5-og-read-nekt.md` § 2.
|
||||
- **Eksperten kan svare på forslaget som ligger på BORDET — og svaret BRUKES, det registreres ikke
|
||||
bare (MAJOR-2, økt 88–89):** før dette fantes ÉN menneske-søm inne i løkka, `plan_reviewer`, og
|
||||
den fyrer FØR noen hypotese finnes. **Nevneren er målt** (`docs/2026-09-02-misjonsreview-v2.md`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue