feat(s7b): IR-projeksjonen er valgfri paa alle tre kallsteder, og fraveret sies

DEL A + DEL C av ordre 20260903T204605Z-215167684-from-.claude.

Soem 1: okf.load_optional_ir_projection ved siden av den fail-faste, moensteret
fra load_cost_baseline/load_optional_cost_baseline. Et PAR, ikke et required=-flagg:
PM-tillegg 5 maalte hva en uoevet parameter koster (elleve evidence_for-kallsteder
brukte defaulten til den andre grenen raatnet), og et flagg ville dessuten gjort
usanne de fem docstringene som siterer load_ir_projection som DEN fail-faste
presedensen. Toleransen stopper ved fravaer: en malformed projeksjon reiser fortsatt.

De tre kallstedene fikk HVER SIN stilling:
  * _project_from_bundle - fravaer hopper over en fail-fast som ikke har noe aa
    sjekke mot; en projeksjon som FINNES og navngir et annet prosjekt nekter
    fortsatt. Divergens-vakten er kontrakten multi-base-dispatchen hviler paa.
  * run_mandate_across_bundles - FILA FOERST, basens ERKLAERTE bundle_id som
    fallback (S7a-3). Presedensen baerer i begge retninger: erklaering-foerst ville
    re-adressert hver eksisterende base der project_id != bundle_id.
  * bundle_candidate_features - optional_bundle_candidate_features, og de TO
    konsumentene svarer ULIKT paa fravaeret. Steg-1-folden HOPPER OVER og sier
    hvor mange tidligere dommer som dermed aldri naadde hypotese-prompten;
    seed_store_from_bundle NEKTER ved navn (VerdictKeyUnavailable), fordi aa mynte
    en noekkel for en dom som erklaerer ingen er nettopp defekten S3.2 lukker.

Synligheten: RunResult.unkeyed_verdicts (ANTALL, ikke flagg - koe-(y)-regelen) +
run.unkeyed_verdicts_notice som ENESTE renderer, None ved null (omisjon, aldri tom
rad). Baereren er MAALT: dry-run-kuttet returnerer OVER folden, saa et felt paa
DryRunReport kunne bare rapportert null - ulikt cost_baseline_anchored og
skipped_links, begge opploest over kuttet. Ikke paa ProvenanceStamp: stempelet
beskriver gaten som doemte EN kandidat.

Prosjektnavnet var et ikke-spoersmaal, og det er maalt: SavingsProposal har intet
navnefelt, saa projeksjonen har aldri vaert en navnekilde. Project.name kommer
fortsatt fra type: project-konseptets title med id-en som siste utvei.

DEL C: --mandate lagt i report_forbidden. Den var ELDRE enn partisjonen og hadde
aldri faatt en rad, saa --report --ledger X --mandate Y droppet kommisjonen i
STILLHET - F4-klassen. Testarmen kjoerer mot en argv report-modus ellers ville
AKSEPTERT, med en kontroll som beviser rc 0 uten flagget.

Kontroll: 1290 passed / 5 skipped (fra 1275/5 - supersett, 0 fjernet).
Golden demo-transcript.stdout BYTE-UENDRET, shasum -a 1 (INNHOLD, ikke git-blob)
= ea8c534773acdbe41ae68f2c55724d69aaf8be4f. ruff + mypy rene.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-04 08:09:03 +02:00
commit b75387ca25
4 changed files with 593 additions and 14 deletions

View file

@ -503,10 +503,36 @@ def _features_from_ir(ir: dict[str, Any]) -> ProposalFeatures:
def bundle_candidate_features(bundle_dir: str) -> ProposalFeatures:
"""The pre-hypothesis ExpeL query key: the candidate measure's structural features, read from
the OKF bundle's IR projection. Available BEFORE any proposal is generated — which is what lets
Step-1 retrieve prior verdicts and fold them into the hypothesis prompt (målbilde §2 step 1)."""
Step-1 retrieve prior verdicts and fold them into the hypothesis prompt (målbilde §2 step 1).
Fail-fast. Use ``optional_bundle_candidate_features`` where a base without a projection is
legitimate and note that its two consumers answer that absence DIFFERENTLY, on purpose."""
return _features_from_ir(okf.load_ir_projection(bundle_dir))
def optional_bundle_candidate_features(bundle_dir: str) -> ProposalFeatures | None:
"""``bundle_candidate_features`` where a base with no IR projection is legitimate: ``None``
instead of raising (S7b søm 1). An ingested corpus has no hand-written projection, so it has no
pre-hypothesis candidate to key retrieval against a fact about the base, not an error.
Tolerance stops at absence, as everywhere: a projection that exists but is malformed still
raises (``okf.load_optional_ir_projection``'s rule)."""
ir = okf.load_optional_ir_projection(bundle_dir)
return None if ir is None else _features_from_ir(ir)
class VerdictKeyUnavailable(ValueError):
"""A ``type: verdict`` concept declares no structural key AND its bundle has no IR projection to
fall back on (S7b søm 1).
Fail-closed, and the ONE place in this seam where absence is not tolerable. The pre-S3.2
fallback keys such a verdict on the bundle's projection candidate; with no projection there is
no candidate, and minting a key anyway would attach the verdict to a candidate it is not about
precisely the defect S3.2 closes. Validation, never repair (``write_concept_file``'s rule): the
three fields are the author's to declare, and ``promote_verdict`` writes them, so a base grown
by the loop itself is never affected."""
# S3.2: a verdict file MAY carry its own structural key. All three fields or none — see
# ``_features_from_verdict_frontmatter``.
_STRUCTURAL_FRONTMATTER_KEYS = ("affected_codes", "measure_type", "claimed_saving_nok")
@ -618,7 +644,15 @@ def seed_store_from_bundle(bundle_dir: str) -> VerdictStore:
if features is None:
# Read the IR projection lazily: a bundle whose verdicts all carry their own key does
# not need one, and this keeps the fallback path's behaviour byte-identical.
fallback = fallback if fallback is not None else bundle_candidate_features(bundle_dir)
fallback = (
fallback if fallback is not None else optional_bundle_candidate_features(bundle_dir)
)
if fallback is None:
raise VerdictKeyUnavailable(
f"{vf.name}: this verdict declares no structural key "
f"({', '.join(_STRUCTURAL_FRONTMATTER_KEYS)}) and the knowledge base carries no "
"IR projection to fall back on, so there is no candidate to key it to"
)
features = fallback
verdicts.append(
capture_verdict(