feat(hitl): ekspertdommen kan ikke oppstaa av stillhet (F2, ORDRE 20260825T214801Z)

run_project KREVDE verdict_input og kjorte capture_verdict ubetinget; CLI-en
defaultet det til {"approved", "reviewed by expert"} og hosting listet det som
PAAKREVD. Netto: hver flaggloes kjoering myntet en ekspertgodkjenning ingen ga,
den gikk inn i den delte storen, og run_portfolio bar den inn i neste prosjekts
hypotese-prompt som en prior expert verdict -- paa flaten som ble overlevert
14.08. Non-goal 3, brutt i en soem.

RunResult.verdict er naa Verdict | None, og None er hva stillhet produserer:
ingenting myntes, ingenting lagres, ingenting varsles. Prinsippet sto allerede i
repoet -- RunFailure sin docstring: aa fylle et felt med en dummy legger
FABRIKKERT proveniens inn i aggregatet.

Traceability koster ingenting: RunResult.verdict_key (property, derivert fra
kandidaten) er verdicts.verdict_key sitt alt dokumenterte formaal -- identisk
med verdict.id naar en dom BLE gitt, og fortsatt meningsfull naar ingen ble det.
Det er den outboxen og den hostede responsen stempler.

Halv dom NEKTES paa begge doerer (FeedbackContract er eneste sted formen
valideres; CLI-en nekter ved navn FOER enhver mode-dispatch). Validering, aldri
reparasjon. De to mode-partisjonene fikk --decision/--rationale inn: kommentarene
sa ordrett at en aerlig nekt var uimplementerbar fordi de non-None
argparse-defaultene gjorde en eksplisitt verdi uskillbar fra defaulten -- med
defaultene borte er den implementerbar.

Hosting er WIDENING, ikke bryting: verdict_input flyttet fra _REQUIRED_FIELDS
til _OPTIONAL_FIELDS. Ingen ekstern kaller brekker.

AERLIGHETS-GRENSE: referanse-fixturens SYNTETISKE verdict_input-rader staar
uroert -- de er merket SYNTETISK paa fire steder og er reviewens F5 (maaling av
misjonspaastanden), ikke F2. Project.verdict_input er naa valgfri.

Load-bearing MAALT (tests/test_ungiven_verdict_loadbearing.py, 15 armer), aatte
mutasjoner alle roede mot HELE suiten + gronn kontroll 1080/5 og golden
demo-transcript.stdout BYTE-UENDRET (ea8c534773acdbe41ae68f2c55724d69aaf8be4f).
En mutasjon falsifiserte testen foerst (vakuoes-gate-klassen, ellevte gang):
--report-armen brukte et bart --report, som nekter rc 1 uansett fordi --ledger
mangler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-08-27 01:22:07 +02:00
commit 56f4f6d084
12 changed files with 685 additions and 49 deletions

View file

@ -234,7 +234,9 @@ async def test_invocations_wires_payload_into_run_project(
assert body["outcome_type"] == "validated"
assert body["p90"] == 200.0
assert body["checker_verdict"] == "approve"
assert body["verdict_id"] == "vid-hosted"
# F2: the response quotes the candidate's verdict KEY, not the captured verdict's id — the two
# coincide on a real run that recorded one, and only the key survives a run nobody reviewed.
assert body["verdict_id"] == _result(_VALIDATED).verdict_key
assert body["proposal"]["measure"] == "LED-retrofit av kontorbelysning"
assert body["provenance"]["model"] == "synthetic"
@ -271,7 +273,9 @@ async def test_unknown_field_is_refused_never_repaired(
assert len(recorder.calls) == 1
@pytest.mark.parametrize("missing", ["project_id", "docs_dir", "verdict_input"])
# ``verdict_input`` is deliberately ABSENT from this list since F2: it is optional, and the arms
# proving the relaxation live in ``tests/test_ungiven_verdict_loadbearing.py``.
@pytest.mark.parametrize("missing", ["project_id", "docs_dir"])
async def test_missing_required_field_is_400(
served: str, monkeypatch: pytest.MonkeyPatch, missing: str
) -> None: