feat(portfolio): K8 — live-run drill, pre-call artifact capture (parity row 21) [skip-docs]
A future operator-gated live run (the M2-analog) is fully rigged and rehearsed
OFFLINE — without one model call, without a key (S4.2-analog, parity row 21;
buildable after K5 + K7). `--live-dry-run` builds everything a real run would
(contracts fail-fast §10 → compose §5 → SDK-client construction → preflight)
and captures the run-config + preflight artifacts, then STOPS before the first
model call. The stop IS the boundary: the loop is never entered, so nothing is
spent (strictly offline, no D6 gate).
- run.py --live-dry-run: requires --outbox + --run-id (the drill's artifacts are
run_id-named), rejected fail-fast before any build. Writes a run_id-named PAIR
to the outbox:
* {run_id}-runconfig.json — comparison-protocol §4 pt 3: model-id per role the
loop calls (proposer/checker, THROUGH resolve_model — the run's own path),
profile, and every cap/parameter. Deliberately NO wall-clock date, so the
bytes stay deterministic (the run's date is stamped at report time, §4 pt 3).
* {run_id}-preflight.json — the captured preflight verdict (clear + refusals).
The drill CAPTURES the preflight result rather than gating the build on it:
exit 0 when clear (rig go-live-ready), non-zero when refused — artifacts
captured and ZERO model calls in EITHER case.
- The client is constructed (the verified key-free SDK premise) but never called;
a call-counting stand-in proves 0 calls. Bytes reuse the deterministic house
JSON writer; run_s10.py/runs/ byte-untouched.
- test_dry_run_loadbearing.py: 7 tests. TWO seams detach-proven RED — the
0-calls stop seam (neutralise the branch → falls to execute_run → the counting
client fires → red) and the capture seam (drop the writes → outbox lacks the
pair → red). Env monkeypatched so the preflight verdict is deterministic
regardless of the operator's ambient shell.
- 514→521 green, golden byte-exact, full gate clean (ruff+format+mypy strict,
24 src files). README: test-count sync ×2 + run.py drill note + load-bearing
mention. IKKE-scope (held): the actual live run (M2-analog, operator) and any
change to preflight/outbox.
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
c08d92a358
commit
08ffddbbb1
3 changed files with 397 additions and 8 deletions
17
README.md
17
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 (514 tests, all running offline without an API
|
||||
> seam, each proven by load-bearing tests (521 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).
|
||||
|
||||
|
|
@ -100,7 +100,13 @@ description, never from its code)
|
|||
the loop under the budget meter, persisting artifacts on both outcomes — a structured
|
||||
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.
|
||||
client. `--live-dry-run` is the **live-run drill** (K8): it builds everything a real run
|
||||
would (contracts → compose → client construction → preflight) and captures a `run_id`-named
|
||||
`runconfig` + `preflight` pair to the outbox (model-id, parameters, caps — no wall-clock, so
|
||||
the bytes stay deterministic), then **stops before the first model call**. It exits 0 when
|
||||
the preflight is clear and non-zero when it refused, but captures the artifacts and makes
|
||||
zero model calls either way — a future operator-gated live run is rigged and rehearsed
|
||||
offline, with no spend.
|
||||
- `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
|
||||
|
|
@ -144,7 +150,10 @@ project k+1's fold via the shared store, with a marker-absent control),
|
|||
`test_outbox_loadbearing.py` (a completed run's `run_id`-named outbox pair is written on the
|
||||
entrance path, with a no-outbox control, and the outcome carries the inbox join key),
|
||||
`test_preflight.py` (a missing credential and a placeholder model id are each refused before
|
||||
any spend, and the preflight carries no network path of its own), and
|
||||
any spend, and the preflight carries no network path of its own),
|
||||
`test_dry_run_loadbearing.py` (the live-run drill captures its `runconfig` + `preflight`
|
||||
artifacts and stops before the first model call — a call-counting client proves zero calls,
|
||||
red the moment the stop seam is detached), and
|
||||
`test_sdk_isolation.py` (local config cannot capture the checker).
|
||||
|
||||
## The ingest layer — CSV and SQL, in front of the loop
|
||||
|
|
@ -207,7 +216,7 @@ Python ≥3.10 · [`claude-agent-sdk`](https://pypi.org/project/claude-agent-sdk
|
|||
|
||||
```bash
|
||||
uv sync # install dependencies
|
||||
uv run pytest # 514 tests — run without any API key and without network
|
||||
uv run pytest # 521 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