Skip to content

Commit 99afa65

Browse files
brabojclaude
andauthored
docs(decisions): rewrite ADR-049 in the imbrain form (#1936)
* docs(decisions): rewrite ADR-049 in the imbrain form ADR-049 now has a table-led Context, a one-sentence Decision, a Rationale, a draw.io diagram, a rule table with what enforces each rule, a worked example of merging the split, and assessed Compatibility and Security sections. base-docs adopts the two imbrain rules this needs: a record may be rewritten in place until the first merge that implements it, and a record may show a rendered diagram with its source beside it instead of inline ASCII. No implementation of ADR-049 has merged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(decisions): merge everything before the split, not only the path move -s ours marks the split and all its ancestors as merged, so a change between the path move and the split was skipped for good. Merge the split's parent normally first, and land the split as one commit. Reproduced: the old steps kept a pre-split policy fix out of the fork; the new ones keep it and the fork's stacks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 8c43b6a commit 99afa65

24 files changed

Lines changed: 383 additions & 155 deletions

‎CHANGELOG.md‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ alters no template carries no entry.
3232
set, and a check gates the layout.
3333
- A merged decision record MAY be restructured to a new section layout by
3434
restatement alone; a section it predates says "Not assessed".
35+
- A decision record MAY be rewritten in place until the first merge that
36+
implements it, and a record MAY show a rendered diagram (draw.io, Mermaid)
37+
with its source committed beside it, instead of inline ASCII.
3538

3639
## [2.90.0] - 2026-09-12
3740

‎CLAUDE.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,8 @@ base/ ──┬── frontend/ ──┐
298298
and Security, per `base-docs` Decision logs
299299
- One coherent architectural decision may cover related choices across
300300
several issues or PRs; do not require a record per implementation concern.
301+
- Until the first implementation of an ADR merges, the record MAY be
302+
rewritten or split in place; from then on it is history
301303
- What is immutable in a merged ADR is the decision — the claims
302304
made in Context, Decision, Alternatives considered and
303305
Consequences. Preserve those claims as history; routine refinements update

‎README.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,8 @@ effective; declining a new rule needs no ADR or ticket. See the
123123
<!-- generated:readme-model-limits -->
124124
| Stack category | Stacks | Largest chain | Prompt | Min context |
125125
|----------------|--------|---------------|--------|-------------|
126-
| abstract | 2 | `stack-python-service` — 556K chars | ~159K tokens | 200K |
127-
| backend | 8 | `stack-django` — 570K chars | ~163K tokens | 200K |
126+
| abstract | 2 | `stack-python-service` — 557K chars | ~159K tokens | 200K |
127+
| backend | 8 | `stack-django` — 571K chars | ~163K tokens | 200K |
128128
| embedded | 1 | `stack-c-embedded` — 363K chars | ~104K tokens | 128K |
129129
| hypermedia | 1 | `stack-htmx` — 382K chars | ~109K tokens | 128K |
130130
| library | 3 | `stack-python-lib` — 486K chars | ~139K tokens | 200K |

‎docs/decisions/049-forks-carry-stacks-and-process.md‎

Lines changed: 123 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -11,105 +11,147 @@ superseded_by: []
1111

1212
## Context
1313

14-
This repository holds two kinds of content. One is the composition engine
15-
and the engineering policy every project shares: the manifest schema, the
16-
ID system, the resolver, the checks, and the quality, testing, security and
17-
data rules. The other is one organisation's choices: seventeen stacks,
18-
three platforms, a release and session process, the interview, examples and
19-
generated chains.
20-
21-
The previous scope decision kept a curated stack set here and sent other
22-
organisations to their own repositories. That left the second kind of
23-
content in the shared core. A review of the core found half of it was an
24-
operating manual rather than engineering policy. A team running a
25-
different process inherits rules that contradict its practice, and a
26-
contradiction between the context file and the practice is a failure the
27-
efficacy benchmark tests for.
28-
29-
Every stack also ships in every release. The generated chains have grown
30-
until the e2e canary hits the generator's output ceiling, and a change to
31-
one organisation's process is a release for everyone.
14+
This repository holds two kinds of content, and only one of them is shared
15+
by every team.
16+
17+
| Content | Examples | Who needs it |
18+
| ------- | -------- | ------------ |
19+
| Composition engine | Manifest schema, IDs, EXTEND and OVERRIDE, resolver, checks | Every project and every fork |
20+
| Shared engineering policy | Quality, testing, security, data, infrastructure, backend, frontend | Every project |
21+
| One organisation's choices | 17 stacks, 3 platforms, release and session process, interview, examples, generated chains | Projects that work the way Imbra works |
22+
23+
The forces:
24+
25+
- Contradiction: a review of the core found half of it is an operating
26+
manual. A team running a different process inherits rules that
27+
contradict its practice, a failure the efficacy benchmark tests for.
28+
- Release coupling: every stack ships in every release, so a change to
29+
one organisation's process is a release for everyone.
30+
- Size: the generated chains grew until the e2e canary hits the
31+
generator's output ceiling.
32+
- Precedent: the previous scope decision already sent other
33+
organisations to their own repositories, but kept a curated stack set
34+
here.
3235

3336
## Decision
3437

35-
Upstream holds the composition engine and the invariant policy, and forks
36-
carry the stacks, platforms, process layer, interview, examples and
37-
generated chains, syncing by merging upstream.
38+
Upstream holds the composition engine and the policy every team shares;
39+
forks carry the stacks, platforms, process layer, interview, examples and
40+
generated chains, and stay current by merging upstream.
3841

3942
## Rationale
4043

41-
- One organisation's process in the shared core contradicts the practice of
42-
a team running a different one; in a fork it binds only that organisation.
43-
- With the stacks in forks, upstream no longer ships every stack in every
44-
release, and one organisation's process change is no longer a release for
45-
everyone.
46-
- A fork that merges upstream receives upstream's fixes, which a copy that
47-
drifts does not.
44+
- One organisation's process binds only the projects that chose it, so no
45+
team inherits rules that contradict its practice.
46+
- A new stack or a process change is a release of one fork, not of every
47+
consumer.
48+
- A git fork receives upstream's fixes by merging; a copy drifts, and
49+
nothing carries a fix into it.
50+
- Generation stays an agent reading Markdown, so a project needs no new
51+
tool.
4852

4953
## Design
5054

51-
1. **Upstream holds the engine and the invariant policy** — this
52-
repository keeps the manifest schema, IDs, EXTEND and OVERRIDE, the
53-
resolver, the checks and the engineering policy that holds for any
54-
team. Which files fall on which side is a separate boundary decision.
55-
2. **Forks hold the rest** — stacks, platforms, the process layer, the
56-
interview, examples and generated chains live in forks. Imbra's fork,
57-
`imbra-ai-templates`, is the first; any organisation MAY fork the same
58-
way.
59-
3. **A fork is a git fork that merges upstream** — a fork syncs with
60-
`git merge upstream/main`, never by copying files.
61-
4. **A fork appends and never edits upstream keys** — a fork MUST NOT edit
62-
the manifest keys upstream owns (`core:`, `base:`, `backend:`,
63-
`frontend:`). It appends under `stacks:`, `platform:` and keys of its
64-
own.
65-
5. **Forks own their ID namespaces** — the prefixes `stack-*`,
66-
`platform-*` and `<org>-*` are reserved for forks, and upstream adds no
67-
ID under them.
68-
6. **Upstream commits no derived content** — no `generated/`, no
69-
`examples/` and no stack table. A fork regenerates its own.
70-
7. **Some files are expected to conflict** — `README.md`, `CLAUDE.md` and
71-
`CHANGELOG.md` are fork-owned, and a fork resolves them in its own
72-
favour on every merge.
73-
8. **The split is merged with the `ours` strategy** — a fork that exists
74-
before the split merges the split commit with `git merge -s ours`. An
75-
ordinary merge deletes every file the fork left unchanged and upstream
76-
removed.
77-
9. **Generation stays agent-driven** — the split adds no compiler. A
78-
project still hands its agent the interview and a resolved chain.
79-
10. **The split takes effect in the v3.0 release** — until then this
80-
repository carries both kinds of content as it does today.
55+
![Upstream holds the engine and shared policy; three forks, Imbra's among them, merge it with git merge upstream/main; each fork hands a project's agent the interview and a resolved chain](../diagrams/fork-model.png)
56+
57+
| # | Rule | In practice |
58+
| - | ---- | ----------- |
59+
| 1 | Upstream holds the engine and shared policy | The manifest schema, IDs, EXTEND and OVERRIDE, the resolver, the checks, and the policy that holds for any team. Which file falls on which side is the boundary record's call (#1919) |
60+
| 2 | Forks hold the rest | Stacks, platforms, the process layer, the interview, examples and generated chains. `imbra-ai-templates` is the first fork; any organisation MAY fork the same way |
61+
| 3 | A fork merges upstream | A fork syncs with `git merge upstream/main`, never by copying files |
62+
| 4 | A fork appends, never edits upstream keys | A fork MUST NOT edit `core:`, `base:`, `backend:` or `frontend:` in the manifest; it appends under `stacks:`, `platform:` and keys of its own |
63+
| 5 | Forks own their ID prefixes | `stack-*`, `platform-*` and `<org>-*` are reserved for forks; upstream adds no ID under them |
64+
| 6 | Upstream commits no derived content | No `generated/`, no `examples/`, no stack table; a fork regenerates its own |
65+
| 7 | Fork-owned files conflict by design | `README.md`, `CLAUDE.md` and `CHANGELOG.md` belong to the fork, which resolves them in its own favour on every merge |
66+
| 8 | The split merges with `ours` | Upstream lands the split as one commit. A fork that predates it merges the commit's parent normally, then the split commit with `git merge -s ours`; an ordinary merge deletes every file the fork left unchanged and upstream removed |
67+
| 9 | Generation stays agent-driven | No compiler: a project hands its agent the interview and a resolved chain |
68+
| 10 | Takes effect in v3.0 | Until then this repository carries both kinds of content, as it does today |
69+
70+
| Rule | Promise or mechanism | Enforced by |
71+
| ---- | -------------------- | ----------- |
72+
| 1, 2 | Promise to projects and forks | The boundary record (#1919) |
73+
| 3, 9 | Mechanism | - |
74+
| 4 | Promise to forks | The manifest ownership check (#1923) |
75+
| 5 | Promise to forks | The duplicate-ID smoke check, which fires on the merge |
76+
| 6, 7, 8 | Mechanism | The fork-sync CI job (#1924) and the migration guide (#1292) |
77+
| 10 | Promise to projects | The compatibility record (#1921) |
78+
79+
### Merging the split
80+
81+
A fork made before v3.0 takes upstream's changes in three steps. Upstream
82+
lands the split as one commit. Everything before it merges normally first:
83+
`-s ours` marks a commit and all its ancestors as merged, so a change not
84+
merged before it, such as the path move or a policy fix, is skipped for
85+
good.
86+
87+
```bash
88+
git remote add upstream https://github.com/solid-ai-dev/solid-ai-templates.git
89+
git fetch upstream
90+
91+
# Everything before the split, the path move included, merges normally.
92+
git merge <split-commit>^
93+
94+
# Upstream drops the stacks; the fork keeps them.
95+
git merge -s ours <split-commit>
96+
97+
# Everything after the split merges normally.
98+
git merge upstream/main
99+
```
81100

82101
## Alternatives considered
83102

84-
- **Keep the curated set here** — rejected; it keeps one organisation's
85-
process in the shared core, and every stack ships in every release.
86-
- **Stacks as a package or submodule** — rejected; it adds an install step
87-
for a user who only attaches a file to an agent, and a submodule pins
88-
rather than merges.
89-
- **A separate stacks repository that copies the core** — rejected; a copy
90-
drifts, and nothing carries an upstream fix into it.
91-
- **A compiler that assembles context files** — rejected; the composition
92-
stays readable Markdown an agent resolves, and a build step is a new
93-
dependency for every consumer.
103+
| Alternative | Why not |
104+
| ----------- | ------- |
105+
| Keep the curated set here | One organisation's process stays in the shared core, and every stack ships in every release. |
106+
| Stacks as a package or submodule | An install step for a user who only attaches a file to an agent, and a submodule pins rather than merges. |
107+
| A stacks repository that copies the core | A copy drifts, and nothing carries an upstream fix into it. |
108+
| A compiler that assembles context files | A build step becomes a dependency for every consumer, and the composition stops being Markdown an agent reads. |
94109

95110
## Consequences
96111

97-
- A boundary record assigns each template to upstream or to forks
98-
- A compatibility record states what upstream freezes from `3.0.0`, so a
99-
fork can rely on the IDs it extends
100-
- Smoke gains a check that fails when a fork edits an upstream manifest key
101-
- Upstream CI gains a job that simulates a fork, lands an upstream change
102-
and asserts the merge is clean
103-
- A fixture stack and platform give the per-stack checks an input once the
104-
stacks leave
105-
- The README is rewritten for the fork model, and the issue on capping
106-
stack scope and defining the extension model closes on this record
107-
- The migration guide documents the `ours` merge and the ownership rule
112+
| Consequence | Effect |
113+
| ----------- | ------ |
114+
| Boundary record | Assigns each template to upstream or to forks (#1919) |
115+
| Compatibility record | States what upstream freezes from `3.0.0`, so a fork can rely on the IDs it extends (#1921) |
116+
| Ownership check | Smoke fails when a fork edits an upstream manifest key (#1923) |
117+
| Fork-sync CI job | Simulates a fork, lands an upstream change, and asserts the merge is clean (#1924) |
118+
| Fixture stack and platform | The per-stack checks keep an input once the stacks leave (#1925) |
119+
| README rewrite | The fork model replaces the stacks table; the extension-model issue closed on this record (#305) |
120+
| Migration guide | Documents the merge steps above and the ownership rule (#1292) |
108121

109122
## Compatibility
110123

111-
Not assessed: this record predates the section.
124+
- Until v3.0 nothing changes. `v2.90.0` stays the pin a consumer holds
125+
until it migrates.
126+
- From v3.0, a project that resolves a stack, a platform, the interview or
127+
a generated chain from upstream finds them gone. It re-pins to a fork that
128+
carries them; Imbra's fork keeps today's IDs, so the project's EXTEND and
129+
OVERRIDE directives still resolve.
130+
- A project that builds only on upstream's shared policy keeps working
131+
against upstream, on the IDs the compatibility record freezes.
132+
- A fork made before the split loses every stack it left unchanged if it
133+
merges the split normally; the merge steps above prevent that.
134+
- The removal ships as a major release, under `Breaking changes` in the
135+
CHANGELOG. How long 2.x receives fixes is the compatibility record's
136+
call (#1921).
112137

113138
## Security
114139

115-
Not assessed: this record predates the section.
140+
- Trust moves to the fork's maintainer. A context file instructs an agent
141+
that can run commands, and a shipped check is code the agent runs, so a
142+
project trusts whoever maintains the fork it pins. A project SHOULD pin a
143+
fork by tag or commit, not by branch.
144+
- The ownership rule guards upstream's manifest keys, not what they mean
145+
in a chain. A fork can OVERRIDE an upstream security section from one of
146+
its own stacks, and no upstream check sees it.
147+
- `SECURITY.md` routes reports about upstream only. Each fork needs its
148+
own disclosure route for the content it adds.
149+
- No credentials or secrets are involved.
150+
151+
## Related
152+
153+
- ADR-009 - the curated stack set this record supersedes.
154+
- solid-ai-dev/solid-ai-templates#1919 - the boundary record.
155+
- solid-ai-dev/solid-ai-templates#1921 - the compatibility record.
156+
- solid-ai-dev/solid-ai-templates#305 - the extension model this record
157+
answers.

0 commit comments

Comments
 (0)