Skip to content

feat(worker): repair red CI in rounds inside the attempt (CI repair, U5) - #18

Merged
Steel-tech merged 2 commits into
mainfrom
feat/ci-repair-loop
Sep 28, 2026
Merged

Steel-tech merged 2 commits into
mainfrom
feat/ci-repair-loop

Conversation

@Steel-tech

Copy link
Copy Markdown
Contributor

What

U5 of the CI repair plan: the round loop. With this PR, a definition that declares publish.ci.on_fail actually repairs red CI inside the attempt. Single PR against main.

When publish ends ci_failed, the engine kept the chain alive (U3), and the frozen definition declares a repair budget, PublishingRunner runs rounds until CI is green or the budget runs out:

  1. Head check: the head jig last pushed is read from the ledger (U2). If CI was red on a head jig didn't push, someone else pushed, so no round runs (ci_repair_head_moved).
  2. Authorize round N under a freshened lease.
  3. Repair: the red checks plus their log tails (U4) go to RepairCI. The repair phase and every phase after it run again, judged by acceptance and the publish hold.
  4. Only a round that ends accepted, unheld and changed goes further:
    • it is fenced again right before the push;
    • its own commits may carry only paths it changed;
    • it is pushed non-force, the remote head is proven, the round is recorded, and remote_ref moves to the new head.
  5. CI is awaited again: green publishes, red loops.
Stop Code Pushed?
Budget spent, CI still red ci_repair_exhausted earlier rounds only
Someone else pushed ci_repair_head_moved no
Round cancelled / not accepted / held / changed nothing ci_repair_cancelled / _round_failed / _held / _no_change no
Round committed a path it never changed publish_staging_escape no
Lease lost during the round lease error no
Non-force push rejected (a person pushed mid-round) publish_push_failed no

Every stop leaves the attempt accepted_unpublished with the PR at the last pushed head. PublishSummary.ci_repairs lists every round with its heads, failed checks, changed paths, acceptance evidence and outcome. The publish-only retry never repairs (R11).

⚠️ Now live for definitions that opt in

This is the first PR where an agent pushes to a PR after CI goes red. It happens only for definitions that declare publish.ci.on_fail, and no stock definition does yet: factory.yaml opts in with U6. Definitions without on_fail behave exactly as before; a test confirms it even when the engine hands back a continuation.

Verification

  • just check is green.
  • 13 worker tests against the real control plane and git remotes, with a scripted continuation: one round to green; two rounds chained through the ledger; budget exhausted; a person's push before a round and during one; held, failed, cancelled and no-change rounds; lease lost mid-round, which never pushes; undeclared committed paths; no continuation; a continuation without on_fail; the retry never repairs.
  • End-to-end (cmd/jig/ci_repair_test.go), as the plan required: one round through a live jig serve and the real jig worker wiring. It plants a handoff note while CI is red and asserts that it survives the round, that the round's trace events reach the control plane, and that the scratch is gone afterwards. Reintroducing the U3 wiring bug fails it.
  • Mutation-checked: 22 mutants caught. Three first-pass survivors led to new tests: two-round chaining, which is the U2 review's trap; a continuation without on_fail; and a broken mutant, redone.

Adversarial review: four findings, all fixed

  1. P1, a stale attempt could push after losing its lease. The fence ran before a long round, not before the push. It now runs again right before the push.
  2. The attempt record described the chain, not the pushed round. Each round now carries its changed paths and acceptance, and remote_ref follows the push.
  3. A definition's own git add -A in a round could commit the chain's test artifacts unchecked. Now refused.
  4. A cancelled round was reported as a failed round. It now has its own code.

Known untested (defensive, stated rather than hidden)

  • Lease revival by freshen after sleep. It mirrors publishStep's existing pattern, and needs a clock the lease doesn't expose.
  • The Completed-conflict guard, which is only reachable in a real race.
  • Remote proof failing after a successful push.
  • The host closing the trace on release, which would leak a file handle, not lose events.

🤖 Generated with Claude Code

Steel-tech and others added 2 commits September 27, 2026 22:05
When publish ends ci_failed, the engine kept its chain alive, and the
frozen definition declares publish.ci.on_fail, the publishing runner now
repairs in rounds until CI is green or the budget runs out. Each round
reads the head jig last pushed from the ledger, refuses to build on a
head someone else pushed, authorizes the round under a freshened lease,
and hands the red checks and their log tails to the engine's RepairCI.

Only a round that ends accepted, unheld, and changed goes further: it is
fenced again right before the push (a round can outlive its lease), its
own commits may carry only paths it changed, and then its paths are
committed and pushed non-force, the remote head is proven, the round is
recorded, remote_ref moves to it, and CI is awaited again.

Every stop short of green leaves the attempt accepted_unpublished with
the pull request at the last pushed head and every round's heads,
changed paths, acceptance, and outcome in the publish summary; a
cancelled round says so. The publish-only retry never repairs.

cmd/jig/ci_repair_test.go runs a round through the real jig worker
wiring and fails if the continuation's scratch or trace do not outlive
Execute, the bug the U3 review found.

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

Warning

Review limit reached

Next included review available in 14 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 9e6a35fb-1739-47ef-8561-5f566aa93ba0

📥 Commits

Reviewing files that changed from the base of the PR and between 39267bc and 3fb380f.

📒 Files selected for processing (5)
  • cmd/jig/ci_repair_test.go
  • docs/plans/2026-09-27-001-feat-ci-repair-plan.md
  • internal/worker/publish.go
  • internal/worker/publish_repair.go
  • internal/worker/publish_repair_test.go

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 81dd298 into main Sep 28, 2026
5 checks passed
@Steel-tech
Steel-tech deleted the feat/ci-repair-loop 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