feat(portfolio): K3 — portfolio learning loop (shared verdict store, parity row 5) [skip-docs]
A single VerdictStore threaded through run_portfolio: a verdict available when project k composes survives into project k+1's fold (method-spec §5 cross-project threading). The optional verdict_dir is the portfolio-level expert inbox, read before each fold (role split §3 Step 7 — the portfolio never writes a run's own verdict back; §1/§6 — no self-contamination, only expert/seed verdicts cross). compose_run_context gains an optional passed-in store (None = fresh; every existing caller composes exactly as before). Load-bearing (tests/test_portfolio_learning_loadbearing.py), 2 detach proofs + control + §4.2 idempotency: - cross-project threading: project 1's bundle seed survives into project 2's prompt via the shared store; detach (compose ignores the passed-in store, always fresh) -> red. - portfolio inbox fold: a verdict_dir marker reaches the project's fold; detach (drop the run_portfolio merge) -> red; control (no verdict_dir) -> marker absent. - double-merge idempotency: a verdict merged before every project folds exactly once (first-write-wins on id). 437->442 green, golden byte-exact, full gate clean (ruff + format + mypy strict). run_s10.py and runs/ byte-untouched. README synced (test count, portfolio block, load-bearing list). K2 re-entrancy test stays green — the shared store threads verdict fold lines only, never bundle context markers. [skip-docs]: no invariant changed (CLAUDE.md untouched); the run_portfolio and compose_run_context docstrings + README carry the doc need. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiTwaKLesgcwXx2mDviqpt
This commit is contained in:
parent
f2c64da9ee
commit
9bae4fb563
4 changed files with 275 additions and 17 deletions
21
README.md
21
README.md
|
|
@ -13,7 +13,7 @@ human-in-the-loop, and the system learns from the verdicts.
|
|||
> **Status:** the D7 build (S5–S10) is complete, and the deterministic **ingest layer**
|
||||
> (CSV and SQL source types) has since been added in front of the loop. The deterministic
|
||||
> backbone, the agentic loop, the learning loop, and the ingest connectors are wired seam by
|
||||
> seam, each proven by load-bearing tests (437 tests, all running offline without an API
|
||||
> seam, each proven by load-bearing tests (442 tests, all running offline without an API
|
||||
> key). The programme's single budgeted **live model run has been executed and validated** —
|
||||
> its artifacts are committed under [`runs/s10/`](runs/s10/) (see below).
|
||||
|
||||
|
|
@ -93,11 +93,14 @@ description, never from its code)
|
|||
budget stop included. The model client is injected, so the offline suite proves the
|
||||
same orchestration with a scripted client; only the CLI's default constructs the SDK
|
||||
client.
|
||||
- `portfolio.py` — the sequential multi-project run: `run_portfolio` drives N projects from
|
||||
a schema-validated reference config, composing each project's context afresh (re-entrant,
|
||||
fresh debate state per run) and collecting one result per project in config order. The
|
||||
§8 budget meter is the one explicitly shared, portfolio-wide cap; the default failure
|
||||
policy raises (a stack-local choice until D-D flips it to collect-and-continue).
|
||||
- `portfolio.py` — the sequential multi-project run and learning loop: `run_portfolio` drives
|
||||
N projects from a schema-validated reference config, composing each project's context afresh
|
||||
(re-entrant, fresh debate state per run) and collecting one result per project in config
|
||||
order. Two things are deliberately shared portfolio-wide: the §8 budget meter (the cap) and a
|
||||
single learning `VerdictStore` — a verdict available when project k composes survives into
|
||||
project k+1's fold (cross-project threading, §5), and an optional `verdict_dir` is the
|
||||
portfolio-level expert inbox the system reads before each fold. The default failure policy
|
||||
raises (a stack-local choice until D-D flips it to collect-and-continue).
|
||||
- `run_s10.py` — the programme's ONE live run (cost discipline D6); run-path only.
|
||||
|
||||
### Load-bearing tests (§11)
|
||||
|
|
@ -110,7 +113,9 @@ proposal), `test_step5_refine_loadbearing.py` (the rejection reason verifiably r
|
|||
retry prompt, and the loop still stops at the cap), `test_step7_async_loop_loadbearing.py`
|
||||
(a verdict dropped after run A reaches run B's prompt through the file loop, with an
|
||||
empty-inbox control), `test_step8_promotion_loadbearing.py` (the gate refuses non-approved
|
||||
verdicts; the promoted signal stays out of the read-context), and
|
||||
verdicts; the promoted signal stays out of the read-context),
|
||||
`test_portfolio_learning_loadbearing.py` (a verdict available at project k survives into
|
||||
project k+1's fold via the shared store, with a marker-absent control), and
|
||||
`test_sdk_isolation.py` (local config cannot capture the checker).
|
||||
|
||||
## The ingest layer — CSV and SQL, in front of the loop
|
||||
|
|
@ -173,7 +178,7 @@ Python ≥3.10 · [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk
|
|||
|
||||
```bash
|
||||
uv sync # install dependencies
|
||||
uv run pytest # 437 tests — run without any API key and without network
|
||||
uv run pytest # 442 tests — run without any API key and without network
|
||||
uv run ruff check . && uv run ruff format --check .
|
||||
uv run mypy src # strict
|
||||
```
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue