Skip to content
Like what we’re building? Star on GitHub

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 modernize lot 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) Β· iterion 853b5ce28 for the bots; the engine the branch binary v3.154.1+d5b7db09f carrying the first wave-1 runtime fix (main was at v3.155.0) Β· child modernize from 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 none launched FROM a scratch copy of the greet project seeded with .modernize/plan.yaml (one lot: a docstring and type hints on greeting(), exit gate = the unit tests and a docstring check) and a .golden-master/ oracle whose verify-oracle.sh runs the CLI and prints a gate-mode JSON report; --store-dir the 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_BIN the branch binary; the handoff answered by iterion 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 observed moved: true, the steward recorded 0 acts and 0 escalations, the loop gate declared the programme exhausted (max_lots 1), finalize wrote .modernize/campaign/handoff.md (1 done, 0 blocked, oracle green) and the run parked on handoff_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.jsonl carries the rendered handoff instructions with blocked 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 whose mode was not gate (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 under escalation: interactive) and governance: 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. And iterion resume does not inherit the launch's --sandbox none: the acknowledgement re-provisioned a sandbox (the bot's devbox realised inside a container) before the final done β€” set ITERION_SANDBOX_DEFAULT=none on 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: interactive pause 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_approval paused 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). Every script: tool sees its own .iterion-script-*.py in 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 token false β€” 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.