"""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