main() gains a --portfolio mode flag dispatching to the EXISTING run_portfolio (not modified): loads --goals/--ledger/--dimension-config via the fail-fast loaders, passes project_ids=(pid,) or None (all reference projects), and prints a deterministic goal-stop line 'goal reached: scope=... project=... observed_ore=... limit_ore=... stopped_early=...' from the returned GoalReached/PortfolioResult. Positional project_id relaxed to nargs='?' and --docs-dir to optional, with a compensating single-project-mode guard (no pid/no --docs-dir -> rc 1 refusal) so the legacy contract still fails loudly. Dispatch + guard sit BEFORE the live_dry_run branch (Pass-2 #3 — appended after, they'd be dead code). Loader failures use the same structured refusal ('portfolio run refused: ...'). Load-bearing pair (RED-first): portfolio-hard goal already met breaks offline before any client (scope=portfolio, stopped_early=True); per-project-hard control skips the only pid (scope=project, stopped_early=False) — the two arms force the printed fields to derive from run_portfolio's real values. Marker 13731 øre. 9 passed, ruff + mypy clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KNNiJRk1sSwxgVLS5AobT1
87 lines
3.8 KiB
Python
87 lines
3.8 KiB
Python
"""Load-bearing CLI test for the observable GoalReached stop (S5.3 Step 3, SC1).
|
|
|
|
DETACH THAT TURNS THIS RED: route ``--goals``/``--ledger`` away from ``run_portfolio`` (drop the
|
|
wiring in ``main()``'s portfolio dispatch) → no goal-stop line reaches stdout → the positive arm's
|
|
marker assertion fails. The positive+control pair proves the printed scope/øre fields flow from
|
|
``run_portfolio``'s actual ``GoalReached``/``PortfolioResult`` values — a canned print could not
|
|
produce BOTH the portfolio-scope and the project-scope variant from the same wiring.
|
|
|
|
Both arms are provably OFFLINE: the goal is already met before any project builds a chat client
|
|
(portfolio-hard ``break``s at ``run.py:560-567``; per-project-hard ``continue``s past the only pid at
|
|
``run.py:571-579`` — no client is ever constructed). Marker value 13731 øre appears nowhere else in
|
|
the codebase (repo marker convention). No socket/network is exercised (brief NFR).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from portfolio_optimiser import run
|
|
from portfolio_optimiser.ledger import LedgerEntry, SavingsLedger
|
|
|
|
_MARKER_ORE = 13731 # marker: appears nowhere else in the codebase
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _isolate_model_env(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Hermetic env (verbatim from ``test_live_dry_run.py``) — irrelevant here (no client is built),
|
|
kept for parity so an accidental client construction can never read the operator's Foundry env."""
|
|
monkeypatch.delenv("PORTFOLIO_MODEL_MAP", raising=False)
|
|
monkeypatch.delenv("PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT", raising=False)
|
|
|
|
|
|
def _marker_ledger(tmp_path: Path) -> Path:
|
|
"""A ledger with exactly one realized entry totalling the marker øre against FV42-GSV-E1 —
|
|
reused by both arms (portfolio_total == per_project_total('FV42-GSV-E1') == 13731)."""
|
|
led = SavingsLedger()
|
|
led.add_realized(
|
|
LedgerEntry(
|
|
project_id="FV42-GSV-E1",
|
|
dimension="energi",
|
|
candidate_identity="c-marker",
|
|
amount_ore=_MARKER_ORE,
|
|
verdict_id="v-marker",
|
|
provenance="prior-hitl",
|
|
)
|
|
)
|
|
p = tmp_path / "ledger.json"
|
|
led.save(str(p))
|
|
return p
|
|
|
|
|
|
def _write_goals(tmp_path: Path, data: dict) -> Path:
|
|
p = tmp_path / "goals.json"
|
|
p.write_text(json.dumps(data), encoding="utf-8")
|
|
return p
|
|
|
|
|
|
def test_portfolio_hard_goal_reached_is_observable_in_cli(tmp_path, capsys) -> None:
|
|
"""POSITIVE: a portfolio-hard goal already met by the ledger stops the whole pass offline and
|
|
prints an observable goal-stop line carrying scope=portfolio + the marker øre + stopped_early."""
|
|
goals = _write_goals(tmp_path, {"portfolio": {"absolute_ore": _MARKER_ORE, "mode": "hard"}})
|
|
ledger = _marker_ledger(tmp_path)
|
|
rc = run.main(["--portfolio", "--goals", str(goals), "--ledger", str(ledger)])
|
|
assert rc == 0
|
|
out = capsys.readouterr().out
|
|
assert "goal reached: scope=portfolio" in out
|
|
assert f"observed_ore={_MARKER_ORE}" in out
|
|
assert "stopped_early=True" in out
|
|
|
|
|
|
def test_per_project_hard_goal_control_distinguishes_scope(tmp_path, capsys) -> None:
|
|
"""CONTROL (causality): a per-project-hard goal met on the ONLY selected pid skips it (continue,
|
|
no client built) → scope=project, stopped_early=False, and NO portfolio-scope line. Proves the
|
|
printed fields are the actual run_portfolio values, not a canned string."""
|
|
goals = _write_goals(
|
|
tmp_path, {"per_project": {"FV42-GSV-E1": {"absolute_ore": _MARKER_ORE, "mode": "hard"}}}
|
|
)
|
|
ledger = _marker_ledger(tmp_path)
|
|
rc = run.main(["FV42-GSV-E1", "--portfolio", "--goals", str(goals), "--ledger", str(ledger)])
|
|
assert rc == 0
|
|
out = capsys.readouterr().out
|
|
assert "scope=project" in out
|
|
assert "stopped_early=False" in out
|
|
assert "scope=portfolio" not in out
|