feat(portfolio): K11 — per-run value report, pure projection over the three layers (parity row 25) [skip-docs]
The S5.4 analog: every run/portfolio pass can produce a deterministic value report — modelled → expert-corrected → realized, goal progress, a quantified learning effect, cost against value — with no model call, no clock and no new state. It is a PURE PROJECTION over what is already persisted (K5 outbox pairs, §4.2 inbox verdicts, K1 ledger), joined on the verdict_id K5 mints. The honesty rule (§1) sets the shape, not the layout: - approved -> the claim stands (quantified) - rejected -> the claim is void, 0 (quantified — an earned zero) - approved_with_adjustment -> §4.2 carries NO adjusted amount, so the corrected value is UNQUANTIFIED, never back-filled with the claim - no verdict -> realized is UNMARKED, never zero-that-reads-as-judged and never the modelled figure Partial quantification is counted in the output (2 of 4 …, 2 UNMARKED) rather than summed into a full-looking total. Learning is measured, not asserted: a rising approval share is reported only alongside the modelled→corrected gap that shrank behind it, over cohorts split by run_id order. Cost (USD, a K6 upper bound) and value (NOK) sit side by side and are never divided — no sourced FX rate exists here, and a ratio would invent one. Surfaces: standalone CLI (valuereport) and an opt-in --value-report on run.py, which requires --outbox and is refused BEFORE any spend without one; the report is written on both run outcomes and never rewrites the run's exit code (a budget stop stays a budget stop). Six seams detach-proven RED: honesty boundary (corrected + realized), gap arithmetic, projection purity, run-seam wiring, pre-spend fail-fast, both-outcome reporting. Fixtures are COMMITTED and generated with the real primitives, so the id-join under test is genuine. Note on the purity test: it was green-but-dead in its first form. Snapshotting the committed fixture tree in place let an earlier test's stray write pre-seed the 'before' snapshot, so the detached write reproduced it byte-for-byte. Every test now projects from a per-test copy, and a pinned file-set test guards the committed tree. Found by running the detach proof — which is what §11 is for. portfolio.py is deliberately NOT wired: run_portfolio persists nothing, so there is nothing for a projection to read. Its docstring now says that instead of promising the wiring it did not get. 562 -> 584 tests green; ruff + mypy --strict clean over 27 src files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MQu2xxwedckjU56byu1aUG
This commit is contained in:
parent
d0107e7f8c
commit
4dcdd8017a
18 changed files with 1477 additions and 6 deletions
13
tests/data/valuereport/inbox/82d6e4e60ae87e14.json
Normal file
13
tests/data/valuereport/inbox/82d6e4e60ae87e14.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"decision": "approved_with_adjustment",
|
||||
"id": "82d6e4e60ae87e14",
|
||||
"proposal_features": {
|
||||
"affected_codes": [
|
||||
"BY-11"
|
||||
],
|
||||
"claimed_saving_nok": 60000.0,
|
||||
"description": "Etterisolering in FV42-P3",
|
||||
"measure_type": "Etterisolering"
|
||||
},
|
||||
"rationale": "fixture verdict for run-004 (approved_with_adjustment)"
|
||||
}
|
||||
13
tests/data/valuereport/inbox/94a98883515d1267.json
Normal file
13
tests/data/valuereport/inbox/94a98883515d1267.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"decision": "approved",
|
||||
"id": "94a98883515d1267",
|
||||
"proposal_features": {
|
||||
"affected_codes": [
|
||||
"VT-07"
|
||||
],
|
||||
"claimed_saving_nok": 80000.0,
|
||||
"description": "VFD-pumpe in FV42-P1",
|
||||
"measure_type": "VFD-pumpe"
|
||||
},
|
||||
"rationale": "fixture verdict for run-002 (approved)"
|
||||
}
|
||||
13
tests/data/valuereport/inbox/fe354d69ef8a47b1.json
Normal file
13
tests/data/valuereport/inbox/fe354d69ef8a47b1.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"decision": "rejected",
|
||||
"id": "fe354d69ef8a47b1",
|
||||
"proposal_features": {
|
||||
"affected_codes": [
|
||||
"EL-01"
|
||||
],
|
||||
"claimed_saving_nok": 100000.0,
|
||||
"description": "LED-retrofit in FV42-P1",
|
||||
"measure_type": "LED-retrofit"
|
||||
},
|
||||
"rationale": "fixture verdict for run-001 (rejected)"
|
||||
}
|
||||
16
tests/data/valuereport/ledger.json
Normal file
16
tests/data/valuereport/ledger.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"entries": [
|
||||
{
|
||||
"affected_codes": [
|
||||
"VT-07"
|
||||
],
|
||||
"amount_nok": 80000.0,
|
||||
"dimension": null,
|
||||
"expert": "ke.fixture",
|
||||
"key": "3826f2b927f7cc7f",
|
||||
"measure_type": "VFD-pumpe",
|
||||
"project": "FV42-P1",
|
||||
"timestamp": "2026-07-24T00:00:00Z"
|
||||
}
|
||||
]
|
||||
}
|
||||
29
tests/data/valuereport/outbox/run-001-outcome.json
Normal file
29
tests/data/valuereport/outbox/run-001-outcome.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"attempts": 1,
|
||||
"checker_decision": "approve",
|
||||
"outcome": {
|
||||
"claimed_saving_nok": 100000.0,
|
||||
"nominal_feasible": 100000.0,
|
||||
"p10": 80000.0,
|
||||
"p50": 100000.0,
|
||||
"p90": 120000.0,
|
||||
"type": "validated",
|
||||
"validates": true
|
||||
},
|
||||
"provenance": {
|
||||
"citations": [
|
||||
{
|
||||
"file": "tiltak-vfd.md",
|
||||
"snippet": "VFD paa pumpe P-07",
|
||||
"span": "L1-L4"
|
||||
}
|
||||
],
|
||||
"model": "unknown",
|
||||
"role": "proposer",
|
||||
"tokens_used": 0,
|
||||
"validator_decision": "validated"
|
||||
},
|
||||
"run_id": "run-001",
|
||||
"validator_decision": "validated",
|
||||
"verdict_id": "fe354d69ef8a47b1"
|
||||
}
|
||||
13
tests/data/valuereport/outbox/run-001-proposal.json
Normal file
13
tests/data/valuereport/outbox/run-001-proposal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"affected_items": [
|
||||
{
|
||||
"code": "EL-01",
|
||||
"quantity": 1.0,
|
||||
"unit_cost": 100000.0
|
||||
}
|
||||
],
|
||||
"assumptions": {},
|
||||
"claimed_saving_nok": 100000.0,
|
||||
"measure": "LED-retrofit",
|
||||
"project_id": "FV42-P1"
|
||||
}
|
||||
29
tests/data/valuereport/outbox/run-002-outcome.json
Normal file
29
tests/data/valuereport/outbox/run-002-outcome.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"attempts": 1,
|
||||
"checker_decision": "approve",
|
||||
"outcome": {
|
||||
"claimed_saving_nok": 80000.0,
|
||||
"nominal_feasible": 80000.0,
|
||||
"p10": 64000.0,
|
||||
"p50": 80000.0,
|
||||
"p90": 96000.0,
|
||||
"type": "validated",
|
||||
"validates": true
|
||||
},
|
||||
"provenance": {
|
||||
"citations": [
|
||||
{
|
||||
"file": "tiltak-vfd.md",
|
||||
"snippet": "VFD paa pumpe P-07",
|
||||
"span": "L1-L4"
|
||||
}
|
||||
],
|
||||
"model": "unknown",
|
||||
"role": "proposer",
|
||||
"tokens_used": 0,
|
||||
"validator_decision": "validated"
|
||||
},
|
||||
"run_id": "run-002",
|
||||
"validator_decision": "validated",
|
||||
"verdict_id": "94a98883515d1267"
|
||||
}
|
||||
13
tests/data/valuereport/outbox/run-002-proposal.json
Normal file
13
tests/data/valuereport/outbox/run-002-proposal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"affected_items": [
|
||||
{
|
||||
"code": "VT-07",
|
||||
"quantity": 1.0,
|
||||
"unit_cost": 80000.0
|
||||
}
|
||||
],
|
||||
"assumptions": {},
|
||||
"claimed_saving_nok": 80000.0,
|
||||
"measure": "VFD-pumpe",
|
||||
"project_id": "FV42-P1"
|
||||
}
|
||||
29
tests/data/valuereport/outbox/run-003-outcome.json
Normal file
29
tests/data/valuereport/outbox/run-003-outcome.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"attempts": 1,
|
||||
"checker_decision": "approve",
|
||||
"outcome": {
|
||||
"claimed_saving_nok": 50000.0,
|
||||
"nominal_feasible": 50000.0,
|
||||
"p10": 40000.0,
|
||||
"p50": 50000.0,
|
||||
"p90": 60000.0,
|
||||
"type": "validated",
|
||||
"validates": true
|
||||
},
|
||||
"provenance": {
|
||||
"citations": [
|
||||
{
|
||||
"file": "tiltak-vfd.md",
|
||||
"snippet": "VFD paa pumpe P-07",
|
||||
"span": "L1-L4"
|
||||
}
|
||||
],
|
||||
"model": "unknown",
|
||||
"role": "proposer",
|
||||
"tokens_used": 0,
|
||||
"validator_decision": "validated"
|
||||
},
|
||||
"run_id": "run-003",
|
||||
"validator_decision": "validated",
|
||||
"verdict_id": "8ec27d38b3e5a8e2"
|
||||
}
|
||||
13
tests/data/valuereport/outbox/run-003-proposal.json
Normal file
13
tests/data/valuereport/outbox/run-003-proposal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"affected_items": [
|
||||
{
|
||||
"code": "AU-03",
|
||||
"quantity": 1.0,
|
||||
"unit_cost": 50000.0
|
||||
}
|
||||
],
|
||||
"assumptions": {},
|
||||
"claimed_saving_nok": 50000.0,
|
||||
"measure": "SD-anlegg",
|
||||
"project_id": "FV42-P2"
|
||||
}
|
||||
29
tests/data/valuereport/outbox/run-004-outcome.json
Normal file
29
tests/data/valuereport/outbox/run-004-outcome.json
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"attempts": 1,
|
||||
"checker_decision": "approve",
|
||||
"outcome": {
|
||||
"claimed_saving_nok": 60000.0,
|
||||
"nominal_feasible": 60000.0,
|
||||
"p10": 48000.0,
|
||||
"p50": 60000.0,
|
||||
"p90": 72000.0,
|
||||
"type": "validated",
|
||||
"validates": true
|
||||
},
|
||||
"provenance": {
|
||||
"citations": [
|
||||
{
|
||||
"file": "tiltak-vfd.md",
|
||||
"snippet": "VFD paa pumpe P-07",
|
||||
"span": "L1-L4"
|
||||
}
|
||||
],
|
||||
"model": "unknown",
|
||||
"role": "proposer",
|
||||
"tokens_used": 0,
|
||||
"validator_decision": "validated"
|
||||
},
|
||||
"run_id": "run-004",
|
||||
"validator_decision": "validated",
|
||||
"verdict_id": "82d6e4e60ae87e14"
|
||||
}
|
||||
13
tests/data/valuereport/outbox/run-004-proposal.json
Normal file
13
tests/data/valuereport/outbox/run-004-proposal.json
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"affected_items": [
|
||||
{
|
||||
"code": "BY-11",
|
||||
"quantity": 1.0,
|
||||
"unit_cost": 60000.0
|
||||
}
|
||||
],
|
||||
"assumptions": {},
|
||||
"claimed_saving_nok": 60000.0,
|
||||
"measure": "Etterisolering",
|
||||
"project_id": "FV42-P3"
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue