fix(git): a merge driver declared after a branch was cut never fires on it - #3309
Merged
Merged
Conversation
…on it Seven open PRs could not be updated onto main today. Two of them conflicted only on docs/audits/impl-estimates.jsonl -- a file that is strictly append-only and for which #3256 (768e740) already declared `docs/audits/*.jsonl merge=union`. Git reads merge attributes from the side being merged INTO. A branch cut before the rule landed carries a .gitattributes without it, so the driver is never consulted: the declaration is retroactively inert. agent/T-2 is the measured case -- its .gitattributes lists only the roadmap rule, 768e740 is not an ancestor, and the merge conflicts. The blobs were never the problem: `git merge-file --union` on the three stages returns rc=0, 50 rows, 0 markers. This script already solves that shape for roadmap.yaml by registering the driver per invocation rather than in the shared config. Bind union the same way, with core.attributesFile -- the LOWEST priority source, so it applies only where the branch has no rule of its own and a branch that already carries the declaration is untouched. Nothing shared is written; the temp file dies with the process. Verified on #3271, whose merge had failed minutes earlier: rc=0, all three of Makefile, impl-estimates.jsonl and roadmap.yaml auto-merged, 49 rows with 0 invalid and 0 duplicate, both roadmap guards PASS. Row 9 carries both polarities -- the fixture must CONFLICT without the override and merge WITH it -- because an override that is never needed cannot be told from one that does not work. Mutation-verified: blanking the override file turns row 9 red (8/9), restoring it green (9/9). Closes #3308 Pmat-Ticket: PMAT-3308 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
noahgift
enabled auto-merge
September 15, 2026 13:04
|
§13.11 rung 1 — quorum shadow verdict Shadow mode: this records a verdict and merges nothing. A refusal |
Merged
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.
The blocker
Seven open PRs could not be updated onto main today —
gh pr update-branchrefused each with "Cannot update PR branch due to conflicts". Two of them (#3005, #3271) conflicted only ondocs/audits/impl-estimates.jsonl, a file that is strictly append-only and for which #3256 (768e740b1) already declared the correct resolution:It still conflicted.
Mechanism
A
.gitattributesmerge-driver declaration is retroactively inert. Git reads merge attributes from the side being merged into. A branch cut before the rule landed carries a.gitattributeswithout it, so the driver is never consulted.Measured on
agent/T-2(#3005):The blobs were never the problem —
git merge-file --unionon the three stages returnsrc=0, 50 rows, 0 markers, every line valid JSON. The attribute simply never bound. This is the "declared fix that cannot fire" class: the repo has had the right declaration since #3256 and every older PR kept conflicting anyway, with no signal that it was not in play.Fix
ci_resolve_dirty.shalready owns this shape forroadmap.yaml: it registers the driver per invocation, never in the shared config, so nothing leaks into a.gitshared by worktrees. Bindunionthe same way, viacore.attributesFile.core.attributesFileis the lowest-priority attribute source, so it applies only where the branch has no rule of its own — a branch that already carries the declaration is untouched. The temp file dies with the process.Evidence
Verified on #3271, whose merge had failed minutes earlier:
49 rows, 0 invalid, 0 duplicate.
check_roadmap_ids_unique.shandcheck_roadmap_sorted.shboth PASS.Row 9 carries both polarities — the fixture must CONFLICT without the override and merge WITH it — because an override that is never needed cannot be distinguished from one that does not work.
bashrs lint: 0 errors.Drained under this diagnosis
ci.ymlcontent edits — not this classCloses #3308
🤖 Generated with Claude Code