Development record
Decisions are one flat file each and the single log is retired
Context
adr-2609151138420062
ruled on 2026-09-15 that the decisions log becomes a folder of minted records,
one file per decision, with an index derived from the folder and never appended
to. It kept .abcd/work/DECISIONS.md as a symlink to that index "for now", so
that existing links, readers and citations went on resolving. It also gave every
repository abcd manages the same shape at adoption.
The conflict that ADR was about recurred on 2026-10-11. Two pull requests each
appended an entry to the tail of .abcd/work/DECISIONS.md, and whichever landed
second conflicted in the merge queue. The merge=union attribute on the path
does not help there: the forge computes mergeability and the queue's merge
without local merge drivers (iss-2609240646538011). Planning the intent that
enacts the shape (itd-2609151138388536), the product thinker settled the open
questions on the same day, and two of the rulings change what the earlier ADR
decided.
A single appended decisions file is not a common standard: the widely used
conventions for decision records are folders of one file per decision (MADR's
docs/decisions/NNNN-title.md, adr-tools' doc/adr/). The old path has no
outside readers to keep resolving, only this repository's own links.
Decision
A decision is a record, minted through the adr-45 seam, stored one per file in
one flat folder. The folder is .abcd/work/decisions/, and each file is
dcn-<id>-<slug>.md, the same flat layout every other record family uses. The
id is the timestamp, so a plain listing sorts by date; there are no year, month
or day subfolders, which would spend path length (iss-2610100626320367) on
information the id already carries. Recording a decision creates a file and
appends to nothing.
.abcd/work/DECISIONS.md is retired, not kept as a symlink or a signpost.
Every link, reader, citation and convention in the repository that names it is
rewritten in the change that lands the shape. A citation of a dated entry points
at that entry's own file. Nothing in the repository names the old path as live
afterwards.
The history is read through a listing, never through a committed index. A command and the site render every decision in date order with its id, derived from the folder. No committed file is written by each new decision, because that file would bring the conflict back.
A repository abcd manages but did not create keeps its existing log. Its
DECISIONS.md is the team's file, and abcd never rewrites it
(adr-2609091248200336's
caution about acting on a person's files unasked). New decisions go to the
folder from the change that adopts the shape onward. The old file stays where it
is, as frozen history, until its owner chooses to convert it by hand with the
same one-off script abcd's own log is converted with (the product thinker's
ruling of 2026-10-14); abcd itself never runs that script there.
The decisions-append gate is retired by the shape, as the earlier ADR ruled.
DA001–DA004 police position and preservation inside one appended file, and a
folder of minted files has no such file. The union merge attribute for
.abcd/work/DECISIONS.md goes in the same change. The changelog's attribute is
a separate conflict class and is not this decision's business.
Alternatives Considered
- Keep
DECISIONS.mdas a symlink to a derived index (the earlier ADR's interim form). Rejected: the index must exist in the tree for the link to resolve, and an index every decision regenerates collides as the log did. A symlink also arrives as plain text on a Windows checkout without symlink support. - Keep
DECISIONS.mdas a short signpost page naming the folder, the listing command and the site page. Considered in the interview, and set aside by the product thinker for full retirement: the signpost keeps a second name for the history alive, and every link to it lands one step short. - Date subfolders,
decisions/<year>/<month>/<day>/<title>.md. Rejected: every other record family is flat with the timestamp in the name, the subfolders add about eleven characters to every path the slug cap was set to keep short, and the listing can group by period without the tree doing it. - Migrate a managed repository's log (automatically, or after asking at install). Rejected for now: abcd does not rewrite a file a team wrote. The frozen-history route leaves nothing of theirs rewritten, at the cost of history sitting in two places.
Consequences
- Two branches that each record a decision add two files and conflict on nothing, with no merge attribute or driver configured.
- The change that lands this rewrites every mention of
.abcd/work/DECISIONS.mdin the repository: 443 mentions in 263 files on 2026-10-11, about fifty of them in code. A citation of a dated entry needs a map from each old entry to its new file. Where several entries share a date and the citation does not say which, the migration cannot choose alone, so it lists those cases for review rather than guessing. - abcd's own existing entries are migrated into the folder with their dates and text verbatim, in date order (the product thinker's ruling of 2026-10-14: the 48 entries written out of date order move to their dates). The migration and the link rewrite are a one-off script run once in this repository (the product thinker's ruling of 2026-10-11), never a verb or code that ships in abcd: they are needed once and only here; abcd never migrates a managed repository, whose owner may run the script by hand.
- A managed repository that adopted abcd before this change has its history in two places: the frozen file, and the folder from then on.
- adr-2609151138420062 is superseded. Its folder-of-minted-records rule and its ban on an appended index stand, restated here; its symlink and its same-shape-at-adoption migration do not.
Frontmatter
| id | adr-2610110925465593 |
|---|---|
| type | adr |
| status | accepted |
| date | 2026-10-11 |
.abcd/development/decisions/adrs/2610110925465593-decisions-are-one-flat-file-each-and-the.md
open on GitHub ↗ · commit history ↗
References3
- adr-45Record ids are timestamp-numeric, capture-stable, and never allocated by looking at the maximumrelated
- adr-2609151138420062The decisions log is a folder of minted records, not one appended filesupersedes
- itd-2609151138388536Every Decision Is Its Own Record, and the Log Stops Conflictingrelated
mentions4
- adr-2609091248200336A tool never creates directories in user-owned project space; the store's location binds now and its verbs bind when the store ships
- 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-2610100626320367A record's file name has no cap tied to the length of its full path, so a record checked out on Windows can pass the 260-character path limit, which the product thinker reports records already do. The longest tracked path is 149 characters inside the repository; the checkout's own location comes on top, and a worktree in the machine store adds its prefix, the 40-character root commit and the worktree's name. Git for Windows will not check such a file out unless core.longpaths is set (itd-2610070544145422 is the Windows install draft).
- spc-2610110931381316append-only-logs-conflict-on-every-merge