Skip to content

feat(git): declare the board and notes merge=union - #663

Merged
tieubao merged 7 commits into
masterfrom
feat/wrap-pop-union
Sep 16, 2026
Merged

tieubao merged 7 commits into
masterfrom
feat/wrap-pop-union

Conversation

@tieubao

@tieubao tieubao commented Sep 16, 2026

Copy link
Copy Markdown
Member

What

The repo now carries a .gitattributes declaring _meta/BACKLOG.md, _meta/backlog-staging.md and docs/implementation-notes/*.md merge=union. It never had one.

Board row: ID-886.

Why

The kit shipped every union mechanism (wrap's carry-across-pull, the union re-merge, board dedupe-all) and no declaration of its own, so git check-attr merge -- _meta/BACKLOG.md answered unspecified. Two sessions adding a row collided by hand: one sitting resolved the same stash-pop conflict four times, each time a stash push, an ff-only merge, a conflicted pop, a throwaway script over the hunks, and a reset to leave the row unstaged. ops-toolkit declares the same file union and never needed it.

The brief asked for something else

The brief asked for a hunk resolver on bin/wrap apply's POP CONFLICT path, on the premise that git stash pop leaves markers on a union-declared file. Refuted before any code:

  1. A measured repro (upstream row and local uncommitted row in the same hunk) has the pop exit 0 with both rows, no markers, the file unstaged. Binary-declared variants do fail, and leave no markers, so a resolver never sees them.
  2. tests/test-wrap.sh has asserted this since the pull-past-dirty work, and lib/wrap/wrap.sh states it in the _unstash comment.
  3. The pop conflicted because the file was not declared union HERE, which the proposed check-attr == union gate would not have fired on either.

Evidence and the rejected alternative: docs/implementation-notes/gitattributes-union.md.

The one code change the declaration forced

Declaring the board union makes wrap's carry-across-pull path reachable for it. That path inserted below the first --- line; the board has none, so a carried row would have landed at line 1, above the title. _union_carry_back now keeps the anchor rule for a file that has an anchor and runs git merge-file --union over pulled/base/local for a file that has none. Narrowing it that way leaves every asserted contract intact: the driver orders the incoming entry above the local one, which is backwards for a newest-first LAB_LOG.

A first attempt placed the block after its local neighbour. Review broke that four ways (blank neighbour, repeated neighbour, non-contiguous lines, empty first line); the driver answers all four in less code.

Verification

Check Result
bash tests/test-gitattributes-union.sh 27/27, exit 0
bash tests/test-wrap.sh all 508 passed, exit 0
bash tests/test-meta.sh 853/853, exit 0
bash lib/gate/negctl.sh . "bash tests/test-gitattributes-union.sh" "rm .gitattributes" PASS (green, red under mutation, green restored)
Review code-reviewer on the bash diff, six findings, four fixed by the driver, one an accepted documented tradeoff, one stale

Proof of done: docs/verification/gitattributes-union.md.

Accepted tradeoff

Union never drops a line, so two branches flipping the same row's Status yield that row twice instead of a conflict. The next board set then refuses with "matches N rows; dedupe first", which is loud and recoverable. The .gitattributes comment names it and says to check which side's Status dedupe-all kept. ops-toolkit made the same call.

The repo shipped every union mechanism (wrap's carry-across-pull, the union
re-merge, the board dedupe) and no .gitattributes of its own, so
`git check-attr merge -- _meta/BACKLOG.md` answered `unspecified`. Two sessions
adding a board row collided by hand; one sitting resolved the same stash-pop
conflict four times.

Declaring the board, the staging board and the implementation notes union clears
both collisions at the source: git's union driver resolves the stash pop, and a
parallel-branch merge on the table keeps both rows.

That declaration also makes wrap's carry-across-pull path reachable for the
board, which had no `---` anchor and would have prepended a carried row to line
one, above the title. `_carry_after_neighbour` places the block after the line it
followed locally, and only when the anchor rule finds nothing, so every
anchored file keeps today's placement.

The brief asked instead for a hunk resolver on the POP CONFLICT path. That was
refuted before any code: a measured repro shows `git stash pop` resolving a
union-declared text file with no markers, and tests/test-wrap.sh has asserted the
same since the pull-past-dirty work. Evidence and the rejected alternative are in
docs/implementation-notes/gitattributes-union.md.
@tieubao
tieubao merged commit 64042a7 into master Sep 16, 2026
1 check passed
@tieubao
tieubao deleted the feat/wrap-pop-union branch September 23, 2026 07:11
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