Maalt oekt 48: en bundle-kjoering uten cost-baseline.json gikk rc 0 uten et ord, og `grep baseline provenance.py outbox.py` ga null treff - hverken stdout, stempelet eller utboksen bar at validatorens steg 0 ble hoppet over. To tenner, begge smaa, og begge fra kjoeringens ENE oppslag av baselinen (koe-(p)): 1. ProvenanceStamp.cost_baseline_anchored - PAAKREVD bool uten default. Begge defaults lyver: True lar en glemsom konstruktoer paastaa en ankring som ikke skjedde, False underrapporterer en ekte. Naar utboksen gratis (write_proposal dumper hele stempelet). DryRunReport baerer det samme - en dry-run stopper foer noe stempel finnes. 2. run.cost_baseline_notice(anchored) - ENESTE renderer, tar den alt opploeste booleanen, returnerer None naar kjoeringen ER forankret (omisjon, aldri en tom rad). Printes paa tre flater: --live-dry-run, full enkeltkjoering, og per prosjekt i portefoeljemodus. IKKE foldet inn i mandate.announce, og det er en MAALING: den fyrer kun med --mandate, saa nettopp de bare bundle-dry-runsene defekten ble maalt paa ville fortsatt sagt ingenting - og den renderes foer run_project, altsaa foer noen har opploest baselinen. Ankeringen forblir VALGFRI (en pre-amendment-base kjoerer uendret) - dette er synlighet, ikke en ny nekt. Golden-transkriptet er byte-uendret: demoen kjoerer en base som HAR fila. Load-bearing MAALT (tests/test_baseline_visibility_loadbearing.py, 11 tester), seks mutasjoner alle roede mot HELE suiten + groenn kontroll 885/5: konstant stamp-wiring (3 roede) - konstant dry-run-wiring (1) - detach dry-run-printen (1) - renderer returnerer alltid linja (2, inkl. den forankrede kontrollen) - detach full-run-printen (1) - detach portefoelje-printen (1). Portefoelje-armen er DEFENSIV og uttalt (ingen referanse-prosjekt setter bundle_dir; budget_stop-presedensen, crafted PortfolioResult). Det paakrevde feltet tvang fem eksisterende test-konstruktoerer til aa ta stilling. Dokumentene som beskrev den gamle stillheten er rettet: kunnskapsbase-for-en-kjoring.md S4.1 (tabellraden re-maalt live), S6 og S7; README «How it is set up»; CLAUDE.md S4.0-raden. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DA6HAF8HFQxGYC2h6ypRQe
49 lines
1.6 KiB
Python
49 lines
1.6 KiB
Python
"""Step 6 tests — first-class Pydantic provenance stamp.
|
|
|
|
At least one citation is mandatory (the load-bearing NFR); a valid stamp exposes the
|
|
proposal's provenance fields; the annotation adapter yields MAF citation dicts for display.
|
|
Pattern: tests/spikes/test_c_validator.py (pydantic raises + field assertions).
|
|
"""
|
|
|
|
import pytest
|
|
from pydantic import ValidationError
|
|
|
|
from portfolio_optimiser.provenance import Citation, ProvenanceStamp
|
|
from portfolio_optimiser.retrieval import TextSpan
|
|
|
|
|
|
def _stamp() -> ProvenanceStamp:
|
|
return ProvenanceStamp(
|
|
citations=[Citation(file="asphalt.txt", locator=TextSpan(0, 10), snippet="0123456789")],
|
|
model="qwen3:4b",
|
|
role="proposer",
|
|
validator_decision="validated",
|
|
token_usage=42,
|
|
cost_baseline_anchored=True,
|
|
)
|
|
|
|
|
|
def test_zero_citations_is_rejected() -> None:
|
|
with pytest.raises(ValidationError):
|
|
ProvenanceStamp(
|
|
citations=[],
|
|
model="m",
|
|
role="proposer",
|
|
validator_decision="validated",
|
|
token_usage=1,
|
|
cost_baseline_anchored=True,
|
|
)
|
|
|
|
|
|
def test_valid_stamp_exposes_provenance_fields() -> None:
|
|
stamp = _stamp()
|
|
assert len(stamp.citations) >= 1
|
|
assert stamp.model and stamp.role
|
|
assert stamp.validator_decision in {"validated", "rejected"}
|
|
assert isinstance(stamp.token_usage, int) and stamp.token_usage > 0
|
|
|
|
|
|
def test_to_annotations_yields_citation_dicts() -> None:
|
|
anns = _stamp().to_annotations()
|
|
assert anns and all(a["type"] == "citation" for a in anns)
|
|
assert anns[0]["annotated_regions"][0]["type"] == "text_span"
|