Skip to content

feat(land): keep fixing a pick while each round makes progress - #384

Merged
moq-bot[bot] merged 3 commits into
mainfrom
quest/m0/queue-watcher/stop
Nov 2, 2026
Merged

moq-bot[bot] merged 3 commits into
mainfrom
quest/m0/queue-watcher/stop

Conversation

@kixelated

@kixelated kixelated commented Nov 2, 2026 •

Copy link
Copy Markdown
Collaborator

Completes Stop rules: the land fixer keeps fixing a dropped PR, with no attempt limit, while each round makes progress, and hands it back on one of three stop rules. This replaces the one attempt per pick from moq-dev/quest#355.

Changes

  • Per-pick history (src/cli/land-fixer.ts): fix-attempt-<n>.json now holds rounds. Each round records the job that ran it, its outcome (fixed, merged, unchanged, flaky, or handback), the head before and after, the failure's signature, and the rule that fired. history() reads the file and saveRound() writes a round; collecting the same job again replaces that job's round. A record without rounds, written by the old gate, starts a fresh history. pick and requeued stay, so batchable still keeps a requeued fix at its pick's place.

  • Stop rules: stopRule(rounds, round) returns the rule that fires, if any:

    • unchanged: a round with no new commit for a failure that isn't flaky;
    • repeated: the failure the last fix (or merge) addressed recurs right after it. Flaky rounds in between don't reset this;
    • flaky: the third flaky round in a row (FLAKY_LIMIT).

    stopReason writes the handback text naming the rule. conflictFailure(base) is the signature for a conflict: it names the base, so a conflict after the base moved counts as a new failure.

  • Fixer result: an optional one-line failure (at most 200 characters) names the failing test or step. assets/land-fix.md asks the fixer to name it the same way every time that test or step fails.

  • Removed: spent, handbackRepeated, and the pre-launch "already used its mechanical fix attempt" handback in startFixer and in serveBatch's ejection loop.

  • Quest tree: deletes stop.md. The watcher-side wiring that remains (calling saveRound/stopRule per round, merged rounds, and the fallback signature from the merge_group job and step) moves into Fix and re-enqueue's plan, since the watcher's round handling lives there. Links in the epic, flaky.md, bad-picks.md, host-land/fixer.md and land-fixer/README.md are rewritten. The README's landing section describes the rules.

Host gate behaviour

Choice: the host gate (quest land serve) applies the same stop rules, at collection. moq-dev/quest, moq.pro, rust2 and rind keep landing through it until they follow moq.

  • The history record is shared code that the switch must change anyway. Keeping one attempt for the gate would mean keeping spent and handbackRepeated next to the new rules, so I counted this as a fix the switch needs rather than gate work the epic freezes.
  • The audit's 98 "already used its mechanical fix attempt" handbacks came from the one-attempt rule, so the repositories still on the gate get that fix now instead of after they follow.

How gate rounds map to the rules:

Gate case Round
The fixer commits a fix for a blamed PR fixed
The gate itself blamed the PR (a conflict, a quest check rejection) and the fixer requeued it unchanged unchanged: hands back at once (it used to requeue and conflict again)
The fixer blamed no PR, the base passed alone, and the fixer requeued it flaky (the gate has no flaky verdict otherwise); the third one in a row hands back
A PR that only shared a broken batch no round, as before

The signature is merge conflict with <base sha> for conflicts. Otherwise it is the fixer's failure, falling back to the gate's reason (failed_checks); the gate runs one check step, which is the analogue of the watcher's job-and-step fallback.

Choices made without asking

  • Third flaky verdict hands back. The quest's Goal says "re-enqueued unchanged as flaky 3 times in a row", while its Plan says "three flaky rounds in a row hand back". I followed the Plan: the third consecutive flaky round hands back instead of re-enqueuing. FLAKY_LIMIT is one constant if the other reading is wanted.
  • The repeat rule also checks flaky rounds. If a failure the fixer just fixed recurs and the fixer then calls it flaky, the PR is handed back as repeated, since the two verdicts contradict each other.
  • "Right after" means the last non-flaky round. A failure that comes back two fixes later (A, then B, then A) is not a repeat. The quest's rules allow that loop as long as each round changes something.
  • Rules are evaluated when a job's result is collected, once the outcome and the fixer-named signature are known. Nothing is decided before the fixer runs.

Suggested follow-ups

None new. The remaining watcher wiring is in Fix and re-enqueue.

Checks

just check and just test pass locally. just test passed with CLAUDE_CONFIG_DIR unset: before moq-dev/quest#340, now merged, two agent.test.ts cases read the caller's environment.

Review

Codex (quest review), gpt-6.1-sol, medium effort, round 1 on head f666e41 against origin/main 92d04c1: 0 findings.

🤖 Generated with Claude Code

(Written by Claude Opus 5.5)

kixelated and others added 2 commits November 1, 2026 17:18
Replace the one fixer attempt per maintainer pick with a per-pick history of
rounds in fix-attempt-<n>.json: each blamed failure's outcome (fixed, merged,
unchanged, flaky, or a handback), the head before and after, and the
failure's signature. `stopRule` hands a pick back on a round with no new
commit for a failure that isn't flaky, on the failure a fix addressed
recurring right after it, or on the third flaky round in a row, and the
handback comment names the rule. The host gate applies the same rules at
collection; the queue watcher will call them too.

The fixer names each failure's test or step in its result's optional
`failure`; a conflict's signature names the base it conflicts with, so a
conflict after the base moved is a new failure. A record written before
rounds were kept starts a fresh history.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title The fixer keeps going while it makes progress feat(land): keep fixing a pick while each round makes progress Nov 2, 2026
@kixelated
kixelated marked this pull request as ready for review November 2, 2026 01:32
@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

Important

  • Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 199538c5-9957-429c-9b93-d42624cf7d8e
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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

@kixelated
kixelated enabled auto-merge (squash) November 2, 2026 01:44
@moq-bot
moq-bot Bot merged commit 206f689 into main Nov 2, 2026
5 checks passed
@moq-bot
moq-bot Bot deleted the quest/m0/queue-watcher/stop branch November 2, 2026 01:50
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