Config-first adoption: declared families replace defaults, and init scaffolds from a config - #76
Conversation
One merge rule produced both limits the examples had to document: the
shipped ADR scheme could not be removed, and a declared scheme's omitted
`output` inherited `docs/decisions` from the default entry — silently
relocating the index on the documented adoption path. A limit that
well-documented is a bug with good paperwork.
The split is by what a table is (ADR-047). Settings tables — paths, code,
lint, site — keep per-key merge: their keys are Luria's vocabulary, and
partial override is the point. Family tables — schemes, fragments,
journals, remotes — are replaced whole when declared: their entries are
names the project chose, so declaring the family is authorship, and "you
get the ones you wrote" is the only reading under which a family can
shrink. Undeclared families keep the shipped defaults.
The two pinned limit tests fired when the rule changed — what pins are for
— and are flipped into their inversions: rfcs-and-specs now has no ADR
scheme at all, collocated collocates by omission with its workaround line
retired, many-journals shows an undeclared family surviving. Hardened for
the newly-legal shape: adr_paths() answers {} without an ADR scheme, and
the index-stub fallback names the scheme it renders instead of
"Architecture decision records" — the sibling of the document-stub bug,
found by the same examples.
Upgrading: a config that declared part of a family while relying on the
rest from the defaults now owns what it declared; the changelog fragment
carries the note. Records scaffolded by `luria init` always declared their
families in full and are unaffected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MAGvXUrSmX597dCXGjgtvW
The scaffold was a fixed-tree copy, which ignored the one file that says
what a record is: a project whose luria.toml declared RFCs and no decisions
still got record/decisions.d scaffolded beside it, and there was no way to
hand init the config you actually wanted (ADR-048).
init now resolves a config — `--config PATH`, else the project's own
luria.toml, else the shipped template's — and writes the shape it declares:
a directory, template and view stub per scheme, templates per journal and
fragment directory, and a docs index whose view list is generated from the
config so it names exactly what this record renders. ADR and a
document-rendered DP keep the shipped rich templates; any other scheme
gets a neutral one built on the `{PREFIX}-NNN` placeholder `luria new`
already substitutes (ADR-036), because Luria has no doctrine about what an
RFC should say. Never-overwrite stands, with one hard refusal: `--config`
against a project that already has a luria.toml errors instead of
skipping, since scaffolding one config's shape while another governs the
record builds directories the project's machinery doesn't know about.
config.load gains text= so the plan and --dry-run work before anything is
on disk.
Automating the adoption guide's three commands as a test caught two
template defects on the first run — a bare LU-ADR-048 and a bare DP-1 that
scheme-driven detection (ADR-046) can now see — so a fresh scaffold would
have started red. Both linked; the test stays as the guard for the class.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MAGvXUrSmX597dCXGjgtvW
The configuration reference's "What is not configurable" list loses its two ADR entries and says why; the adopting guide gains the three init modes and drops the output-workaround warning; the examples README turns its limits section into a demonstration of the replacement rule. ADR-046 gains an unresolved-ok for its illustrative DP-17 — the detector it describes now sees the code it quotes. The devlog carries the round: the two-rule split stated as vocabulary ownership, the pins firing as designed, the same-branch changelog-fragment collision after a squash merge, and the template ceasing to be a copyable tree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MAGvXUrSmX597dCXGjgtvW
|
|
||
| **Same-branch, second contribution: the changelog fragment collides.** The | ||
| branch restarted from main after the squash merge, so `luria new changelog` | ||
| would have reopened the *merged* fragment (named for the branch, which |
There was a problem hiding this comment.
named for the branch
why are we naming changelogs fragments from the branch? this is a solved problem. use the timestamped filenaming scheme we're using for the devlog. it basically ensures fragments never collide. we should just derive changelog's similar to how we do with devlogs. also, I'm wondering if we should merge PRs into an intermediate branch without firing documentation builds on the PR, then we can build documentation on the intermediate branch. That way, every PR doesn't come up as changing 20 files, which is confusing in reviews.
There was a problem hiding this comment.
Both points addressed in aa23377.
Fragment naming — done as you describe. luria new now stamps an unnamed fragment with its filing moment (20260812-021035.md), the identity the devlog already uses. Flat rather than yyyy/mm/dd/ nested, because the collector and the lint glob a fragment directory one level deep. --name stays as the explicit override and still reopens rather than duplicates. One behavioral note: branch naming gave "second ask on this branch returns the same fragment" — timestamps can't, so two asks make two fragments, and they collect into the same dated batch anyway, which preserves ADR-002's reading intent via the collector instead of filename discipline. ADR-036 carries it as a version bump with history (the scaffold choice stands; only the naming detail moved).
The 20-files-in-review problem — I took the cheap swing first. .gitattributes now marks the generated views linguist-generated=true, which GitHub collapses in the Files tab by default (with a "load diff" click-through). A contribution's diff then reads as its sources, and nothing about the committed-views architecture changes — one file, reversible.
On the intermediate-branch idea itself: worth weighing if the collapse isn't relief enough, but it has real teeth. (1) The record a reviewer approves is no longer the record that lands — views regenerate after review, and the rendered index/summaries are sometimes where wording problems show. (2) luria lint's staleness guarantee currently assumes sources and views travel together in one tree; splitting them across branches means the lint passes on a branch whose views are supposed to be stale, so the check needs a mode. (3) Merge conflicts in generated files move from the contributor (who can regenerate) to the bot (which has to). If you want to go that way regardless — the collapse genuinely doesn't help git diff locally, only GitHub review — it's an ADR-sized decision and I'm happy to draft it with these trade-offs as the alternatives section.
Generated by Claude Code
… review Review on #76 called the fragment collision a solved problem being re-solved badly, and it was: the devlog never collides because an entry's identity is when it was filed, while a changelog fragment's identity was the git branch — an address that stopped being stable the moment squash-merge-and-restart became the workflow. `luria new` now stamps unnamed fragments (20260812-021035.md), flat rather than yyyy/mm/dd/ nested because the collector and the lint glob a fragment directory one level deep. `--name` stays as the explicit override and still reopens rather than duplicates; two stamped fragments from one contribution collect into the same dated batch, so ADR-002's reading intent is preserved by the collector rather than filename discipline. ADR-036 carries the change as a version bump with history — the scaffold choice stands, only the naming detail moved. For the twenty-changed-files review noise: .gitattributes marks the generated views linguist-generated, so GitHub collapses them in PR review and a contribution's diff reads as its sources. The views stay committed — ADR-032's reasons stand — this changes only how review renders them. If an intermediate-branch build is still wanted after this, it deserves its own decision; the devlog names the trade to weigh. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MAGvXUrSmX597dCXGjgtvW
|
agree that it's an ADR-sized decision: I'm going to continue brainstorming outloud here. the issue is often conflicts over incremental codes taking the same the number across different branches. I'm wondering if maybe we should have a mechanism for issuing temporary codes, so within a branch the new docs can still be referenced by code, and then we can use a separate mechanism to concretize temporary codes into human-friendly incremental codes. so e.g. user wants to create a new document, so they run |
|
This is the same move as the fragment fix, one level up: identity at filing time shouldn't require global coordination, and the sequence counter should live at the one place merges serialize. Filing-time numbering is a distributed claim on a global counter; that's the bug, and temp codes are the right shape of fix. A few things fall out of thinking it through: The concretizer already has a name in the record. ADR-040 (Proposed) is "renaming schemes and moving documents without losing the record's memory" — and temp→real is exactly rename-plus-rewrite-every-reference, which The sharp edge is citations the rewriter can't reach. By the time Temp codes don't actually need the intermediate branch. Concretization has to run wherever merges serialize — but a merge queue, or the merge-to-main job running it per-PR, serializes fine. The intermediate branch buys batching (and your docs-build point); the collision fix is orthogonal to it. Worth deciding them separately so the smaller mechanism isn't coupled to the bigger architecture question. Mechanical touchpoints, for scoping: One genuine improvement hiding in it: the template says numbering carries information — the order decisions were made. Filing-time numbers actually lie about that under concurrency (branch A files first, merges second). Merge-time assignment makes the sequence mean what it already claims to mean. When it pays: a single-human flow rarely collides, and today's collision is at least loud (add/add conflict on the same filename). N concurrent agent branches is where it becomes real — which I gather is exactly the workflow here. Happy to draft this as a Proposed ADR — temp codes + alias-preserving concretization as the decision, with the intermediate branch, filing-time numbering, and timestamp-codes-forever (the devlog answer, which trades away small citable numbers) as the alternatives section. Say the word and it lands as its own PR. Generated by Claude Code |
|
draft this as a proposed ADR, file the proposal with this branch, merge this change, start a new PR on a new branch implementing the proposal. |
…zation point Drafted from the review discussion on #76. Filing-time numbering is a distributed claim on a global counter — two branches both mint ADR-123 — and the fix follows the fragment fix one level up: identity at filing must not require global coordination, so allocation moves to where merges serialize. `allocate = "merge"` issues temporary codes (ADR-x47fje, disjoint from the numeric pattern by construction) that are first-class on their branch; `luria concretize` assigns real numbers in merge order, rewrites the tree, and records each temp code as a permanent `aka:` alias so nothing cited in a PR thread or commit message ever goes dead. `concretize --check` guards the trunk under ADR-035's admission rule. Proposed, not implemented — the implementation follows in its own PR, and this record adopting merge allocation for itself is a separate decision from the mechanism existing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MAGvXUrSmX597dCXGjgtvW
Follow-up to #75, resolving the two limits it could only document — and answering the question that reframed them: can
luria initnot take a config file as input? It can now, and the two changes turned out to be one thought:luria.tomlbecomes the actual interface to what a record is.ADR-047 — a declared family replaces the default; settings still merge
One merge rule produced both documented limits: the shipped ADR scheme couldn't be removed, and a declared scheme's omitted
outputinheriteddocs/decisions— silently relocating the index on the documented adoption path. A limit that well-documented is a bug with good paperwork.The fix is noticing the config has two kinds of table:
paths,code,lint,site) — keys named by Luria; partial override is the point → merge per key.schemes,fragments,journals,remotes) — entries named by the project; declaring one is authorship → replaced whole when declared, shipped default when not.Rejected: a removal sentinel (
ADR = falseis three states for a two-state question) and replace-on-any-write ([luria.site.theme.light]would clear the site table).The two limit tests pinned in #75 fired the moment the rule changed — the only failures in the suite — and are flipped into their inversions.
examples/rfcs-and-specsnow has no ADR scheme at all;examples/collocatedcollocates by omission, workaround line retired;examples/many-journalsshows an undeclared family surviving.Upgrading: a config that declared part of a family while relying on the rest from the defaults now owns what it declared. Template-scaffolded records always declared families in full and are unaffected; the changelog says so.
ADR-048 — the scaffold is planned from configuration
luria initwas a fixed-tree copy that ignored the one file that says what a record is — a project whoseluria.tomldeclared RFCs and no decisions still gotrecord/decisions.dscaffolded beside it.Now:
--config PATH→ the project's ownluria.toml→ the template's, and the plan is a pure function of the winner. A directory, template and view stub per scheme; templates per journal and fragment dir; a docs index whose view list is generated from the config. ADR/DP keep the shipped rich templates (the doctrine is content Luria actually has); any other scheme gets a neutral template on the{PREFIX}-NNNplaceholderluria newalready substitutes — Luria has no opinion about what an RFC should say, and the template is honest about it.The proven loop, now a CI-run test:
One hard refusal:
--configagainst a project that already has aluria.tomlexits 1 — scaffolding one config's shape while another governs the record builds directories the project's machinery doesn't know about.The guard caught two defects on its first run
Automating the adoption guide's three commands as a test immediately failed on a bare
LU-ADR-048I had just written into the template, and on a bareDP-1sitting in the principles stub since before scheme-driven detection (#75) could see it. A fresh scaffold would have started red — the worst first lesson a reference-linting tool could teach. Both linked; the test stays as the pin for the class. Also fixed the sibling of #75's heading bug: an index scheme with no stub was titled# Architecture decision recordswhatever its prefix.Verification
adr_paths()luria lintclean,luria index --checkcurrent,luria sitestagesProposed; devlog carries the round, including the same-branch changelog-fragment collision after a squash merge (hand-named this round's fragment — worth a naming convention if branch-restarts become a habit)Generated by Claude Code