feat(prepass): --prepass-payload with six refusals by name, README and the hosted surface untouched

Steg 6 og 7 av planen. Flagget lastes fail-fast ved siden av `--mandate` (samme
try/except, saa manglende/ugyldig fil lander paa `run refused:` uten traceback) og
traades inn i BEGGE `run_project`-dispatchene -- dry-run og full kjoering. En egen arm
SPIONERER paa argumentet, ikke paa exit-koden: et flagg som parses, valideres og
droppes er F4-klassen, og de to utfallene er samme rc.

SEKS NEKTER, hver ved NAVN, hver med en rc-0-kontroll paa en argv som ellers ville
blitt AKSEPTERT:
- `report_forbidden` -- report-modus returnerer OVER hver dispatch, saa en utelatelse
  er et stille DROPP. Kontrollen bruker en JSON-ARRAY-ledger; et objekt ville gjort
  armen roed av feil grunn (maalt i oekt 89).
- `single_only` -- navngir `--portfolio`, ALDRI det delte `--prepass-payload`-tokenet:
  en droppet rad faller gjennom til `--bundle-dir`-kravet, hvis melding ogsaa navngir
  flagget, saa en arm paa det delte tokenet ville staatt groenn mot sin egen mutasjon.
- krever `--bundle-dir`; nektet med `--proposals-from-mandate` (returnerer over
  debatten, saa flagget ville vaert stille inert), med `--dimension-config` (pre-passet
  kuttet uten aa kjenne dimensjoner, saa aa aere skopet ville droppe utdrag
  deklarasjonen teller som LEVERT -- da er nevnerne feil for kjoeringen som publiserte
  dem) og med `--explore` (utforskningen leser HELE basen med de fire verktoeyene
  payloadet trekker, saa kjoeringen som helhet ville lest langt utenfor kuttet den
  erklaerer).

Blokka ligger paa FUNKSJONS-nivaa etter mode-dispatchen, aldri nestet under en annen
grens -- under en av dem ville en bar kombinasjon falt rett gjennom.

`hosting.py` er BEVISST URØRT (briefens non-goal, MAJOR-4/S7b-presedensen): feltet
kommer inn i ingen av de tre settene, saa den generiske `unknown field(s)`-400-en
svarer alt, og Fase 4es to halvdeler staar. Gatet av en testarm i stedet for en
redigering -- inkludert den negative halvdelen (hvert videresendt felt ER en
`run_project`-parameter, hvert konsumert er det ikke).

README-blokka navngir alle seks partnerne, uttrykker seg i kundevendt terminologi
(aldri "OKF bundle") og sier BEGGE aerlighets-grensene hoeyt: dette kjoeper et
DEKLARERT kutt, ikke en billigere kjoering; og en TOM leveranse er bevis for fravaer
mens en FULL ikke er bevis for tilstedevaerelse. Uttrekkeren tar BLOKKA (ikke en
delstreng over hele fila -- `--portfolio` og `--report` staar overalt), med
`--plan-review`-blokka som kjent-positiv kontroll.

1466 passed / 5 skipped (fra 1446/5, +20, 0 fjernet). ruff + mypy rene. Golden
`shasum -a 1` av INNHOLDET = ea8c534773acdbe41ae68f2c55724d69aaf8be4f, BYTE-UENDRET.

Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-07 11:27:35 +02:00
commit ca98888358
3 changed files with 535 additions and 0 deletions

View file

@ -481,6 +481,38 @@ when the seam is detached, so the loop cannot silently degrade into theater.
— blocking an HTTP request on a human would also block the event loop that answers
`/readiness`.
**Declaring the cut instead of navigating for it (`--prepass-payload`).** By default the debate
is handed a pointer and reads the knowledge base itself, opening what it judges relevant. That
works, and it leaves one thing unsaid: how much of the base was *not* read, and why. With
`--prepass-payload` you hand the run a cut that was computed ahead of time and that states its
own denominators — how many concepts were considered, how many delivered, how many withheld, and
under which rule. The debate is given that cut and the navigation tools are withdrawn, because a
debate that can still go looking is not bound by the cut it just declared.
The file is produced outside this framework, by any tool that emits the `okf-consumption/1`
payload shape. Nothing is taken on trust: every delivered excerpt is checked against the document
it names in the knowledge base — the same file, the same bytes, the same text — before a single
model call, so a payload cannot smuggle in text the base does not contain. Expert verdicts and
out-of-scope material are refused here exactly as the navigation tools refuse them.
```bash
uv run python -m portfolio_optimiser.run BYGG-KONTOR-NORD --docs-dir <docs> \
--bundle-dir <knowledge-base> --prepass-payload cut.json --outbox-dir out --run-id r1
```
The cut is printed and written to `{run_id}-prepass.json`. **This buys a declared cut, not a
cheaper run** — the delivered text costs more than a pointer does. And note the limit it inherits:
an *empty* delivery is evidence the base does not answer the question, but a *full* one is not
evidence that it does. The excerpts are the closest matches, which is not the same as an answer;
the run says so to the agents in as many words.
Requires `--bundle-dir`. Refused with `--portfolio` (one payload is a cut of one base at one
point in time), with `--report` (which never runs a debate), with `--proposals-from-mandate`
(which returns before the debate), with `--dimension-config` (the cut was computed without
knowing about scopes, so honouring one would drop excerpts its own denominators count as
delivered) and with `--explore` (the exploration reads the whole base with the very tools the
payload withdraws).
**Answering it days later (`--checkpoint-dir` / `--resume`).** A domain expert is rarely at the
terminal when the loop reaches the plan, so the same review can be *parked* to disk instead.
`--checkpoint-dir` writes the suspended workflow there and the open question to