Development record
A managed repository's shared append-only files conflict on nearly every merge, and abcd propagates neither of the two remedies it has already adopted for itself.
A managed repository's shared append-only files conflict on nearly every merge, and abcd propagates neither of the two remedies it has already adopted for itself.
Observed landing 27 worker branches through one integration branch in a single day. .abcd/work/DECISIONS.md is one file every branch appends to, so it conflicted on the first two merges and would have conflicted on every later one; the session fixed it by adding a merge=union attribute for that path to .gitattributes by hand, which is correct because the lines are independent and dated. CHANGELOG.md's [Unreleased] section has the same shape and conflicted on four of the eight merges that carried an entry, but union is NOT safe there: it duplicates the ### headings. That one needed a hand-written section-merging script, kept only in the session's local tier. The reporting session refined this afterwards, and the distinction is the point of the record: once the union attribute was in place the decisions log stopped conflicting entirely, while the changelog went on conflicting on most of the merges that remained, because union is the wrong remedy for it rather than an unapplied one. Two conflict classes that look identical at a glance therefore have different fixes, and a remedy that scaffolds only the attribute would close one and leave the other exactly where it was.
abcd has already answered both questions for its own repository and neither answer travels. iss-118 resolved by adding merge=union to abcd's own .gitattributes for DECISIONS.md and ACKNOWLEDGEMENTS.md; nothing in ahoy install or prepare writes that attribute into a repository abcd adopts, so every managed repo rediscovers the conflict and fixes it by hand or not at all. iss-2608220150157510 carries the per-change changelog fragment proposal for abcd itself; a managed repo needs the same thing and has even less standing to invent it locally.
Wanted, in either order: scaffold the union attribute for the decisions log at adoption time (it is a one-line write into a file ahoy already manages), and give the changelog a per-change fragment directory, which removes both conflict classes rather than one. Storing decisions one-per-file like issues would do the same for the first, at the cost of a new id family — the ledger's one-file-per-record shape was the one thing in this run that never conflicted at all, in 27 merges and 33 resolutions.
Grounds
- pursued: the two files that conflict on every merge are the two that are single append-to-the-bottom files, and the five record families that never conflicted in 27 branch merges are all one-file-per-entry — so the conflict is a property of the file shape, not of the content, and giving the decisions log the ledger's shape removes the class rather than patching it. What would show this wrong: a folder of minted decision files that still conflicts on merge (an index assembled into one committed file would do it, if the assembly is committed rather than derived), or a measured cost of reading the decision history that the index does not recover.
Corroboration (2026-09-19, Gropius session gropiusllm-66, relayed to abcd-17). The conflict became a livelock at scale: a merge queue plus some twenty pull requests that each append to DECISIONS.md and CHANGELOG.md. The forge ignores the union merge driver, every landing re-dirties every other entry, CI on every push saturates the runners, and the queue drops entries that wait past its timeout. The session landed them as one integration branch in one queue entry. Worth a line wherever the landing process is documented for a managed repository, beside this record's deferral.
Corroboration (2026-09-20, Gropius session gropiusllm-2b, relayed to
abcd-17). Two further facets from a sub-agent-lane experiment in the same
repository. Inside one checkout: two sessions appending to DECISIONS.md at once
have no lock, and keep the file consistent only by an append-only discipline
agreed by message; the session asks for an append verb (abcd decide --line "…" in shape) or for the log to become a directory of dated files, so
concurrent sessions are safe by construction rather than by agreement. Across
branches: the livelock recorded on 2026-09-19 recurred, and the
integration-branch landing that resolves it is folklore in that repository's
handover file; the session asks for a launch integrate verb or a documented
recipe so the pattern is abcd's rather than each repository's.
Same day, second session (gropiusllm-97): the two-session append to
DECISIONS.md and NEXT.md held by convention only, and the ask is the same
append-only decide line verb.
- pursued: decisions recorded on branches from two accounts merge without anyone resolving a conflict; shown wrong if a pull request that records a decision is still dropped from the merge queue over a decision conflict.
Deferral 2026-09-29
Deferred past v0.11.1: Promoted to itd-2609151138388536 (draft), and a promoted issue keeps its folder until the intent ships (commands/capture.md, promote). The product thinker's ruling M28 of 2026-09-23 plans it next cycle as its own intent. Owed: that planning interview, which opens on one question: for a managed repository's existing decision log, convert it, offer to convert it, or apply the folder of records to new entries only?
Remedy grounds (2026-09-29)
- The 2026-09-15 ruling fixes the shape (one file per decision, as the ledger); only the treatment of an existing log is open.
- SOTA check: towncrier (https://towncrier.readthedocs.io/en/stable/tutorial.html, read 2026-09-29) keeps one fragment file per change and assembles them at release; abcd's derived changelog (adr-37; itd-2609150819432059 scaffolds the empty Unreleased anchor for managed repositories) is the same idea taken further, since the records are the fragments.
- Rejected: propagating merge=union, which the forge's merge ignores and which duplicates changelog headings.
Frontmatter
| id | iss-2609100507439414 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | major |
.abcd/work/issues/resolved/iss-2609100507439414-append-only-logs-conflict-on-every-merge.md
open on GitHub ↗ · commit history ↗
References1
- itd-2609151138388536Every Decision Is Its Own Record, and the Log Stops Conflictingrelated
mentions10
- adr-37Releases are changelog-driven — rolling `[Unreleased]` is the release decision, and automation tags exactly that commit
- adr-2609151138420062The decisions log is a folder of minted records, not one appended file
- dcn-26091500000000042026-09-15 — The decisions log becomes a folder of individually minted records with a derived index, and `DECISIONS.md` a symlink to that index for now (product thinker, reframing iss-2609100507439414 from "how does adoption propagate abcd's merge-attribute workaround into a managed repository" to "why do the two single append-to-the-bottom files conflict when the five one-file-per-record families did not once across 27 merges", the measurement in iss-2609100508570803). The shape applies to abcd and to every managed repository; the decisions-append gate (DA001–DA004) is retired by the shape, in the same change the shape lands. The rule is adr-2609151138420062, the capability itd-2609151138388536 in `drafts/`; the changelog's conflict class is itd-2609150819432059's and closes separately. The adoption question the product thinker was first asked — point at the conflict, fix it silently, or fix it and take over the changelog — is dissolved by the shape rather than answered. Rejected: keep the file and propagate the union attribute; the folder for abcd only; keep the file and accept the conflicts.
- iss-118DECISIONS.md and ACKNOWLEDGEMENTS.md are multi-writer single files, the merge-hotspot shape that one-writer-per-file rules out. DECISIONS.md was the only textual conflict of the two-programme merge (resolved by hand as a union of both sides). The remedy is a design call per the principle's bounds: per-decision records (full atomicisation, which needs a new id family plus an armed uniqueness detector) or a merge=union gitattribute (smallest fix, legitimate because the ledger's entries are anonymous dated lines that never need identity). ACKNOWLEDGEMENTS.md is the same shape at lower traffic.
- iss-2608220150157510CHANGELOG.md is the repo's most contended file (205 changes since July): every user-facing change appends an entry near the top, so any two in-flight branches conflict there — and it keeps biting on real merges. The SOTA fix is per-change fragments (the towncrier/changesets pattern, which is also the shape the ledger already uses): each change commits one fragment file with a collision-proof name, and the release cut — which already owns CHANGELOG writes via launch ship (adr-37) — assembles the dated section and deletes the fragments. Zero contention by construction; the changelog-driven release flow is unchanged, only its input format widens
- iss-2609240646538011The `merge=union` driver that .gitattributes gives `.abcd/work/DECISIONS.md` and `CHANGELOG.md` (the remedy iss-118 adopted) holds for a local merge only; the forge does not apply it. A pull request's mergeability and the merge queue's merge are computed on the forge, so two open pull requests that each append a DECISIONS.md entry conflict there as soon as one merges, while a local `git merge` of the same two is clean. In autonomous run A every records pull request went DIRTY whenever another records pull request merged first (#678, #679 and #681 on 2026-09-23), and each was recovered by a local merge in the lane's worktree, a push to the same branch behind a ten-to-twenty-minute pre-push preflight, and a re-armed auto-merge. Nothing tells an author that the union driver stops at the local clone. Wanted: the .gitattributes comment and the conventions that cite the driver say it holds locally only, and the remedy that needs no driver (one file per decision, as iss-2609100507439414 and iss-2608220150157511 propose) is weighed with this cost counted, since it removes the conflict on the forge as well.
- iss-2610110941283143A push to an open pull request started only some of its workflows: after b36b551ac was pushed to #904 (2026-10-10, about 22:50Z) only external-review ran, ci and attribution never triggered, and the pull request sat BLOCKED with eight of its nine required checks missing until it was closed and reopened. Nothing noticed: the checks were not failing, only absent.
- iss-2610150526192138Derived values and append-at-end lists in the release-gate manifest and surface registers conflict on every surface-adding branch. Every branch that adds a command page, agent prompt or surface chapter rewrites the same lines of .abcd/development/release-gate/manifest.json (checkerCount, promptHash, and the one-line prompt context that carries the command and agent rosters) and the manifestHash literal in receipt.example.json, which is the sha256 of the manifest, so two such branches always conflict and the second must merge main and run a fresh preflight before it can queue again.
- itd-2609150819432059A managed repository that is not a plugin gets the same release gate
- spc-2610110931381316append-only-logs-conflict-on-every-merge