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
27
README.md
27
README.md
|
|
@ -152,6 +152,24 @@ description, never from its code)
|
|||
outcomes — a budget stop notifies too) and `hitl.py` (read-only preserved) share the same
|
||||
opt-in-gated CLI seam, refusing a webhook-without-opt-in *before* any spend. The payload
|
||||
shape is stack-local (no shared notification spec across the siblings).
|
||||
- `valuereport.py` — what the loop actually delivered (**offline, read-only**): a
|
||||
deterministic projection of the outbox, the inbox and the ledger into three distinct
|
||||
columns — *modelled* (what the system claimed), *expert-corrected* (what the §4.2 verdict
|
||||
makes of that claim), *realized* (what passed the expert gate into the book) — plus goal
|
||||
progress, a quantified learning effect and cost against value. No model call, no clock, no
|
||||
new state. The honesty rule (§1) sets its shape: a figure the layers do not carry is
|
||||
reported `UNMARKED`, never back-filled from the stage before it. An `approved_with_adjustment`
|
||||
verdict changes the amount but carries none in the §4.2 shape, so its corrected value is
|
||||
unquantified — and a project nobody has judged has no realized figure at all, rather than a
|
||||
zero that reads as a judgment. Learning is measured, not asserted: settled proposals split
|
||||
by `run_id` order into an earlier and a later cohort, and a rising approval share is only
|
||||
reported alongside the modelled→corrected gap that shrank behind it. Cost (USD, itself a K6
|
||||
upper bound) and value (NOK) are printed side by side and never divided — this repo carries
|
||||
no sourced exchange rate, and a ratio would invent one. Available standalone
|
||||
(`uv run python -m portfolio_optimiser_claude.valuereport --outbox <dir> --inbox <dir>
|
||||
[--ledger <file>]`) and as an opt-in side product of a run (`run.py --value-report <file>`,
|
||||
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 own exit code).
|
||||
|
||||
### Load-bearing tests (§11)
|
||||
|
||||
|
|
@ -180,8 +198,13 @@ and hitl never writes any layer, proven by a before/after byte snapshot),
|
|||
per-run opt-in flag refuses fail-fast and its transport never fires — red the moment the gate
|
||||
is detached — the canned transport receives the structured payload, an AST grep-guard proves
|
||||
no network path lives outside the one injectable seam function, and the run/hitl entrances
|
||||
emit on their outcomes while hitl stays read-only), and
|
||||
`test_sdk_isolation.py` (local config cannot capture the checker).
|
||||
emit on their outcomes while hitl stays read-only),
|
||||
`test_valuereport_loadbearing.py` and `test_valuereport_seam_loadbearing.py` (an unjudged
|
||||
project's realized value stays unmarked and never mirrors the modelled claim — red the moment
|
||||
that boundary is detached — a rising approval share is not reported as learning without the
|
||||
gap arithmetic behind it, the projection writes no byte into the three layers it reads, and
|
||||
the run entrance produces the report on both outcomes while leaving the run's verdict alone),
|
||||
and `test_sdk_isolation.py` (local config cannot capture the checker).
|
||||
|
||||
## The ingest layer — CSV and SQL, in front of the loop
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue