Campy π§ β campaign run bilans β
Supervises a whole modernisation programme by running modernize as a subbot in a bounded loop, judging progress in git, and executing the golden-master ledger's announced re-records between runs. Not one LLM node of its own. See bots/campaign/.
2026-09-17 β profile 2: a one-lot programme through the whole loop, parked on the handoff review whose instructions carry their paragraphs (run 01a0af0c-cb11) β
- Status: validated β preflight, one
modernizelot through the subbot, observe, steward, the exhausted loop gate, finalize, the handoff review acknowledged, done. Two earlier attempts refused at the child's oracle gate (below). - Versions: bot campaign 0.1.1 (
dsl: 2, wave 2 of #1344) Β· iterion853b5ce28for the bots; the engine the branch binaryv3.154.1+d5b7db09fcarrying the first wave-1 runtime fix (main was at v3.155.0) Β· childmodernizefrom the same catalogue (profile 1, not yet migrated), claude_code + claude-opus-5 through the host's Anthropic-compatible facade. - Method: CLI
iterion run <bundle>/main.bot --sandbox nonelaunched FROM a scratch copy of the greet project seeded with.modernize/plan.yaml(one lot: a docstring and type hints ongreeting(), exit gate = the unit tests and a docstring check) and a.golden-master/oracle whoseverify-oracle.shruns the CLI and prints a gate-mode JSON report;--store-dirthe operator's workspace store,--var governance=bot --var escalation=handoff --var max_lots=1 --var lot_max_passes=2, caps--max-cost-usd 8 --max-duration 30m,ITERION_BINthe branch binary; the handoff answered byiterion resume --answer ack=β¦. - Result: the child landed the lot in two passes (campaign $0.44 + $0.68, gate green, oracle green, references untouched,
mark_done); the campaign observedmoved: true, the steward recorded 0 acts and 0 escalations, the loop gate declared the programme exhausted (max_lots1), finalize wrote.modernize/campaign/handoff.md(1 done, 0 blocked, oracle green) and the run parked onhandoff_review. β $1.15 for this attempt; β $0.45 for the two refused ones. - Value: the supervisor's whole graph live on profile 2, the subbot seam included; the run's
events.jsonlcarries the rendered handoff instructions withblocked lots.\n\nAcknowledge to close the run., the paragraph break the profile-1 lexer used to fold. - Findings / misses: two refusals were mine, not the bot's β the scratch oracle first printed prose (
ORACLE_NOT_RUN: the gate wrapper exited 0 without printing a report), then a report whosemodewas notgate(a subset or a non-gate run, not a verdict on the tree): the gate reads the LAST JSON object on stdout and takes it as a verdict only in gate mode, exactly as the modernize contract says. Not exercised:escalate(the other prompt the profile touches; it needs a rebaseline request underescalation: interactive) andgovernance: human. - Engine hardening: none needed.
- Lessons for next run: a scratch oracle is three lines and one JSON object β
{"mode": "gate", "ok": true, "stable": true, β¦}β and the refusal messages say what is missing. Anditerion resumedoes not inherit the launch's--sandbox none: the acknowledgement re-provisioned a sandbox (the bot's devbox realised inside a container) before the finaldoneβ setITERION_SANDBOX_DEFAULT=noneon the resume of a host run.
2026-08-12 β third dogfood: the refusal and governance paths, exercised on purpose (runs 019ff5aa, 019ff5b3) β
- Status: the falsification matrix is closed except the engine-level loop bounds (max_lots exhaustion, the budget guard's declined back-edge). $0 LLM: two short runs on a seeded branch.
- Mismatch refusal, in flight. A request deliberately announcing a diff no record produces: the steward recorded, measured observed==[] against the announcement, committed NOTHING, committed the refusal, and the
escalation: interactivepause fired carrying the item. The second run proved the other half: a committed refusal is never retried. governance: human, on the operator's own edit. A plan lot retitled MID-campaign was flagged as a modified-lot contract extension,pending_approvalpaused the campaign regardless of mode, and the approval went to the committed record. A measured design fact came with it: extension detection covers the CAMPAIGN segment β the same edit made before launch is base, not extension. Base is consent.- The target-side config-selector fix, visible in the campaign's own measurement: engine-qualified gate commands went from vacuously green to honestly red (the absent config named), and the requalification figure moved from 30+ to 22 β the difference IS the vacuity, now counted honestly.
2026-08-12 β second dogfood: an UNFINISHED programme, four runs, every core path proven (runs 019ff4af β 019ff4f2) β
- Status: VALIDATED on the paths that matter β steward act Γ2 under the observed==announced criterion, contract-extension governance Γ2, the first lots landed THROUGH the loop, and at the last iteration the full machine cycle with no operator gesture: the worker measured red, wrote and committed the request block, declared blocked; the steward consumed it in the same iteration, recorded, matched the announcement exactly, committed act and verdict, replayed the full counter-test green; the lot requalified on the final tree.
- Target: the same finished programme, EXTENDED live with three deliberate one-key wording lots (
rebaseline_allowed: true, measured single- and double-reference perimeters). ~$8.50 LLM across three child runs.
What each run taught β
Run A β the producer gap. The child did the lot impeccably right up to the protocol: perimeter measured to the file, the future act PROVEN in advance by replaying the full counter-test on a re-recorded copy (emergent β nobody asked for that), blocked declared correctlyβ¦ and the announcement entirely in prose, because the ledger's existing entries taught it their shape louder than the skill's template. pending=[]; nothing could act. The skill now says THE BLOCK IS THE REQUEST in so many words, and emitted ledgers should open with a header naming the blocks.
Run B β the steward's act, mechanically. An operator transcription of the worker's own announcement, then: record, observed diff == announced set exactly, act + verdict blocks committed, full counter-test green behind, the lot requalified. Zero LLM.
Run C β the honest no-move. The operator's target key turned out to be rendered nowhere; the child measured that, landed the change, let the net stay green RIGHTLY, converged done with a committed explanation β and invented no request. Meanwhile the supervisor flagged the operator's own plan edit as a contract extension and listed it for the handoff: governance observed working on the party that configured it.
Run D β the gate that lied, and the loop that caught it. Two iterations returned oracle green on a tree whose serve HAD to diverge: the capture reused an application another era had booted β a live pid dates nothing. The child, facing a gate that contradicted its own measurement, claimed a request in its output without committing it; the supervisor believed only git (moved=false), stagnation armed, and the THIRD iteration β fresh app, honest red β closed the full cycle machine-only. Root cause fixed in the target's environment scripts (reuse iff build-inputs content fingerprint matches; content over clock, since git-restored timestamps make a fresh producer look older than its artifact) and written into the net's doctrine skill.
Still unexercised, named β
The diff-mismatch refusal (every observed set matched every announcement so far), governance: human and escalation: interactive (both dogfoods ran the defaults), and a campaign long enough to hit max_lots or the budget guard's declined back-edge.
2026-08-12 β first real run: a finished programme, walked end to end (run 019ff2f6) β
- Status: VALIDATED on the exhausted-programme path. preflight β subbot no-op β observe β steward β loop_gate β finalize β human review β done, every transition for the reason designed.
- Target: a completed 36-lot modernisation programme (5 done / 31 blocked in contract terms) with its behavioural net green and its ledger fully acted.
- Cost: $0.00 LLM β the child's plan reader emitted a clean no-op and the supervisor is deterministic; ~10 min wall clock, almost all of it the final gate and the requalification commands.
The verdict β
The child landed nothing (moved=no, journal row 1), observe declared the programme exhausted β no eligible lot remains β and loop_gate left through the handoff tail on exactly that reason, not on stagnation. finalize replayed the full counter-test on the committed tree: green, with figures identical to the programme's own final verdict (7/7 mutants detected, no-op silent, revert clean, collateral 0, stable, corpus 24/23 distinct). The banked run branch carries two supervisor commits: the journal row and the handoff.
Requalification: the bot beat the manual pass, on the manual pass's own lesson β
31 blocked lots, 16 distinct gate commands, each played once on the final tree, verdicts projected: 30/31 requalify as written; one stays blocked on a script the programme itself retired (exit 127, named in the handoff).
A manual requalification of the same tree had scored only 16/31 as written, because it ran the gate commands from a host shell where the declared toolchain was not on PATH. The engine realises the target's devbox environment before any node runs, so the same commands β build-tool wrapper included β exited 0 here. Measure in the declared environment was a lesson this programme paid for once; the supervisor embodies it structurally.
One honesty note the handoff carries: two commands qualified for a second database engine exit 0 vacuously β this tree's gate script ignores the engine selector variables entirely, so they replay the default engine. Green as written, not evidence about the second engine. A gate command that ignores its own qualifier is a contract smell worth a lot of its own.
Two v0 defects, found by the first two launches, fixed the same day β
- The engine's materialised node script is not work in flight (
15ab7b0). Everyscript:tool sees its own.iterion-script-*.pyin the workspace, so the clean-tree refusal fired on a freshly created worktree β and the steward would have counted the file in every observed diff. All three status reads now carry that one exclusion. - bool/json inputs render as JSON literals (
1545155).{{input.moved}}reached python as the tokenfalseβ NameError. One line names the three JSON atoms before interpolation.
Both were caught by the refusal machinery doing its job loudly, which is the design working: neither failure was silent, both named their cause in one line of stderr.
Not yet exercised, named so nobody reads this bilan as more than it is β
The steward's act path (no pending ledger requests existed), the diff-mismatch refusal, contract-extension governance under both policies, the escalation pause, and the multi-lot loop with real child runs. The next dogfood that counts is a campaign carried from lot 1 on a programme that is not finished β where the supervisor has to do the job it was measured against a human doing.
