feat(fase1): hard/soft goal-stop in run_portfolio on accumulated ledger (F1)
This commit is contained in:
parent
c6f62d41db
commit
16b6d80b82
4 changed files with 270 additions and 6 deletions
|
|
@ -17,6 +17,7 @@ from __future__ import annotations
|
|||
|
||||
import pytest
|
||||
|
||||
from portfolio_optimiser.contracts import GoalConfig, GoalContract
|
||||
from portfolio_optimiser.ledger import (
|
||||
LedgerEntry,
|
||||
RealizationRefused,
|
||||
|
|
@ -25,6 +26,7 @@ from portfolio_optimiser.ledger import (
|
|||
realize,
|
||||
stamp,
|
||||
)
|
||||
from portfolio_optimiser.run import run_portfolio
|
||||
from portfolio_optimiser.verdicts import ProposalFeatures, Verdict
|
||||
|
||||
_TS = "2026-07-06T00:00:00Z"
|
||||
|
|
@ -180,3 +182,53 @@ def test_realize_ore_conversion_is_exact() -> None:
|
|||
timestamp=_TS,
|
||||
)
|
||||
assert entry.amount_ore == 1234567
|
||||
|
||||
|
||||
# --- Step 8: goal-stop is load-bearing (SC6) -----------------------------------------------------
|
||||
|
||||
_PORTFOLIO_IDS = ["FV42-GSV-E1", "RV13-RAS-TP", "BRU-LAKS-REHAB"]
|
||||
|
||||
|
||||
def _portfolio_ledger(amount_ore: int) -> SavingsLedger:
|
||||
led = SavingsLedger()
|
||||
led.add_realized(
|
||||
LedgerEntry(
|
||||
project_id="FV42-GSV-E1",
|
||||
dimension="energi",
|
||||
candidate_identity="prior-hitl",
|
||||
amount_ore=amount_ore,
|
||||
verdict_id="v-prior",
|
||||
provenance=stamp(approver="ekspert", experiment="earlier", timestamp=_TS),
|
||||
)
|
||||
)
|
||||
return led
|
||||
|
||||
|
||||
async def test_goal_stop_is_load_bearing(make_portfolio_client_factory) -> None:
|
||||
"""LOAD-BEARING (SC6): a reached HARD portfolio goal stops the pass (no project runs); a
|
||||
below-goal ledger runs the FULL pass. RED if the goal-stop check is detached in run_portfolio
|
||||
(a reached goal then runs past the target). The control (below goal -> full pass) proves the
|
||||
stop is CAUSED by the goal being reached, not by the fixture."""
|
||||
goals = GoalConfig(portfolio=GoalContract(absolute_ore=1000))
|
||||
|
||||
stopped = await run_portfolio(
|
||||
_PORTFOLIO_IDS,
|
||||
"local",
|
||||
ledger=_portfolio_ledger(1000), # == goal (>=)
|
||||
goals=goals,
|
||||
client_factory=make_portfolio_client_factory({}),
|
||||
max_rounds=1,
|
||||
)
|
||||
assert stopped.stopped_early is True
|
||||
assert stopped.runs == () # detach -> this becomes 3 (runs past the reached goal)
|
||||
|
||||
below = await run_portfolio(
|
||||
_PORTFOLIO_IDS,
|
||||
"local",
|
||||
ledger=_portfolio_ledger(999), # below the goal
|
||||
goals=goals,
|
||||
client_factory=make_portfolio_client_factory({}),
|
||||
max_rounds=1,
|
||||
)
|
||||
assert below.stopped_early is False # control: goal not reached -> full pass
|
||||
assert len(below.runs) == 3
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue