fix(queue): unstall batch construction -- porcelain parse + pre-push regeneration - #740
Merged
Merged
Conversation
…regeneration
Two independent defects left the merge-queue advancer reporting PASSED every
pass while merging nothing; the board grew to 38 open PRs.
1. dirty_paths sliced the porcelain line at a fixed column (line[3:]). git()
strips its output, so an unstaged modification arrives as 'M path' rather
than ' M path' and the slice ate the first character of the path
('ests/CLAUDE.md'). Every registered generated file therefore read as an
unregistered edit, worktree_is_safe answered 'working tree is dirty'
forever, and no integration branch could be built at all. The status field
is now matched, not sliced, which is correct for both forms.
2. A batch is a tree no member ever tested: when two individually-green members
each add a test file, the suite counts in tests/CLAUDE.md are right on both
branches and wrong on their union. The pre-push hook ran the drift gate and
fail-closed, so the batch push died with '[DRIFT] Test suite count mismatch'
and the queue held a branch it could never publish. regenerate_on_batch now
runs the generators on the batch branch before the push and commits ONLY
paths in the GENERATED_PATHS registry.
No gate is weakened: the same generator the gate calls is used, the gate still
runs on the pushed branch and in CI, a generator that fails or writes an
unregistered path commits nothing and the push fails closed as before.
The registry names --fix, not --regenerate: the pre-push hook's own failure
text advises a flag verify_test_suite_count.py rejects, so a test invokes each
generator's real argument parser rather than trusting the string.
Tests: 122 in test_merge_queue.py (8 new), all proven to fail against the
pre-fix module.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
tools/CLAUDE.md is already 151 lines on main (the linter counts
content.split("\n"), so a trailing newline is the 151st element). ci.yml's
"CLAUDE.md linter gate (scope invariant)" step therefore fails ci (0) on every
PR branched after the file crossed the cap -- #693 and this branch both hit it
while main itself stayed green, because that gate does not run on push to main.
Folds alert_bridge.py and dep_graph.py onto one row -- the same pairing this
file already uses for launch_tui.py / orchestrator_status.py. No entry is lost.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the merge-queue stall that grew the board to 38 open PRs while the
AesopMergeQueuedaemon reported PASSED on every pass.Why nothing was merging
Three independent causes, two fixed here:
1.
dirty_pathsoff-by-one (fixed). It sliced the porcelain line at a fixed column (line[3:]).git()strips its output, so an unstaged modification arrives asM path, notM path, and the slice ate the first character —tests/CLAUDE.mdparsed asests/CLAUDE.md. Every registered generated file then read as an unregistered edit, soworktree_is_safe()answeredworking tree is dirtyforever and no integration branch could be built at all. Reproduced live against the deployed tree. The status field is now matched, not sliced, which is correct for both the raw and stripped forms.2. Batch union fails a gate every member passed (fixed). A batch is a tree no member ever tested. When two individually-green members each add a test file, the suite counts in
tests/CLAUDE.mdare correct on both branches and wrong on their union. The pre-push hook ran the drift gate and fail-closed, so the batch push died with[DRIFT] Test suite count mismatch in tests/CLAUDE.md: Python: CLAUDE.md says 235, actual is 236and the queue was left holding a branch it could never publish (git_failedrow, 2026-08-03T08:00:09Z).regenerate_on_batchnow runs the generators on the batch branch before the push and commits ONLY paths in theGENERATED_PATHSregistry.3. #712 poisons every batch (not fixed here; #712 held as
queue-rejected). #712 wiresimport_resolution_checkinto ci.yml as a hard 'Sibling import guard' step. The checker on main does not yet recognize the sanctioned sys.path idiom and flagstools/merge_queue.py:68-69, failingci (0)on every integration branch. #712's own green check predatesmerge_queue.pylanding on main, so it reads individually-green while poisoning each batch it joins. #724 fixes the checker — land #724 first, then re-admit #712.No gate is weakened
Same generator the gate itself calls; the gate still runs on the pushed branch and in CI; a generator that fails, is missing, or writes an unregistered path commits nothing and the push fails closed exactly as before (
regenerator_failed/regenerator_overreachexception rows).Bonus defect
The registry names
--fix, not--regenerate: the pre-push hook's own failure text advises a flagverify_test_suite_count.pyrejects (unrecognized arguments). A test now invokes each generator's real argument parser rather than trusting the string.Tests
122 in
tests/test_merge_queue.py(8 new), all proven to fail against the pre-fix module:OLD parse of stripped form: ['ests/CLAUDE.md']vsNEW: ['tests/CLAUDE.md']OLD has REGENERATORS: Falsetest_merge_queue.py+test_merge_train.py: 164 passed.🤖 Generated with Claude Code