portfolio-optimiser/tests/fixtures
Kjell Tore Guttormsen 277bb95777 feat(validator,generate,run): an identifier a proposal builds on must be in the input, or the verdict falls [skip-docs]
P6 (økt 108) ended in ValidatedProposal (verdict 5fd6272e3725fe68) on two cost
codes -- M-04-01 / M-04-03 -- that appear in NO prompt of that run. Measured
here first, verbatim: validate_proposal(p, baseline=None) validates it; the same
proposal against any non-empty CostBaseline is rejected naming both codes.

So the hole was never "fabrication goes uncaught" -- _reconcile_against_baseline
exists and is right -- but that the falsifier is reached only through
`if baseline is not None`. The input always exists; the baseline does not.

New stage 0b (_ground_against_input), OUTSIDE the baseline branch, after stage 0
so an anchored run's message is byte-identical to before. ONE Rejection, the
validator's own type, naming EVERY ungrounded identifier "; "-joined in the
proposal's own order (økt 94's completeness reason).

The rule has NO pattern -- `code in grounding`, exact substring -- and that is a
measurement: over the delivered corpora (K2 1108 files / 2 005 561 chars, the
three N payloads 8 excerpts each) the identifier forms are heterogeneous, and a
pattern chosen to cover them would be a rule about shapes. Bare numerals are the
one inert class (46 394 occurrences / 2 117 distinct in K2); the rule fails OPEN
there, never closed.

Evidence is three non-model-authored sources: what run_project DELIVERED (the
rendered cut/pointer/chunks plus the base's context_files -- never files, which
would make the type: verdict layer evidence), the project's own cost lines, and
the baseline's codes when anchored. The rendered PROMPT is deliberately NOT
evidence, on two measurements: gen_context IS the debate output on the S2c path,
and from attempt 2 the prompt carries the previous Rejection.reason verbatim --
which for this stage QUOTES the identifier it just refused. Grounding in the
prompt would let the gate's own refusal disarm it on its second round.

Prose scanning was chosen against WITH THE NUMBERS: a typed gate catches 2/2
(P6) and 2/2 (S7c) -- 100% of what reached a verdict. What stays uncaught, said
plainly: an ungrounded identifier that lives only in agent/debate prose and never
becomes an affected_item code (2 of 4 P6, 2 of 4 S7c, 1 of 2 P4).

Iron Law: 9 red / 2 green before the rule existed. Ten mutations all red against
the whole suite, green control 1558 passed / 5 skipped (from 1543/5, superset,
0 removed), golden demo-transcript.stdout BYTE-UNCHANGED (shasum -a 1 of the
CONTENT = ea8c534773acdbe41ae68f2c55724d69aaf8be4f).

Three existing fixtures changed, no gate weakened -- most of all
test_pre_amendment_bundle_runs_unchanged, which sent the SAME FABRICATED code and
asserted it validated: the økt-108 hole written down as an expectation.

No paid run. Order 20260909T113641Z-38938691-from-.claude.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-09 16:04:01 +02:00
..
k2-prisskjema-SYNTETISK feat(okf): derive a cost baseline from a priced schedule, or refuse 2026-09-03 03:15:39 +02:00
k2-prisskjema-uprisert-SYNTETISK feat(okf): derive a cost baseline from a priced schedule, or refuse 2026-09-03 03:15:39 +02:00
p7-grounding feat(validator,generate,run): an identifier a proposal builds on must be in the input, or the verdict falls [skip-docs] 2026-09-09 16:04:01 +02:00
prepass feat(prepass): payload model, loader, shape gate and the binding to the mounted base [skip-docs] 2026-09-07 10:52:20 +02:00
README.md feat(okf): derive a cost baseline from a priced schedule, or refuse 2026-09-03 03:15:39 +02:00

Test fixtures

k2-prisskjema-SYNTETISK/ and k2-prisskjema-uprisert-SYNTETISK/

SYNTETISK — every number in these bundles is invented. They copy the structure of a public-tender Prisskjema.xlsx (NS 3451 posts: code, description, unit, quantity, unit price, sum) and nothing else. No private tender document is reproduced here, and the unit prices in the priced variant are round synthetic values chosen to be obviously not a real bid — they must never be read as anyone's pricing. This is the first building block of the S7b corpus.

The two variants are the two halves of the same measurement:

bundle Enhetspris column what it stands for
k2-prisskjema-SYNTETISK/ filled a priced schedule a baseline CAN be derived from
k2-prisskjema-uprisert-SYNTETISK/ empty K2's actual pre-award state — codes and descriptions, no quantities or prices

The table form is MEASURED, not hand-typed

Both bodies are the verbatim output of the producer's own office conversion, so the fixture states something about the format rather than recording our own idea of it:

pandoc -f xlsx -t markdown --eol=lf --wrap=none <sheet>.xlsx

with pandoc 3.10.2 — the writer and the two arguments are llm_ingestion_okf.extract's _PANDOC_WRITER / _PANDOC_ARGS. That is a pandoc simple table (a dash rule whose groups define the column spans), NOT the pipe table render_table emits: measured in session 78, the xlsx path reaches a concept body through inbox.py's render_inbox_concept(sanitized_text, …) and never touches render_table at all. An empty spreadsheet cell therefore arrives as blank space inside its column span — which is exactly what the unpriced variant has to carry for the refusal it gates to be the real one.