@taprootio/trellis 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Taproot IO, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # Trellis
2
+
3
+ A tool-agnostic toolkit for running a file-based backlog the same way in any git
4
+ repo. This repository is Trellis, and it dogfoods its own conventions to manage
5
+ its own backlog.
6
+
7
+ Start with [`AGENTS.md`](AGENTS.md) for the conventions and the milestone ethos,
8
+ then browse the live backlog in [`trellis/README.md`](trellis/README.md).
9
+
10
+ ## Onboard a repo
11
+
12
+ `trellis init` scaffolds the Trellis layout into any repo — the config, a team
13
+ roster stub, the `trellis/` layout, the generated index, the CI check, an AGENTS.md
14
+ backlog section, and the process playbooks — idempotently, without clobbering
15
+ existing files:
16
+
17
+ ```
18
+ npx @taprootio/trellis init <target> --prefix ABC # --dry-run to preview
19
+ ```
20
+
21
+ It does not vendor the generator; the onboarded repo runs Trellis via the
22
+ package (the scaffolded CI calls `npx @taprootio/trellis check`), which ships in TRL0010.
23
+
24
+ ## Import an existing backlog
25
+
26
+ `trellis import` converts a backlog on a foreign schema into Trellis items in an
27
+ already-initialized repo, driven by a declarative mapping — either a built-in
28
+ **profile** (`--profile <name>`; run `--list-profiles`) or your own
29
+ `--mapping <file.json>`. It is **dry-run by default** — preview the plan and the id
30
+ map, then re-run with `--apply`:
31
+
32
+ ```
33
+ npx @taprootio/trellis import <source> --profile yaml-frontmatter --target . # add --apply to write
34
+ ```
35
+
36
+ Ids are assigned fresh-sequentially from the target's next id, colliding source
37
+ ids are deduped, and `depends_on` is rewritten through the id map; the source tree
38
+ is never modified and a real run leaves the backlog `--check`-green. To scaffold
39
+ and import in one step on a fresh repo, use
40
+ `trellis init --import <path> --profile <name>`. The mapping schema, the built-in
41
+ profiles, and the full getting-started guide are in
42
+ [`docs/import.md`](docs/import.md).
43
+
44
+ ## Track task history
45
+
46
+ `trellis history` reconstructs a per-task change log from git — who changed an
47
+ item, when, and why — surviving the active→completed move via `git log --follow`:
48
+
49
+ ```
50
+ npx @taprootio/trellis history <id> # one task; omit <id> for the whole repo
51
+ npx @taprootio/trellis history --write # materialize trellis/history.json for a static viewer
52
+ ```
53
+
54
+ Entries are `{ id, commit, date, author, subject, reason }`, newest-first, where
55
+ `reason` is a `Trellis-Reason:` commit trailer when present, else the commit
56
+ subject. This is a **derived, non-gated report** (SPEC §8.4): volatile and
57
+ non-authoritative (git is the record), so it is **not** part of `backlog:check`,
58
+ and the materialized `history.json` is gitignored — regenerate it at build time.
59
+
60
+ ## Operate over MCP
61
+
62
+ The backlog operations are also exposed as MCP tools, so any MCP-aware client
63
+ (Claude, Cursor, Windsurf, Codex, …) can list, read, create, move, validate,
64
+ regenerate, and read the history of tasks in a repo:
65
+
66
+ ```
67
+ npx @taprootio/trellis mcp --repo <path> # serves over stdio; defaults to cwd
68
+ ```
69
+
70
+ Tools: `list_tasks`, `get_task`, `next_id`, `create_task`, `move_task`,
71
+ `validate`, `regenerate`, `import`, `history` — each reuses the same core as the
72
+ CLI, so results carry the `backlog.json` shape (except `import`, which returns an
73
+ import summary, and `history`, which returns git-derived change entries). Mutating
74
+ tools regenerate and validate before returning, rolling back on failure; `import`
75
+ is dry-run unless `apply:true` (see [`docs/import.md`](docs/import.md)); `history`
76
+ is read-only. The process loops (work-a-task, review) ship separately as MCP
77
+ prompts in TRL0006.
package/SPEC.md ADDED
@@ -0,0 +1,405 @@
1
+ # Trellis Backlog Spec
2
+
3
+ **Version:** 2.3.0 · **Status:** stable
4
+
5
+ Trellis is a tool-agnostic convention for running a software backlog as plain
6
+ files in a git repository. Work items are Markdown files with YAML front-matter;
7
+ a generator validates them and produces a human index and a machine-readable
8
+ `backlog.json`; CI gates the repo so the index can never drift. This document is
9
+ the canonical specification, intended to be vendored into any repository
10
+ unchanged.
11
+
12
+ This spec describes the backlog **format and artifacts** — the data, the
13
+ generated outputs, and the tooling contract. It does not prescribe a *process*
14
+ for working items (planning, review, branching); those layer on top and are
15
+ specified separately.
16
+
17
+ ## 1. Concepts
18
+
19
+ - **Item** — one unit of tracked work, stored as a single Markdown file.
20
+ - **Status** — `active`, `completed`, or `removed`. An item has exactly one,
21
+ reflected by which directory it lives in.
22
+ - **Generator** — a program that validates items and regenerates the derived
23
+ artifacts (`README.md` tables and `backlog.json`).
24
+ - **Config** — `backlog.config.json`, the per-repo vocabulary (id prefix,
25
+ milestones, priorities, effort scale) that lets the same generator serve any
26
+ repo.
27
+
28
+ The per-item files are the single source of truth. The index and `backlog.json`
29
+ are derived and must never be hand-edited.
30
+
31
+ ## 2. Repository layout
32
+
33
+ ```
34
+ <repo>/
35
+ trellis/
36
+ backlog.config.json # per-repo configuration (§7)
37
+ team.json # optional team roster (§7.2)
38
+ active/<ID>.md # open items (§5)
39
+ completed/
40
+ tasks/<ID>.md # finished items, history preserved
41
+ index.md # GENERATED list of completed items (§8.1)
42
+ removed/<ID>.md # abandoned items, archived
43
+ removed/index.md # GENERATED list of removed items (§8.1)
44
+ README.md # GENERATED human index (§8.1)
45
+ backlog.json # GENERATED machine index (§8.2)
46
+ assets/effort/ # optional effort-scale images (§6.3)
47
+ ```
48
+
49
+ The **backlog root** defaults to `trellis/` at the repo root and is configurable
50
+ per repo via the `tasksDir` key (§7) — a repo whose static-site generator
51
+ publishes one directory (e.g. Eleventy over `docs/`) can repoint it elsewhere.
52
+ The **config file** is always `trellis/backlog.config.json`, a fixed location
53
+ independent of `tasksDir`: tooling must be able to find the config before it
54
+ knows where the task tree lives. Above, `tasksDir` is its default (`trellis/`),
55
+ so the config and the task tree share one folder. Everything inside an item
56
+ file's body is free-form Markdown.
57
+
58
+ ## 3. Identifiers
59
+
60
+ An id is a configured **prefix** followed by a zero-padded **number** of a
61
+ configured width — e.g. with prefix `AB` and width 4, `AB0042`.
62
+
63
+ - The id MUST match the item's filename (`AB0042` ⇄ `AB0042.md`).
64
+ - Ids are assigned monotonically from the `nextId` published in the generated
65
+ `backlog.json` (§8.2).
66
+ - An id is permanent and globally unique across all three directories. It MUST
67
+ NOT be reused, even after an item is removed.
68
+
69
+ ## 4. Status lifecycle
70
+
71
+ ```
72
+ create
73
+
74
+
75
+ [ active ] ──── ship ────▶ [ completed ] → completed/tasks/
76
+
77
+ └──────── drop ───────▶ [ removed ] → removed/ (with reason)
78
+ ```
79
+
80
+ - There is **no on-hold status.** Park low-priority work as a low-priority
81
+ `active` item, or remove it with a `removed_reason` that names the trigger to
82
+ revisit.
83
+ - Transitions are a file **move** plus front-matter edits (§5), made in the same
84
+ change that ships or drops the work, followed by a generator run.
85
+ - History is preserved: completing or removing an item moves the file (keeping
86
+ its git history); it is never deleted.
87
+
88
+ ## 5. Item file format
89
+
90
+ An item file is YAML front-matter delimited by `---`, followed by a Markdown
91
+ body.
92
+
93
+ ### 5.1 Front-matter schema
94
+
95
+ | field | active | completed | removed | rule |
96
+ | --- | :-: | :-: | :-: | --- |
97
+ | `id` | ✓ | ✓ | ✓ | matches the filename |
98
+ | `title` | ✓ | ✓ | ✓ | one line |
99
+ | `status` | ✓ | ✓ | ✓ | matches the directory |
100
+ | `summary` | ✓ | ✓ | ✓ | one sentence; feeds the README table |
101
+ | `milestone` | ✓ | ✓ | ✓ | a configured milestone (§7.1); historical on closed items |
102
+ | `priority` | ✓ | ✓ | ✓ | a configured priority (§7); historical on closed items |
103
+ | `effort` | ✓ | ✓ | ✓ | a configured effort value (§6); historical on closed items |
104
+ | `depends_on` | ✓ | ✓ | ✓ | list of existing ids (`[]` if none) |
105
+ | `owner` | ○ | ○ | ○ | a roster handle (§7.2); active → an active member; historical on closed |
106
+ | `collaborators` | ○ | ○ | ○ | list of roster handles; active → active members; historical on closed |
107
+ | `completed_on` | – | ✓ | – | ISO date (`YYYY-MM-DD`) |
108
+ | `removed_on` | – | – | ✓ | ISO date |
109
+ | `removed_reason` | – | – | ✓ | one line; why, and any trigger to revisit |
110
+
111
+ (○ = optional.) `owner` and `collaborators` are optional everywhere and reference
112
+ the team roster (§7.2); no item need carry them, and no backfill is required.
113
+
114
+ On close (completed or removed), the descriptive metadata — `milestone`,
115
+ `summary`, `priority`, `effort`, `depends_on` — is carried over from the active
116
+ item as a historical snapshot, and the close fields are added (`completed_on`,
117
+ or `removed_on`/`removed_reason`). These retained enum values are **historical**:
118
+ tooling records them as-was and does not re-validate them against the current
119
+ config (§8.3), so milestones and scales can evolve without breaking the archive.
120
+ `owner`/`collaborators` are likewise historical on closed items, so a member who
121
+ has since gone `inactive` (or left the roster) does not invalidate the archive —
122
+ though the stored value must still be a syntactically valid handle (§7.2).
123
+
124
+ ### 5.2 Body
125
+
126
+ The body is free-form. Recommended sections are **Scope**, **Notes**, and
127
+ **Risks** for active items, plus a **Completed** section prepended on closeout
128
+ that summarizes what shipped and any follow-ups.
129
+
130
+ ### 5.3 Dependencies
131
+
132
+ `depends_on` may reference `active` or `completed` ids (every referenced id MUST
133
+ exist somewhere), but an item SHOULD NOT be *worked* until its dependencies are
134
+ `completed`.
135
+
136
+ ## 6. Effort
137
+
138
+ `effort` is **relative complexity, not time.** Canonical effort values are a
139
+ Fibonacci-like set (default `1, 2, 3, 5, 8, 13, 21`), configurable per repo. The
140
+ non-linear gaps are intentional: reaching the top of the scale is the signal to
141
+ split an item.
142
+
143
+ The number is always the stored, canonical value. Teams MAY skin it with a
144
+ custom **effort scale** for display.
145
+
146
+ ### 6.1 Effort-scale config
147
+
148
+ ```json
149
+ "effort": {
150
+ "values": [1, 2, 3, 5, 8, 13, 21],
151
+ "scale": "fish",
152
+ "scales": {
153
+ "fish": {
154
+ "1": { "label": "Minnow", "emoji": "🐟" },
155
+ "2": { "label": "Goldfish", "emoji": "🐠" },
156
+ "3": { "label": "Trout", "emoji": "🐡" },
157
+ "5": { "label": "Tuna", "image": "assets/effort/tuna.svg" },
158
+ "8": { "label": "Swordfish" },
159
+ "13": { "label": "Shark", "emoji": "🦈" },
160
+ "21": { "label": "Whale", "emoji": "🐋" }
161
+ }
162
+ }
163
+ }
164
+ ```
165
+
166
+ - `values` (required) — the canonical effort set.
167
+ - `scales` (optional) — named display scales. Each maps **every** value in
168
+ `values` (string keys) to an entry; a value missing from the active scale is an
169
+ error.
170
+ - `scale` (optional) — the active scale name. Absent or `"fibonacci"` selects the
171
+ identity scale (label = the number, no emoji/image).
172
+ - Each entry: `label` (required, unique within the scale), `emoji` (optional),
173
+ and `image` (optional, a path relative to the backlog root — `tasksDir`, default
174
+ `trellis/` — so it resolves the same way the artifacts do). The Tuna/Swordfish
175
+ entries above show the image-only and label-only cases.
176
+
177
+ ### 6.2 Authoring and resolution
178
+
179
+ - In front-matter, `effort` MAY be the canonical number **or** a case-insensitive
180
+ `label` from the active scale (e.g. `effort: Goldfish`). The generator resolves
181
+ a label to its number; an unresolvable or ambiguous value is an error.
182
+ - `backlog.json` ALWAYS carries the resolved canonical number plus the resolved
183
+ `effortLabel`, and `effortEmoji`/`effortImage` when present — so consumers
184
+ render without reading the config.
185
+
186
+ ### 6.3 Rendering
187
+
188
+ - The generated README shows `label · N` (label and number together) when a
189
+ non-identity scale is active, and just `N` otherwise — keeping the number
190
+ legible for velocity and rollup math.
191
+ - `label` doubles as the accessible text/alt for any `image`. SVG or emoji are
192
+ preferred; images are optional and live under `<tasksDir>/assets/effort/`
193
+ (default `trellis/assets/effort/`).
194
+ - The array form `effort: [1, 2, 3, 5, 8, 13, 21]` is shorthand for
195
+ `{ "values": [ … ], "scale": "fibonacci" }` and remains valid.
196
+
197
+ ## 7. Configuration (`backlog.config.json`)
198
+
199
+ ```json
200
+ {
201
+ "specVersion": "2.0",
202
+ "idPrefix": "AB",
203
+ "idWidth": 4,
204
+ "milestones": ["Alpha", "Beta", "v1", "Future"],
205
+ "priorities": ["High", "Medium", "Low"],
206
+ "effort": [1, 2, 3, 5, 8, 13, 21]
207
+ }
208
+ ```
209
+
210
+ | key | meaning |
211
+ | --- | --- |
212
+ | `specVersion` | the Trellis spec version this repo targets (§9) |
213
+ | `idPrefix` | id prefix (e.g. `AB`) |
214
+ | `idWidth` | zero-padded digit count |
215
+ | `milestones` | ordered milestone names (§7.1) |
216
+ | `priorities` | ordered priority names, highest first |
217
+ | `effort` | canonical values, or the effort-scale object (§6.1) |
218
+ | `tasksDir` | optional backlog-root path, repo-relative; defaults to `trellis/` |
219
+
220
+ `tasksDir` locates the task tree and the generated artifacts; omit it to accept
221
+ the `trellis/` default. The config file itself stays at `trellis/backlog.config.json`
222
+ regardless (§2) — its location is **not** governed by `tasksDir`, so the spec
223
+ example above omits the key.
224
+
225
+ **Configurable** per repo: everything in the table above, including the backlog
226
+ root via `tasksDir`. **Fixed** by the spec: the `trellis/backlog.config.json`
227
+ config location, the in-root layout (`active/`, `completed/tasks/`, `removed/`,
228
+ and the generated artifacts under `tasksDir`), the status lifecycle, the
229
+ front-matter schema, the generated-artifact contracts, and the meaning of each
230
+ field.
231
+
232
+ ### 7.1 Milestones are a maturity axis
233
+
234
+ A milestone names the **release gate** an item must land in — and nothing else.
235
+ It is a single, ordered axis (e.g. `Alpha → Beta → v1 → Future`). A milestone is
236
+ **not** a feature area (that is the title), **not** a priority (its own field),
237
+ and **not** an on-hold state (§4). Milestone *names* are configured; the
238
+ single-axis, ordered semantics are fixed.
239
+
240
+ ### 7.2 Team roster (`team.json`)
241
+
242
+ The optional **team roster** records who can own work. It is a separate authored
243
+ file, `team.json`, at the fixed config home (`trellis/team.json`, next to
244
+ `backlog.config.json` and independent of `tasksDir`) — kept apart from the config so
245
+ the core vocabulary stays stable while the roster (richer, faster-changing) evolves
246
+ on its own.
247
+
248
+ ```json
249
+ {
250
+ "members": [
251
+ { "handle": "ada", "name": "Ada Lovelace", "email": "ada@example.com", "status": "active" },
252
+ { "handle": "alan", "name": "Alan Turing", "status": "inactive" }
253
+ ]
254
+ }
255
+ ```
256
+
257
+ - `members` (required) — the list of people. The object wrapper leaves room for
258
+ additive top-level keys in future minor versions.
259
+ - `handle` (required) — the stable key referenced by `owner`/`collaborators` in
260
+ front-matter (§5.1). Constrained to `[A-Za-z0-9._-]` (so it survives the inline
261
+ serialization of `collaborators`) and unique, case-insensitively.
262
+ - `name` (required) and `email` (optional) are **display only**.
263
+ - `status` — `active` or `inactive` (default `active`). Only **active** members may
264
+ own or collaborate on **active** items; closed items keep historical assignees
265
+ (§8.3), so a member can go `inactive` or leave without breaking the archive.
266
+
267
+ The roster is **optional**: an absent `team.json` is an empty roster, so a repo that
268
+ never assigns owners is unaffected. A present-but-malformed roster (bad JSON,
269
+ duplicate handle, bad `handle`/`status`, unknown member key) is a validation error
270
+ (§8.3). `handle` is the only identity contract — names/emails are not coupled to any
271
+ external identity provider; cross-repo identity is left to future work.
272
+
273
+ ## 8. Generated artifacts
274
+
275
+ These are derived from the item files and config on every generator run. They
276
+ MUST be deterministic — identical inputs produce byte-identical output, with no
277
+ timestamps or other volatile fields — so that `--check` (§8.3) is stable in CI.
278
+ Reports derived from a source *outside* the item files that is inherently volatile
279
+ (e.g. git history) are a separate, **non-gated** class — see §8.4.
280
+
281
+ ### 8.1 `README.md`
282
+
283
+ A human index. Item tables are emitted between the markers
284
+ `<!-- BEGIN GENERATED:MILESTONES -->` and `<!-- END GENERATED:MILESTONES -->`;
285
+ content outside the markers is author-owned and preserved. Active items are
286
+ grouped by milestone (config order) and sorted by priority then id. The next id
287
+ is not published here — `backlog.json` carries it (§8.2). Text between the markers
288
+ MUST NOT be hand-edited.
289
+
290
+ The completed and removed indexes (`completed/index.md`, `removed/index.md`) are
291
+ generated the same way, each between its own `BEGIN/END GENERATED` markers, as a
292
+ table that includes the item's **summary** — completed: id, title, summary, date;
293
+ removed: id, title, summary, date, reason. Closing or removing an item is a file
294
+ move plus a generator run; index rows are never hand-added.
295
+
296
+ ### 8.2 `backlog.json`
297
+
298
+ The machine contract consumers build on:
299
+
300
+ ```json
301
+ {
302
+ "prefix": "AB",
303
+ "milestones": ["Alpha", "Beta", "v1", "Future"],
304
+ "nextId": "AB0016",
305
+ "counts": { "active": 14, "completed": 1, "removed": 0 },
306
+ "tasks": [
307
+ {
308
+ "id": "AB0042", "title": "…", "status": "active",
309
+ "milestone": "Beta", "priority": "High",
310
+ "effort": 5, "effortLabel": "Tuna", "effortImage": "assets/effort/tuna.svg",
311
+ "depends_on": ["AB0007"], "owner": "ada", "collaborators": ["alan"], "summary": "…"
312
+ }
313
+ ]
314
+ }
315
+ ```
316
+
317
+ Every entry carries the descriptive metadata (`milestone`, `summary`,
318
+ `priority`, `effort`, `depends_on`) plus `owner` (a roster handle or `null`) and
319
+ `collaborators` (handles, `[]` if none) — historical on closed entries. Completed
320
+ entries add `completed_on`; removed entries add `removed_on` and `removed_reason`.
321
+ Effort label/emoji/image fields appear when a scale is active.
322
+
323
+ ### 8.3 Tooling contract
324
+
325
+ A conforming generator MUST:
326
+
327
+ 1. **Validate** every item against the schema (§5), config (§7), and roster (§7.2)
328
+ with actionable messages: id/filename match, required fields, enum membership,
329
+ effort resolution, unique ids, `depends_on` referential integrity, and
330
+ owner/collaborator roster membership. Enum membership (milestone/priority/effort)
331
+ and roster **membership** (owner/collaborators must be **active** members) are
332
+ enforced for **active** items only; on completed/removed items these values are
333
+ historical and MUST NOT fail validation if they are no longer in the current
334
+ config or roster (a mismatch MAY warn). `owner`/`collaborators` MUST nonetheless
335
+ be syntactically valid handles (§7.2) on **every** item — the closed-item
336
+ exemption is from membership, not from being a handle. A malformed `team.json` is
337
+ a validation error regardless.
338
+ 2. **Regenerate** `README.md`, the completed/removed indexes (each between its
339
+ markers), and `backlog.json` deterministically.
340
+ 3. Support a **`--check`** mode that validates and verifies the artifacts are
341
+ current **without writing**, exiting non-zero on any error or drift. This is
342
+ the CI gate.
343
+ 4. **Warn** when `specVersion` is absent or its major version differs from the
344
+ spec version the generator implements (§9).
345
+
346
+ ### 8.4 Derived, non-gated reports
347
+
348
+ Not every useful derivation belongs to the gated, deterministic set above. A
349
+ **derived report** is computed from a source *outside* the item files — typically
350
+ volatile (commit timestamps, author names) — so it cannot be byte-identical across
351
+ runs and is therefore handled separately from §8.1–§8.3. Such reports:
352
+
353
+ - are **regenerable** and **not authoritative** — the upstream source is (for a
354
+ git-derived report, git itself);
355
+ - are produced **on demand or at build time** (e.g. a CI step before a static-site
356
+ deploy), **not** by the generator on every edit;
357
+ - SHOULD NOT be committed; a repo that does commit one MUST mark it generated (e.g.
358
+ a top-level `"generated": true`) and keep it out of `--check`.
359
+
360
+ A conforming generator's **`--check` MUST NOT depend on git history** or any other
361
+ volatile source. Derived reports are **optional** and do not affect conformance
362
+ (§11).
363
+
364
+ **`history.json`** is the reference derived report: a per-repo change log keyed by
365
+ task id, materialized for a viewer with no git runtime at serve time. It lives under
366
+ the backlog root (`<tasksDir>/history.json`) when materialized. Each id maps to a
367
+ list of entries `{ id, commit, date, author, subject, reason }`, newest-first,
368
+ reconstructed with `git log --follow` over the task file so history survives the
369
+ active→completed move. `reason` is the value of a `Trellis-Reason:` commit trailer
370
+ when present, otherwise the commit subject. An item imported into a repo carries
371
+ history from its import commit forward; a single-commit or empty history is valid,
372
+ not an error.
373
+
374
+ ## 9. Versioning and compatibility
375
+
376
+ This spec uses SemVer. A repo declares the version it targets via `specVersion`
377
+ (`major.minor`). Within a major version, changes are additive and backward
378
+ compatible; a major bump may change required fields or artifact shape. Tooling
379
+ warns on a major mismatch between `specVersion` and the implemented spec.
380
+
381
+ ## 10. CI and branch protection
382
+
383
+ Conformant repositories MUST gate the default branch so the index cannot drift:
384
+
385
+ - The default branch is **protected**; changes land via pull/merge request.
386
+ - The generator's **`--check` is a required status check** that must pass before
387
+ merge.
388
+
389
+ This is forge-agnostic — GitHub branch protection, GitLab merge-request
390
+ pipelines, Bitbucket, or Azure DevOps all satisfy it. Setup specifics are left to
391
+ the consumer's onboarding tooling, which SHOULD provide a forge-appropriate setup
392
+ recipe.
393
+
394
+ Where a forge identifies the required check by name (e.g. a GitHub Actions job
395
+ name), keeping that name stable and pinned — and updating the protection rule in
396
+ lockstep if it ever changes — keeps a workflow or job rename from silently
397
+ dropping the gate. This is operational guidance for keeping the required check
398
+ effective, not an additional conformance requirement.
399
+
400
+ ## 11. Conformance
401
+
402
+ A repository is **Trellis-conformant** at `specVersion` *X* if its items follow
403
+ §3–§6, its `backlog.config.json` follows §7, its generated artifacts follow §8,
404
+ and its default branch is gated per §10. A **tool** is conformant if it
405
+ implements the generator contract (§8.3) for that version.