Skip to content

feat(engine): keep an accepted chain alive to repair red CI (CI repair, U3) - #15

Merged
Steel-tech merged 4 commits into
feat/ci-repair-ledgerfrom
feat/ci-repair-engine
Sep 28, 2026
Merged

Steel-tech merged 4 commits into
feat/ci-repair-ledgerfrom
feat/ci-repair-engine

Conversation

@Steel-tech

Copy link
Copy Markdown
Contributor

Stacked on #14 (itself on #13). Base is feat/ci-repair-ledger; merge in order and GitHub retargets each.

What

U3 of the CI repair plan: the engine keeps an accepted chain alive so a red CI run can be repaired inside the same attempt.

When a definition declares publish.ci.on_fail, an accepted attempt that isn't held now comes back with a worker.Continuation. Each RepairCI(ctx, failure) call runs one round:

  1. The on_fail phase receives the CI failure as its input envelope, through the same door a failing test already uses. The CI data is bounded and framed as data, not instructions.
  2. Every phase after it runs again, with if: guards, gates and repair edges all live. Phases before it do not re-run.
  3. The round is judged by the same acceptance check and publish hold as the chain. run is split into run and conclude, so both end through one door.
Carried into a round Not carried
Field view and who reported each field, phase results, gate reports Agent sessions and transcripts (rounds get their own session keys, <attempt>-ci<N>-<role>)
Send count and wall-clock deadline, so a round never buys more budget The ephemeral HOME: wiped when the chain ends and when each round ends, then re-seeded per role
Handoff notes directory Touched paths: a round reports only what it changed

A round spends the continuation if it isn't accepted, trips the hold, changes nothing (ci_repair_no_change), or leaves a HOME that can't be wiped. Release destroys everything and can safely be called twice.

Who releases: the worker's attempt loop and jig run release unconditionally. Hosts that own resources a round still needs, such as jig worker's scratch removal and trace stream, hand that cleanup to the continuation via worker.DeferToContinuation.

⚠️ Still inert

Nothing calls RepairCI until U5. Every continuation is released as soon as the attempt ends, so behaviour on main is unchanged. The existing suite, including the exact event-sequence tests, passes untouched.

Verification

  • just check is green.
  • 17 new engine tests on the scripted runtime cover:
    • What a round runs: only run and later phases; a reviewer rejection loops the builder under its own edge; a high-risk fix trips the hold; the repair phase ignores its own if: guard.
    • Budgets and cancellation: shared send budget, shared ceiling, cancellation.
    • HOME and scratch: the HOME is wiped after the chain and after a round (with a credential-shaped file written into it); roles are re-seeded; handoff notes survive; an unwipeable HOME gets no round; a panic still destroys the scratch.
    • Lifecycle and input: no continuation unless repairable; Release is idempotent and refuses later rounds; a no-change round ends the repair; every CI field is bounded.
  • 3 worker tests: the attempt loop releases; DeferToContinuation runs cleanup once, after the inner Release; an unbound publisher releases what it drops.
  • Mutation-checked: 35 breaks, all caught. Three results needed correcting first:
    • Two fixture gaps let mutants survive: HOME was never actually written to, and re-seeding was untested. Both fixed.
    • Two "caught" results were compile errors, not real catches. Both were redone so they compile, and are now genuinely caught.

Review

An independent review found a design-level bug, fixed in 69d2ede. jig worker's runner removed the scratch and closed the trace stream as soon as Execute returned, so a U5 round would have started with no handoff notes and a dead trace. It also found five more problems, all fixed: a leak on the unbound publisher path, a panic skipping HOME cleanup, a failed HOME wipe being ignored, the missing no-change outcome, and the uncapped head.

Not yet proven: the cmd/jig/worker.go wiring itself. It can't be exercised until a round actually runs, so the plan now requires U5 to include an end-to-end round through the real jig worker harness.

🤖 Generated with Claude Code

Steel-tech and others added 4 commits September 27, 2026 20:49
…r U3)

An accepted, unheld attempt whose definition declares publish.ci.on_fail
now returns a worker.Continuation with its outcome. RepairCI runs one
round: the on_fail phase receives the CI failure as its input envelope,
bounded and framed as data, then every phase after it runs again with
guards, gates, and repair edges live, and the round is judged by the same
acceptance and publish hold as the chain (run is split into run and
conclude so both end through one door).

A round carries the field view, results, gate reports, send count, and
deadline, so it can never buy the attempt more budget, and reports only
the paths it changed. It does not carry sessions or HOME: the HOME is
wiped when the chain ends and when each round ends, roles are re-seeded
on first use, and round sessions get their own key namespace. The
handoff directory is kept. A round that does not end accepted and unheld
spends the continuation.

Release destroys the scratch and is idempotent. The worker's attempt loop
and the direct run path release unconditionally; nothing uses a
continuation until the publishing runner does in U5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d harden its HOME

Review of U3 found that jig worker's runner removed the attempt's scratch
and closed its trace stream as soon as Execute returned, before any
repair round could use them: a round would have lost the handoff notes
and emitted into a closed trace. worker.DeferToContinuation now moves a
host's cleanup into the continuation's Release, once, after the engine's
own Release, and cmd/jig/worker.go uses it. An unbound publishing runner
now releases the continuation it would otherwise drop.

Execute keeps a panic-safe defer that destroys the scratch unless a
continuation took ownership. A HOME that cannot be wiped (an agent can
make RemoveAll fail with a read-only directory) is never handed to a
round: no continuation after the chain, a spent one after a round. An
accepted round that changed nothing ends as ci_repair_no_change and
spends the continuation. The CI head is capped like every other CI field,
and the bounds, handoff survival, and panic path are now tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 28, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 82202a61-f8c9-4f0d-b7ee-33899fc158ef

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Steel-tech
Steel-tech merged commit 45871c0 into feat/ci-repair-ledger Sep 28, 2026
5 checks passed
Steel-tech added a commit that referenced this pull request Sep 28, 2026
Land CI repair U2 + U3 on main (merged into stacked bases by #14/#15)
@Steel-tech
Steel-tech deleted the feat/ci-repair-engine branch September 28, 2026 05:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant