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 — blocking an HTTP request on a human would also block the event loop that answers
`/readiness`. `/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 **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. 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 `--checkpoint-dir` writes the suspended workflow there and the open question to

View file

@ -120,6 +120,7 @@ from portfolio_optimiser.validator import (
validate_proposal, validate_proposal,
) )
from portfolio_optimiser import hitl, okf, outbox, prepass from portfolio_optimiser import hitl, okf, outbox, prepass
from portfolio_optimiser.prepass import load_prepass_payload
from portfolio_optimiser.semretrieval import ( from portfolio_optimiser.semretrieval import (
SEMANTIC_WEIGHT_DEFAULT, SEMANTIC_WEIGHT_DEFAULT,
Embedder, Embedder,
@ -2378,6 +2379,22 @@ def main(argv: list[str] | None = None) -> int:
"round trip is recorded in {run_id}-exploration.json, feedback verbatim. Input that ends " "round trip is recorded in {run_id}-exploration.json, feedback verbatim. Input that ends "
"without an answer is an error, NEVER a sign-off", "without an answer is an error, NEVER a sign-off",
) )
parser.add_argument(
"--prepass-payload",
default=None,
metavar="FILE",
help="Hand the debate a DECLARED CUT of the knowledge base instead of the pointer and the "
"four navigator tools. FILE is one contract-conformant OKF consumption pre-pass payload "
"(okf-consumption/1): the delivered excerpts plus the three denominators — how many "
"concepts were considered, how many withheld, and by which rule. Every excerpt is verified "
"against the mounted document (identity, sha256, and the delivered text re-derived from "
"the base) before a single model call, so a payload cannot deliver bytes the base does not "
"hold; the verdict layer and the dimension scope are refused here exactly as the navigator "
"tools refuse them. The cut is recorded in {run_id}-prepass.json and printed. This makes "
"the run's cut DECLARED rather than emergent; it does not make it cheaper. Requires "
"--bundle-dir; refused with --portfolio, --report, --proposals-from-mandate, "
"--dimension-config and --explore.",
)
parser.add_argument( parser.add_argument(
"--proposal-review", "--proposal-review",
action="store_true", action="store_true",
@ -2634,6 +2651,10 @@ def main(argv: list[str] | None = None) -> int:
# MAJOR-2, and for the identical reason: report mode returns above the run dispatch, so # MAJOR-2, and for the identical reason: report mode returns above the run dispatch, so
# an omission here is a SILENT DROP — the door would be accepted and never asked. # an omission here is a SILENT DROP — the door would be accepted and never asked.
"--proposal-review": args.proposal_review, "--proposal-review": args.proposal_review,
# The pre-pass door, listed for the same reason as its neighbours: report mode returns
# ABOVE every dispatch, so an omission here is a silent DROP — the operator would be
# told nothing and the declared cut would simply never happen.
"--prepass-payload": args.prepass_payload is not None,
# The three U12 flags, listed for exactly that reason: report mode returns before the # The three U12 flags, listed for exactly that reason: report mode returns before the
# resume dispatch, so an omission here is a silent drop, not a refusal. # resume dispatch, so an omission here is a silent drop, not a refusal.
"--checkpoint-dir": args.checkpoint_dir is not None, "--checkpoint-dir": args.checkpoint_dir is not None,
@ -2717,6 +2738,12 @@ def main(argv: list[str] | None = None) -> int:
# them apart. BY NAME, like its neighbours: falling through to a --bundle-dir # them apart. BY NAME, like its neighbours: falling through to a --bundle-dir
# requirement would tell the operator to add a flag this mode also refuses. # requirement would tell the operator to add a flag this mode also refuses.
"--proposal-review": args.proposal_review, "--proposal-review": args.proposal_review,
# ONE payload is a cut of ONE base at ONE ref, and --bundle-dir (its only source of a
# base here) is already single-project-only. BY NAME rather than falling through to
# the --bundle-dir requirement below, whose message ALSO names --prepass-payload: an
# operator who wrote --portfolio --prepass-payload has to hear which of the two is
# wrong, and an arm asserting on the shared token could not tell the two apart.
"--prepass-payload": args.prepass_payload,
# And the asynchronous half of the same door, on the same side of the partition and by # And the asynchronous half of the same door, on the same side of the partition and by
# NAME for the same reason. # NAME for the same reason.
"--checkpoint-dir": args.checkpoint_dir, "--checkpoint-dir": args.checkpoint_dir,
@ -2767,6 +2794,54 @@ def main(argv: list[str] | None = None) -> int:
) )
return 1 return 1
# The pre-pass door's four remaining refusals, at FUNCTION level and never nested under
# another flag's branch: under one, a bare combination would fall straight through to a
# dispatch that drops the payload in silence (the F4 class).
if not args.portfolio and args.prepass_payload is not None:
if args.bundle_dir is None:
print(
"run refused: --prepass-payload requires --bundle-dir (a payload is a cut OF a "
"knowledge base, and every excerpt in it is verified against the mounted "
"document before the run starts; the road path has no base to verify against)",
file=sys.stderr,
)
return 1
if args.proposals_from_mandate:
# That mode settles a commission against the derived schedule and RETURNS above the
# debate entirely, so a payload there would be accepted and silently inert.
print(
"run refused: --prepass-payload is not used by --proposals-from-mandate (that "
"mode builds candidates deterministically from the commission and the derived "
"schedule, and returns before any debate — so the cut would be declared and then "
"never read)",
file=sys.stderr,
)
return 1
if args.dimension_config is not None:
# The pre-pass has no dimension concept, so its CUT is unscoped. Discarding delivered
# excerpts here would make the payload's own denominators wrong for the run that
# published them, which is the denominator failure this whole seam exists to remove.
print(
"run refused: --prepass-payload and --dimension-config cannot be combined (the "
"pre-pass cut the base without knowing about dimensions, so honouring the scope "
"would drop excerpts its declaration counts as delivered — the denominators would "
"then be wrong for the run that published them; cut for the dimension instead, or "
"run unscoped)",
file=sys.stderr,
)
return 1
if args.explore:
# The exploration reads the WHOLE base with all four navigator tools and then shapes
# the mandate the debate is told it must not go outside. That is precisely the ground
# on which the debate's own tools are withdrawn, one caller over.
print(
"run refused: --prepass-payload and --explore cannot be combined (the exploration "
"navigates the whole knowledge base with the same four tools the payload "
"withdraws, so the run as a whole would read far outside the cut it declares)",
file=sys.stderr,
)
return 1
# S7b: the deterministic candidate source needs BOTH halves of its input, and each missing half # S7b: the deterministic candidate source needs BOTH halves of its input, and each missing half
# is refused by its own name. Neither is inferable — a commission is what a person wrote, and # is refused by its own name. Neither is inferable — a commission is what a person wrote, and
# the derived schedule is the only thing that can supply a quantity and a price — so a run that # the derived schedule is the only thing that can supply a quantity and a price — so a run that
@ -3127,6 +3202,18 @@ def main(argv: list[str] | None = None) -> int:
print(f"run refused: {exc}", file=sys.stderr) print(f"run refused: {exc}", file=sys.stderr)
return 1 return 1
# The declared cut, loaded fail-fast alongside the commission and for the same reason: a
# payload that cannot be read is not a run to start with a navigating debate instead. Missing,
# not JSON, or not the shape the models require — all three land on the refusal surface with
# rc 1 and no traceback, which is what ``PrepassRefused`` subclasses ``ValueError`` for.
prepass_payload: prepass.PrepassPayload | None = None
if args.prepass_payload is not None:
try:
prepass_payload = load_prepass_payload(args.prepass_payload)
except (FileNotFoundError, ValidationError, ValueError) as exc:
print(f"run refused: {exc}", file=sys.stderr)
return 1
# The egress config, loaded fail-fast alongside the commission. Degrading a broken one to "no # The egress config, loaded fail-fast alongside the commission. Degrading a broken one to "no
# external services" would make the announcement describe a run nobody configured, and a # external services" would make the announcement describe a run nobody configured, and a
# partially-parsed one could contact a subset nobody chose. # partially-parsed one could contact a subset nobody chose.
@ -3499,6 +3586,7 @@ def main(argv: list[str] | None = None) -> int:
), ),
outbox_dir=args.outbox_dir, outbox_dir=args.outbox_dir,
run_id=args.run_id, run_id=args.run_id,
prepass_payload=prepass_payload,
verdict_input=_verdict_input_from_args(args), verdict_input=_verdict_input_from_args(args),
derive_cost_baseline=args.derive_cost_baseline, derive_cost_baseline=args.derive_cost_baseline,
mcp_servers=mcp_servers, mcp_servers=mcp_servers,
@ -3567,6 +3655,7 @@ def main(argv: list[str] | None = None) -> int:
), ),
outbox_dir=args.outbox_dir, outbox_dir=args.outbox_dir,
run_id=args.run_id, run_id=args.run_id,
prepass_payload=prepass_payload,
verdict_input=_verdict_input_from_args(args), verdict_input=_verdict_input_from_args(args),
semantic_retrieval=args.semantic_retrieval, semantic_retrieval=args.semantic_retrieval,
derive_cost_baseline=args.derive_cost_baseline, derive_cost_baseline=args.derive_cost_baseline,

View file

@ -0,0 +1,414 @@
"""Load-bearing gate for the ``--prepass-payload`` operator door (order 20260907T080223Z).
Two halves, and each has its own failure mode.
- **The wiring.** A flag that is parsed, validated and never passed to ``run_project`` is the F4
silent-drop class: the operator sees rc 0 and a navigating run. So one arm asserts the value
ARRIVES, by spying on the dispatch rather than by reading an exit code.
- **The refusals.** Six surfaces would otherwise accept the flag and do nothing with it. Each is
refused BY NAME and each arm is paired with an **rc-0 control on an argv that would otherwise
be ACCEPTED** without which a red arm can come from the fixture rather than from the row.
"""
from __future__ import annotations
import json
import shutil
from pathlib import Path
from typing import Any
import pytest
import portfolio_optimiser.run as run_module
from portfolio_optimiser.run import main
FIXTURE = Path(__file__).parent / "fixtures" / "prepass" / "bygg-energi-mikro-fixture.payload.json"
SHIPPED_BASE = Path(__file__).parent.parent / "shared" / "examples" / "bygg-energi-mikro"
PROJECT_ID = "BYGG-KONTOR-NORD"
_PROPOSAL = json.dumps(
{
"project_id": PROJECT_ID,
"measure": "energy_efficiency",
"claimed_saving_nok": 30000,
"affected_items": [{"code": "ENERGI-TOTAL-EL", "quantity": 120000.0, "unit_cost": 1.25}],
"assumptions": {},
}
)
def _base(tmp_path: Path) -> str:
root = tmp_path / "mounted-under-another-name"
shutil.copytree(SHIPPED_BASE, root)
index = root / "index.md"
lines = index.read_text(encoding="utf-8").split("\n")
lines.insert(1, "bundle_id: bygg-energi-mikro-fixture")
index.write_text("\n".join(lines), encoding="utf-8")
return str(root)
def _payload_file(tmp_path: Path) -> str:
path = tmp_path / "payload.json"
path.write_text(FIXTURE.read_text(encoding="utf-8"), encoding="utf-8")
return str(path)
def _docs(tmp_path: Path) -> str:
d = tmp_path / "docs"
d.mkdir(exist_ok=True)
(d / "cost.txt").write_text("Energitiltak i kontorbygg.", encoding="utf-8")
return str(d)
def _replies(tmp_path: Path) -> str:
path = tmp_path / "replies.json"
path.write_text(
json.dumps({"proposer": _PROPOSAL, "checker": "VERDICT: APPROVE"}), encoding="utf-8"
)
return str(path)
def _run_argv(tmp_path: Path, *extra: str) -> list[str]:
"""An argv the CLI ACCEPTS — the control every refusal arm below is measured against."""
return [
PROJECT_ID,
"--bundle-dir",
_base(tmp_path),
"--docs-dir",
_docs(tmp_path),
"--scripted-replies",
_replies(tmp_path),
*extra,
]
def _refuse_model(monkeypatch: pytest.MonkeyPatch) -> None:
"""Any model client construction becomes a failure, so a refusal that fired AFTER the spend is
distinguishable from one that fired before it. At the exit code the two look identical."""
def refuse(profile: Any) -> Any:
raise AssertionError("a model client was built despite a refusal") # pragma: no cover
monkeypatch.setattr(run_module, "_default_factory", refuse)
# --- the wiring ------------------------------------------------------------------------------
def test_the_flag_reaches_run_project(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
"""Parsed-and-dropped and parsed-and-passed are the same exit code. This arm reads the
argument, not the outcome."""
seen: list[Any] = []
original = run_module.run_project
async def spy(*args: Any, **kwargs: Any) -> Any:
seen.append(kwargs.get("prepass_payload"))
return await original(*args, **kwargs)
monkeypatch.setattr(run_module, "run_project", spy)
rc = main(_run_argv(tmp_path, "--prepass-payload", _payload_file(tmp_path)))
assert rc == 0
assert seen and seen[0] is not None
assert seen[0].bundle.bundle_id == "bygg-energi-mikro-fixture"
def test_the_notice_reaches_stdout(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
rc = main(_run_argv(tmp_path, "--prepass-payload", _payload_file(tmp_path)))
assert rc == 0
assert "DECLARED CUT" in capsys.readouterr().out
def test_the_dry_run_dispatch_is_threaded_too(
tmp_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
"""``DryRunReport.prepass`` and the dry-run notice call are dead code without this."""
# ``--scripted-replies`` and ``--live-dry-run`` are a documented contradiction, so the dry-run
# argv is built without it.
rc = main(
[
PROJECT_ID,
"--bundle-dir",
_base(tmp_path),
"--docs-dir",
_docs(tmp_path),
"--live-dry-run",
"--prepass-payload",
_payload_file(tmp_path),
]
)
assert rc == 0
assert "DECLARED CUT" in capsys.readouterr().out
def test_a_missing_payload_file_refuses_without_a_traceback(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
_refuse_model(monkeypatch)
rc = main(_run_argv(tmp_path, "--prepass-payload", str(tmp_path / "nope.json")))
assert rc == 1
assert "run refused:" in capsys.readouterr().err
def test_a_malformed_payload_file_refuses_without_a_traceback(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
_refuse_model(monkeypatch)
bad = tmp_path / "bad.json"
bad.write_text("{not json", encoding="utf-8")
rc = main(_run_argv(tmp_path, "--prepass-payload", str(bad)))
assert rc == 1
assert "run refused:" in capsys.readouterr().err
def test_a_payload_for_another_base_refuses_before_any_model_call(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
"""``PrepassRefused`` is a ``ValueError``, so it lands on the refusal surface and not the
crash channel and the refusal happens before a client is built."""
_refuse_model(monkeypatch)
raw = json.loads(FIXTURE.read_text(encoding="utf-8"))
raw["bundle"]["bundle_id"] = "a-different-corpus"
path = tmp_path / "other.json"
path.write_text(json.dumps(raw), encoding="utf-8")
rc = main(_run_argv(tmp_path, "--prepass-payload", str(path)))
assert rc == 1
assert "run refused:" in capsys.readouterr().err
# --- the six refusals, each with an rc-0 control ---------------------------------------------
def test_the_flag_requires_a_bundle_dir(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
_refuse_model(monkeypatch)
rc = main(
[
PROJECT_ID,
"--docs-dir",
_docs(tmp_path),
"--scripted-replies",
_replies(tmp_path),
"--prepass-payload",
_payload_file(tmp_path),
]
)
assert rc == 1
assert "--bundle-dir" in capsys.readouterr().err
def test_it_is_refused_in_portfolio_mode_by_name(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
"""NAMING ``--portfolio``, never the shared ``--prepass-payload`` token: a dropped row falls
through to the ``--bundle-dir`` requirement, whose message names the flag too so an arm
asserting on the shared token would be green against the mutation it exists for."""
_refuse_model(monkeypatch)
rc = main(
[
"--portfolio",
"--scripted-replies",
_replies(tmp_path),
"--prepass-payload",
_payload_file(tmp_path),
]
)
assert rc == 1
assert "--portfolio" in capsys.readouterr().err
def test_portfolio_mode_without_the_flag_is_accepted(
tmp_path: Path, capsys: pytest.CaptureFixture[str]
) -> None:
"""The rc-0 control for the arm above."""
rc = main(["--portfolio", "--scripted-replies", _replies(tmp_path)])
assert rc == 0
def _ledger(tmp_path: Path) -> str:
"""A JSON **ARRAY**. An object is refused by the ledger loader itself, which would make every
report arm below red for the wrong reason (measured in økt 89)."""
path = tmp_path / "ledger.json"
path.write_text(json.dumps([]), encoding="utf-8")
return str(path)
def test_it_is_refused_in_report_mode(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
"""Report mode returns ABOVE every dispatch, so an omission here is a silent DROP."""
_refuse_model(monkeypatch)
rc = main(
["--report", "--ledger", _ledger(tmp_path), "--prepass-payload", _payload_file(tmp_path)]
)
assert rc == 1
assert "--report" in capsys.readouterr().err
def test_report_mode_without_the_flag_is_accepted(tmp_path: Path) -> None:
"""The rc-0 control for the arm above."""
assert main(["--report", "--ledger", _ledger(tmp_path)]) == 0
def test_it_is_refused_with_proposals_from_mandate(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
"""That mode returns above the debate, so the flag would be silently inert."""
_refuse_model(monkeypatch)
mandate = tmp_path / "m.json"
mandate.write_text(
json.dumps(
{
"objective": "x",
"approaches": [
{
"id": "a1",
"label": "energy_efficiency",
"rationale": "r",
"affected_codes": ["ENERGI-TOTAL-EL"],
"claimed_saving_nok": 1000,
}
],
"allow_own_proposals": False,
}
),
encoding="utf-8",
)
rc = main(
_run_argv(
tmp_path,
"--proposals-from-mandate",
"--mandate",
str(mandate),
"--derive-cost-baseline",
"--prepass-payload",
_payload_file(tmp_path),
)
)
assert rc == 1
assert "--proposals-from-mandate" in capsys.readouterr().err
def test_it_is_refused_with_a_dimension_config(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
"""The pre-pass has no dimension concept, so its CUT is unscoped. Composing them would mean
po discarding excerpts the declaration counted as delivered which makes the payload's own
denominators wrong for the run that published them."""
_refuse_model(monkeypatch)
dim = tmp_path / "dim.json"
dim.write_text(
json.dumps(
{
"id": "energi",
"label": "Energi",
"allowed_measure_types": ["energy_efficiency"],
}
),
encoding="utf-8",
)
rc = main(
_run_argv(
tmp_path, "--dimension-config", str(dim), "--prepass-payload", _payload_file(tmp_path)
)
)
assert rc == 1
assert "--dimension-config" in capsys.readouterr().err
def test_a_dimension_config_without_the_flag_is_accepted(tmp_path: Path) -> None:
"""The rc-0 control: the two flags are each fine alone."""
dim = tmp_path / "dim.json"
dim.write_text(
json.dumps(
{
"id": "energi",
"label": "Energi",
"allowed_measure_types": ["energy_efficiency"],
}
),
encoding="utf-8",
)
assert main(_run_argv(tmp_path, "--dimension-config", str(dim))) == 0
def test_it_is_refused_with_explore(
tmp_path: Path, capsys: pytest.CaptureFixture[str], monkeypatch: pytest.MonkeyPatch
) -> None:
"""The exploration reads the WHOLE base with all four navigator tools and then hands its
mandate to a debate told it is under a declared cut this plan's own grounds for withdrawing
the tools, one caller over."""
_refuse_model(monkeypatch)
rc = main(
_run_argv(
tmp_path, "--explore", "finn tiltak", "--prepass-payload", _payload_file(tmp_path)
)
)
assert rc == 1
assert "--explore" in capsys.readouterr().err
def test_a_plain_run_without_the_flag_is_accepted(tmp_path: Path) -> None:
"""The rc-0 control shared by the arms that add exactly one flag to this argv."""
assert main(_run_argv(tmp_path)) == 0
# --- the hosted surface, deliberately untouched ------------------------------------------------
def test_the_hosted_surface_refuses_the_field_without_being_edited() -> None:
"""The brief's Non-Goal (MAJOR-4 / S7b precedent): the field enters NONE of the three sets, so
the generic ``unknown field(s)`` 400 already answers it and Fase 4e's two halves stand."""
from portfolio_optimiser import hosting
assert "prepass_payload" not in hosting._ALLOWED_FIELDS
with pytest.raises(ValueError, match="unknown field"):
hosting._run_kwargs({"project_id": PROJECT_ID, "prepass_payload": "/x.json"})
def test_the_phase_4e_partitions_still_hold() -> None:
"""The negative half: every forwarded field is a real ``run_project`` parameter and every
consumed one is not. Adding a parameter without touching hosting must not break it."""
import inspect
from portfolio_optimiser import hosting
from portfolio_optimiser.run import run_project
parameters = set(inspect.signature(run_project).parameters)
assert set(hosting._REQUIRED_FIELDS) <= parameters
assert set(hosting._OPTIONAL_FIELDS) <= parameters
assert set(hosting._CONSUMED_FIELDS).isdisjoint(parameters)
# --- the README block --------------------------------------------------------------------------
def _readme_block(flag: str) -> str:
"""The prose block for ONE flag, extracted rather than substring-matched: ``--portfolio`` and
``--report`` occur all over the README, so a file-wide search cannot tell a documented refusal
from an unrelated mention."""
readme = (Path(__file__).parent.parent / "README.md").read_text(encoding="utf-8")
start = readme.index(f"(`{flag}`)")
end = readme.find("\n **", start)
return readme[start : end if end != -1 else len(readme)]
def test_the_readme_documents_the_flag_and_every_partner_refusal() -> None:
block = _readme_block("--prepass-payload")
for partner in (
"--bundle-dir",
"--portfolio",
"--report",
"--proposals-from-mandate",
"--dimension-config",
"--explore",
):
assert partner in block, partner
# Customer-facing terminology: never "OKF bundle" on a published surface.
assert "OKF bundle" not in block
def test_the_extractor_finds_a_block_that_has_existed_since_f4() -> None:
"""The known-positive control: an extractor that silently finds nothing would make the arm
above green against a README with no block at all."""
assert "--explore" in _readme_block("--plan-review")