Repository navigation
feat(kix): recover jobs from lost and blocked publications - #190
Conversation
Quest-Claim: f5cf5caa948c10bc
A retry records the failed job it continues, and a planning PR's decision record follows that chain, so answers survive a failure that opened no PR. A failed job's PR whose number went unrecorded no longer blocks its retry: the inbox and the start route accept the only open PR observed on the job's branch at a commit it published, and delivery adopts the branch's only open PR. A publication decision on a service fork branch that changed outside its job, or was left by earlier work, offers Start over: kix resets the branch to the job's revision and queues a fresh job there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Implementation pushed in cefdbbd. (written by Claude Opus 5.5) |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3461982ad9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Start over force-reset the service fork branch before recording the restart in D1, so a concurrent answer or cancel could let the reset discard commits and then refuse the restart. Replace the job first, then reset. If the reset fails, the replacement waits on the same Start over decision instead of running against the unreset branch. Regression tests drive both cases through the seeded fake GitHub. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Renumber the recovery migration to 0016 after main's 0015_activity_reads, and move the recovery seeds off main's new pull request 17 and themes draft: the unrecorded pull request becomes 20, and the blocked job moves to a new fonts quest. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merging via
(written by Claude Opus 5.5) |
Finishes
quest/m0/kix/recovery.md. A maintainer can get a failed or blocked kix job moving again from the inbox without going to GitHub, and a retry keeps answers that were already given.Changes
createJobnow records which failed job a retry continues (jobs.continues, migration0016_recovery.sql).store.decisionsfollows that chain instead of matching jobs by pull request, so a failed planning job that pushed commits but never opened a PR keeps its answered rounds in the retry's decision record.continued()instate.tsgives the PR a retry continues. That is the PR the failed job recorded, or else the only open observed PR on the job's branch whose head is a commit the job published (headorpending). Matching the commit rather than just the branch name means a contributor's PR on a same-named branch still claims the quest, which was the concern raised on kixelated/quest#171. The inbox's Try again button and the start route both use it.deliveradopts the branch's only open PR from the service fork, so it no longer needs the PR to have been created after the current job. Publication already checks that the branch is where the job left it. A PR opened since the job and then closed by a maintainer is still not replaced.BranchChanged), now hasrestart: trueand shows a Start over button. The button can also be called through the answer route asrestart: true. Starting over rechecks the answering maintainer's authority, then claims the restart in D1 (store.restart): it answers the decision, marks the old job cancelled, and queues a new job at the job's revision with a fresh session and no recorded PR. Only after that claim succeeds does it force-reset the fork branch to the revision (Branch.reset), so a concurrent answer or cancel refuses the restart before any commit is discarded. If the reset fails,store.holdputs the replacement, which hasn't run yet, on the same Start over decision so the maintainer can retry. If a runner already took the replacement, it finds the branch changed when it publishes. If the branch's PR is still open, the new job's delivery adopts it. An iterated PR's head is never offered for reset.cloudflare/dev/github.ts,dev/seed.ts): now models the service fork (its identity and forced or created branch refs) and PRs opened from the fork. The seed adds a failed job whose PR went unrecorded and a waiting job whose branch changed, so both states show up on the local server.cloudflare/GITHUB.mdandcloudflare/README.md. The quest file and its link are removed.Validation
just checkandjust testpass after merging main (Worker: 319 tests).seed.test.tscase, run through the seeded fake GitHub and the inbox forms: Try again on a quest whose failed job's PR fix(skills): route every follow-up through /quest-plan in the foreground #20 went unrecorded, and Start over resetting the fork branch and queueing a new job. Repeating it is refused.seed.test.ts, also through the seeded fake GitHub: a cancel injected while the restart rechecks GitHub leaves the fork branch untouched, and a failed reset leaves the replacement waiting on Start over, which then succeeds. Both fail with the earlier reset-then-claim ordering.publish.test.tscases: decisions survive a chain of two failed jobs with no PR, the unrecorded open PR is adopted on retry, Start over followed by republishing keeps the PR, and an iterated head is never offered Start over.Limitations
publish.test.ts. The seeded fake doesn't model commits or PR creation, so it covers only the inbox-level paths.Decisions
Settled by the maintainer:
revision, not the current default-branch commit. It's deterministic, and a retry or a new start already picks up the current commit.continueslink, so the old answers drop out of the decision record because their work was discarded. Requeueing would keep the oldcreatedAt, and a PR closed by the reset would then block delivery.dev/github.tscovers only what the inbox flows need, and the publication internals stay onpublish.test.ts's own fake. Merging them is a suggested follow-up below.Chosen while addressing review, open to change:
0016_recovery.sql, after main's0015_activity_reads.sql. The recovery seeds also moved off main's new PR feat(skills): rename quest-complete to quest-finish #17 and themes draft: the unrecorded PR is now fix(skills): route every follow-up through /quest-plan in the foreground #20, and the blocked job is on a newquest/m1/fonts.md.Suggested follow-ups
test/publish.test.ts's stateful GitHub fake intocloudflare/dev/github.ts(commits, fast-forward refs, PR creation), so the local server can run publication end to end. Recommended.🤖 Generated with Claude Code
(written by Claude Opus 5.5)