@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.
@@ -0,0 +1,189 @@
1
+ // Trellis MCP prompts + resources (zero-dependency, transport-agnostic).
2
+ //
3
+ // TRL0006: the work-task / code-review / pr-draft loops as MCP **prompts**, and
4
+ // the spec / conventions / config / playbooks / PR-template as MCP **resources**.
5
+ // Like the tool ops in src/mcp.mjs, every builder takes an explicit repoRoot and
6
+ // reads the repo's OWN files at request time — stateless and repoRoot-keyed, so
7
+ // one server instance serves any repo and the prompts never drift from the
8
+ // Markdown playbooks they mirror. The SDK + transport wiring live in
9
+ // scripts/trellis-mcp.mjs; keeping these functions dependency-free means the whole
10
+ // surface is unit-testable with `node --test` and no transport.
11
+ //
12
+ // Reading conventions from the repo (not baking a stack into the prompt text) is
13
+ // the TRL0006 Risk made concrete: a prompt for repo A carries A's playbooks and
14
+ // vocabulary; for repo B, B's.
15
+
16
+ import { readFileSync, existsSync } from "node:fs";
17
+ import { join } from "node:path";
18
+ import { loadConfig } from "./backlog.mjs";
19
+ import { TrellisError } from "./mcp.mjs";
20
+
21
+ // --------------------------------------------------------------- resources
22
+ // The catalog. `rel` is the repo-relative file each `trellis://` uri serves; the
23
+ // content is always read live from repoRoot, so a repo's own copy wins.
24
+ export const RESOURCES = [
25
+ {
26
+ uri: "trellis://spec", name: "spec", title: "Backlog spec",
27
+ description: "The canonical Backlog Spec (SPEC.md).",
28
+ rel: "SPEC.md", mimeType: "text/markdown",
29
+ },
30
+ {
31
+ uri: "trellis://conventions", name: "conventions", title: "Repo conventions (AGENTS.md)",
32
+ description: "The repo's AGENTS.md — the canonical conventions every loop grounds in first.",
33
+ rel: "AGENTS.md", mimeType: "text/markdown",
34
+ },
35
+ {
36
+ uri: "trellis://config", name: "config", title: "Backlog config",
37
+ description: "backlog.config.json — the per-repo vocabulary (id prefix, milestones, priorities, effort).",
38
+ rel: "trellis/backlog.config.json", mimeType: "application/json",
39
+ },
40
+ {
41
+ uri: "trellis://playbook/conventions", name: "playbook-conventions", title: "Conventions contract",
42
+ description: "The per-repo conventions contract — the named seam points the universal loop reads from a repo's AGENTS.md.",
43
+ rel: "trellis/playbooks/conventions.md", mimeType: "text/markdown",
44
+ },
45
+ {
46
+ uri: "trellis://playbook/work-task", name: "playbook-work-task", title: "Playbook: work a task",
47
+ description: "The work-a-task loop, as Markdown.",
48
+ rel: "trellis/playbooks/work-task.md", mimeType: "text/markdown",
49
+ },
50
+ {
51
+ uri: "trellis://playbook/code-review", name: "playbook-code-review", title: "Playbook: code review",
52
+ description: "The code-review loop and its JSON output standard, as Markdown.",
53
+ rel: "trellis/playbooks/code-review.md", mimeType: "text/markdown",
54
+ },
55
+ {
56
+ uri: "trellis://playbook/pr-draft", name: "playbook-pr-draft", title: "Playbook: draft a PR",
57
+ description: "The PR title/description draft loop, as Markdown.",
58
+ rel: "trellis/playbooks/pr-draft.md", mimeType: "text/markdown",
59
+ },
60
+ {
61
+ uri: "trellis://template/pull-request", name: "template-pull-request", title: "PR template",
62
+ description: "The repository pull-request template the pr-draft loop fills.",
63
+ rel: ".github/pull_request_template.md", mimeType: "text/markdown",
64
+ },
65
+ ];
66
+
67
+ function resourceByUri(uri) {
68
+ const r = RESOURCES.find((x) => x.uri === uri);
69
+ if (!r) throw new TrellisError(`unknown resource: ${uri}`, "not_found");
70
+ return r;
71
+ }
72
+
73
+ // list_resources — the catalog, each flagged `available` iff its file exists in
74
+ // this repo. (SPEC.md, for one, is absent in onboarded repos until packaging
75
+ // bundles a canonical copy — TRL0010.)
76
+ export function listResources(repoRoot) {
77
+ return RESOURCES.map(({ uri, name, title, description, mimeType, rel }) => ({
78
+ uri, name, title, description, mimeType,
79
+ available: existsSync(join(repoRoot, rel)),
80
+ }));
81
+ }
82
+
83
+ // read_resource — the file behind a trellis:// uri. Throws `not_found` when the
84
+ // uri is unknown OR the file is absent in this repo, rather than faking content.
85
+ export function readResource(repoRoot, uri) {
86
+ const r = resourceByUri(uri);
87
+ const abs = join(repoRoot, r.rel);
88
+ if (!existsSync(abs)) {
89
+ throw new TrellisError(`resource ${uri} is unavailable in this repo (no ${r.rel})`, "not_found");
90
+ }
91
+ return { uri: r.uri, mimeType: r.mimeType, text: readFileSync(abs, "utf8") };
92
+ }
93
+
94
+ // ----------------------------------------------------------------- prompts
95
+ // Each prompt embeds the repo's own playbook body (`playbook`) verbatim, behind a
96
+ // repo-parameterized preamble. `arguments` is the MCP prompt-argument list.
97
+ export const PROMPTS = [
98
+ {
99
+ name: "work-task", title: "Work a task",
100
+ description: "Take a Trellis task from active/ to ready-for-review by following the work-a-task loop.",
101
+ playbook: "trellis/playbooks/work-task.md",
102
+ arguments: [{ name: "id", description: "task id to work, using this repo's configured id prefix and width", required: true }],
103
+ },
104
+ {
105
+ name: "code-review", title: "Code review",
106
+ description: "Review the current branch's work against the repo's conventions and emit the canonical JSON findings.",
107
+ playbook: "trellis/playbooks/code-review.md",
108
+ arguments: [],
109
+ },
110
+ {
111
+ name: "pr-draft", title: "Draft a PR",
112
+ description: "Draft a copy-ready PR title and description that conform to the repo's PR template.",
113
+ playbook: "trellis/playbooks/pr-draft.md",
114
+ arguments: [],
115
+ },
116
+ ];
117
+
118
+ function promptByName(name) {
119
+ const p = PROMPTS.find((x) => x.name === name);
120
+ if (!p) throw new TrellisError(`unknown prompt: ${name}`, "not_found");
121
+ return p;
122
+ }
123
+
124
+ // Ground every prompt in this repo's conventions, with the id vocabulary read
125
+ // live from backlog.config.json (never baked into the prompt text). On a config
126
+ // error we still point at AGENTS.md rather than emitting a wrong vocabulary.
127
+ function conventionsPreamble(cfg, cfgErrors) {
128
+ const vocab = cfgErrors.length
129
+ ? "the repo's `backlog.config.json` governs its vocabulary"
130
+ : `ids are \`${cfg.idPrefix}\` + ${cfg.idWidth} digits; milestones are ${cfg.milestones.join(" → ")}; priorities are ${cfg.priorities.join(", ")}`;
131
+ return "Ground in this repo's own conventions first: read its `AGENTS.md` (the "
132
+ + "`trellis://conventions` resource) and the `SPEC.md` / docs it points at. The "
133
+ + `repo's conventions govern — ${vocab}.`;
134
+ }
135
+
136
+ // Validate the work-task `id` argument against this repo's id format (when the
137
+ // config is loadable), so a malformed id fails fast with a clear message.
138
+ function requireId(raw, cfg, cfgErrors) {
139
+ if (typeof raw !== "string" || !raw.trim()) throw new TrellisError("`id` is required", "invalid_request");
140
+ const id = raw.trim();
141
+ if (/[\r\n]/.test(id)) throw new TrellisError("`id` must be a single line", "invalid_request");
142
+ if (!cfgErrors.length) {
143
+ const re = new RegExp(`^${cfg.idPrefix}\\d{${cfg.idWidth}}$`);
144
+ if (!re.test(id)) throw new TrellisError(`invalid task id: ${id} (expected ${cfg.idPrefix} + ${cfg.idWidth} digits)`, "invalid_request");
145
+ }
146
+ return id;
147
+ }
148
+
149
+ // The per-prompt instruction that bridges the preamble to the embedded playbook.
150
+ function instruction(name, args, cfg, cfgErrors) {
151
+ if (name === "work-task") {
152
+ const id = requireId(args.id, cfg, cfgErrors);
153
+ return `Then work task **${id}** end-to-end by following the loop below exactly — do not improvise the order, and honor its two pauses (refinement sign-off, then plan confirmation) before any branch or file edit.`;
154
+ }
155
+ if (name === "code-review") {
156
+ return "Then review the current branch's work by following the process below, and finish with the canonical JSON findings array exactly as the output standard specifies.";
157
+ }
158
+ return "Then draft the PR title and description by following the loop below, emitting the single copy-ready Markdown block it specifies.";
159
+ }
160
+
161
+ // build_prompt — assemble one prompt's messages for this repo: a conventions
162
+ // preamble + a per-prompt instruction + the repo's playbook body, embedded
163
+ // verbatim so the prompt is self-contained on clients that don't resolve resource
164
+ // links. Throws `not_found` if the backing playbook is missing in this repo.
165
+ export function buildPrompt(repoRoot, name, args = {}) {
166
+ const p = promptByName(name);
167
+ const { cfg, errors: cfgErrors } = loadConfig(repoRoot);
168
+
169
+ const abs = join(repoRoot, p.playbook);
170
+ if (!existsSync(abs)) {
171
+ throw new TrellisError(`prompt ${name} needs ${p.playbook}, which is missing in this repo`, "not_found");
172
+ }
173
+ const playbook = readFileSync(abs, "utf8").replace(/\n*$/, "\n");
174
+
175
+ const text = [
176
+ conventionsPreamble(cfg, cfgErrors),
177
+ "",
178
+ instruction(name, args, cfg, cfgErrors),
179
+ "",
180
+ "--- playbook ---",
181
+ "",
182
+ playbook,
183
+ ].join("\n");
184
+
185
+ return {
186
+ description: p.description,
187
+ messages: [{ role: "user", content: { type: "text", text } }],
188
+ };
189
+ }
@@ -0,0 +1,31 @@
1
+ <!--
2
+ Title format: <ID>: imperative summary (<= 72 chars), where <ID> is this repo's
3
+ backlog id (its configured prefix + number, per backlog.config.json).
4
+ For a branch spanning several items, lead with the primary id and name the rest
5
+ in the body. Generate a conforming title + description with the shortcut at
6
+ trellis/playbooks/pr-draft.md.
7
+ Attribution: follow the repo's attribution policy (AGENTS.md). Default: none — no
8
+ Co-Authored-By trailers or "Generated with" footers.
9
+ -->
10
+
11
+ ## Summary
12
+
13
+ <!-- 1–3 sentences: what this PR does and why. -->
14
+
15
+ ## Task
16
+
17
+ <!-- Link the backlog item(s) by id. Note any that are closed out here. -->
18
+
19
+ ## Changes
20
+
21
+ <!-- The notable changes, grouped if helpful. Describe what shipped, not a file list. -->
22
+
23
+ ## Verification
24
+
25
+ - [ ] the backlog `check` passes (the repo's check command — see AGENTS.md)
26
+ - [ ] Touched docs / spec updated
27
+ <!-- Add the commands you ran and their result. -->
28
+
29
+ ## Follow-ups
30
+
31
+ <!-- New backlog items spun off, or "none". -->
@@ -0,0 +1,116 @@
1
+ # Enabling branch protection
2
+
3
+ The Trellis spec (§10, "CI and branch protection") makes a gated default branch
4
+ part of being Trellis-conformant: the default branch is **protected**, changes
5
+ land via pull/merge request, and the generator's **`--check` is a required status
6
+ check**. The CI workflow (`.github/workflows/backlog.yml`) provides the check; this
7
+ recipe turns it into an enforced gate. Branch protection is a host-repo setting, not
8
+ committed code, so it is enabled once per repo by an admin — it does not travel in
9
+ a commit the way the workflow does.
10
+
11
+ ## The required-check context: `backlog`
12
+
13
+ The check you require is identified by a **stable context name**. In the Trellis
14
+ workflow that name is **`backlog`** — the job:
15
+
16
+ ```yaml
17
+ # .github/workflows/backlog.yml
18
+ name: Backlog Hygiene # ← the WORKFLOW's display name (not the check context)
19
+ jobs:
20
+ backlog:
21
+ name: backlog # ← the JOB name == the required status check context
22
+ ```
23
+
24
+ Require the check named **`backlog`**, not `Backlog Hygiene`. The job carries an
25
+ explicit `name: backlog` so the context is pinned: renaming the workflow's display
26
+ name cannot move it, and a rename of the job is a visible edit to this line rather
27
+ than a silent gate-disabling drift (the risk §10's stable-context guidance
28
+ guards against). If you change the job name, update the required check to match.
29
+
30
+ ## GitHub
31
+
32
+ ### With `gh` (scriptable)
33
+
34
+ From a clone of the repo, with the [`gh`](https://cli.github.com/) CLI
35
+ authenticated and admin on the repo. `gh` substitutes `{owner}`/`{repo}` from the
36
+ current repository; replace `main` if your default branch differs:
37
+
38
+ ```bash
39
+ gh api --method PUT repos/{owner}/{repo}/branches/main/protection --input - <<'JSON'
40
+ {
41
+ "required_status_checks": { "strict": true, "contexts": ["backlog"] },
42
+ "required_pull_request_reviews": { "required_approving_review_count": 1 },
43
+ "enforce_admins": true,
44
+ "restrictions": null
45
+ }
46
+ JSON
47
+ ```
48
+
49
+ - `required_status_checks.contexts: ["backlog"]` — the gate. `strict: true` also
50
+ requires the branch to be up to date before merging; drop it if you don't want
51
+ that.
52
+ - `required_pull_request_reviews` — requires changes to land via PR. Set
53
+ `required_approving_review_count: 0` to require a PR without a mandatory
54
+ approval, or raise it for more reviewers.
55
+ - `enforce_admins: true` — applies the rule to admins too; relax if you need an
56
+ admin escape hatch.
57
+ - `restrictions: null` — no push allow-list. All four top-level keys must be
58
+ present (a key may be `null`); this is the minimal conformant payload.
59
+
60
+ ### In the UI
61
+
62
+ **Settings → Branches → Add branch ruleset** (or **Add rule** under the classic
63
+ "Branch protection rules"), targeting the default branch:
64
+
65
+ 1. **Require a pull request before merging** (optionally require approvals).
66
+ 2. **Require status checks to pass before merging** → search for and select
67
+ **`backlog`**. Optionally enable "Require branches to be up to date."
68
+ 3. Save.
69
+
70
+ > The `backlog` check only appears in the picker after the workflow has run at
71
+ > least once on the repo. Push the scaffold (or open a first PR) so the check
72
+ > registers, then add it to the rule.
73
+
74
+ ## Other forges
75
+
76
+ The same two requirements — a protected default branch and a required check —
77
+ map onto every major forge. The "check" is whichever CI job runs `trellis check`
78
+ (named `backlog` here); on forges without GitHub-style named contexts, the gate is
79
+ "the pipeline/build must succeed."
80
+
81
+ ### GitLab
82
+
83
+ - **Protected branch** — *Settings → Repository → Protected branches*: protect the
84
+ default branch, set **Allowed to push and merge → No one** (force changes
85
+ through merge requests) and **Allowed to merge** to the appropriate role.
86
+ - **Required check** — *Settings → Merge requests*: enable **Pipelines must
87
+ succeed** so an MR can't merge unless its pipeline (the job running
88
+ `trellis check`) passes; optionally require approvals. Scriptable via
89
+ `PUT /projects/:id` with `only_allow_merge_if_pipeline_succeeds: true` and
90
+ `POST /projects/:id/protected_branches`.
91
+
92
+ ### Bitbucket
93
+
94
+ - **Protected branch** — *Repository settings → Branch restrictions*: add a
95
+ restriction on the default branch — **Prevent changes without a pull request**
96
+ (and prevent direct pushes).
97
+ - **Required check** — in the same restriction, add the merge check **require
98
+ passing builds** (minimum 1). The Pipelines step that runs `trellis check`
99
+ reports its build status via the Build Status API; require a minimum number of
100
+ approvals if desired.
101
+
102
+ ### Azure DevOps
103
+
104
+ - **Protected branch** — *Repos → Branches → ⋯ on the default branch → Branch
105
+ policies*. Adding any policy blocks direct pushes; add **Require a minimum
106
+ number of reviewers** to force PRs.
107
+ - **Required check** — add a **Build validation** policy that references the
108
+ pipeline running `trellis check` (set it Required). If the pipeline reports via
109
+ the Status API instead, use a **Status checks** policy referencing that context.
110
+
111
+ ## Verify the gate
112
+
113
+ Open a throwaway PR that intentionally drifts the index — e.g. edit a generated
114
+ file between its `BEGIN/END GENERATED` markers, or add an item without
115
+ regenerating — and confirm the `backlog` check fails and the merge button is
116
+ blocked until it passes. Close the PR without merging.
@@ -0,0 +1,64 @@
1
+ # Playbook: code review
2
+
3
+ A ready-to-use shortcut for reviewing work in a Trellis repo. Point any AI
4
+ assistant at this file, or invoke the equivalent `code-review` MCP prompt (served
5
+ from this file by the Trellis MCP server). The
6
+ *what to check* defers to the repo's own conventions (AGENTS.md / SPEC.md), so
7
+ this stays tech-agnostic — the **process** and the **output format** are the
8
+ standard.
9
+
10
+ ## Process
11
+
12
+ 1. **Ground in the repo's conventions first.** Read AGENTS.md (canonical) and any
13
+ SPEC.md / docs the change touches. You cannot judge work against rules you
14
+ have not read. Seam points named in code font (`check`, `gates`) read their
15
+ value from AGENTS.md — see [`conventions.md`](conventions.md).
16
+ 2. **Identify the task.** Determine the backlog id this work belongs to — the
17
+ repo's configured id (branch name, commits, the added/moved file under
18
+ the backlog root, default `trellis/`). Review against its stated intent, not
19
+ just the diff in isolation.
20
+ 3. **Verify closeout** when the work claims to finish a task: the item moved to
21
+ `completed/tasks/` with `status: completed` + `completed_on`, the generated
22
+ artifacts are current (the repo's `check` command passes — e.g.
23
+ `npm run backlog:check`; see AGENTS.md), and links hold. Flag any miss as a
24
+ `blocker`.
25
+ 4. **Review thoroughly, twice.** First pass for the obvious; second pass for what
26
+ you rationalized away — data and control flow across seams, behavior under
27
+ failure / edge / hostile input, and what *should* have changed but didn't
28
+ (missing tests, a stale doc, an un-updated snapshot).
29
+ 5. **Run the gates you can** — the repo's `check` command and its `gates`
30
+ (tests/lint; e.g. `npm run backlog:check` + `node --test`; see AGENTS.md).
31
+ A failing gate is a `blocker`.
32
+
33
+ ## Output format (the standard)
34
+
35
+ Put any discussion first, then end with **exactly one** fenced `json` block
36
+ containing a single array and nothing after it, so it pastes into PR/issue
37
+ tooling in one click. The array is always last, even when empty.
38
+
39
+ Each finding is an object:
40
+
41
+ - `file` — repo-relative path to the file that needs to change.
42
+ - `line` — line number where the change applies.
43
+ - `severity` — one of `"blocker"`, `"warning"`, `"nit"`.
44
+ - `suggestion` — concise fix, including the exact change to make.
45
+
46
+ ```json
47
+ [
48
+ {
49
+ "file": "src/backlog.mjs",
50
+ "line": 153,
51
+ "severity": "blocker",
52
+ "suggestion": "Validate completed/removed items too — the loop only iterates active, so a completed task with status: active passes --check."
53
+ },
54
+ {
55
+ "file": "src/backlog.mjs",
56
+ "line": 55,
57
+ "severity": "warning",
58
+ "suggestion": "Reject non-numeric config effort values; `[\"Tiny\"]` currently passes."
59
+ }
60
+ ]
61
+ ```
62
+
63
+ Use `[]` when there are no findings. `severity` lets the author triage every
64
+ `blocker` before any `nit` without re-reading the list.
@@ -0,0 +1,56 @@
1
+ # The per-repo conventions contract
2
+
3
+ The work-a-task, code-review, and pr-draft playbooks are **universal** — they
4
+ describe the *shape* of the loop (refine → plan → branch → work → review → close)
5
+ and the *output standards*, and nothing about any one repo's stack. Everything
6
+ that is repo-specific — which command regenerates the backlog, how branches are
7
+ named, which gates must stay green — is read at runtime from the repo's own
8
+ `AGENTS.md`.
9
+
10
+ This file defines that seam: the **named points** a playbook refers to, and the
11
+ rule for where their values live. The *definition* here is universal and travels
12
+ unchanged into every Trellis repo; the *values* are per-repo and live in each
13
+ repo's `AGENTS.md` "Loop contract" block (scaffolded by `trellis init`). A
14
+ playbook step names a seam point as its instruction ("regenerate the backlog");
15
+ any concrete command it shows is a tagged example of *one* repo's value, never
16
+ the command a different repo should run.
17
+
18
+ ## Seam points
19
+
20
+ | seam point | what the loop needs | this repo's value lives in |
21
+ | --- | --- | --- |
22
+ | `regenerate` | the command that revalidates items and rewrites the generated index + `backlog.json` after any item add/move/edit | `AGENTS.md` |
23
+ | `check` | the read-only gate the loop keeps green locally and CI enforces (validate + verify-no-drift, non-zero on failure) | `AGENTS.md` |
24
+ | `branch-naming` | the pattern for a task branch, e.g. `<initials>/<id-lowercase>/<slug>` | `AGENTS.md` |
25
+ | `gates` | any other gates a change must keep green before review (tests, lint, build) | `AGENTS.md` |
26
+ | `attribution` | the commit/PR attribution policy (Trellis convention: none) | `AGENTS.md` |
27
+
28
+ Two more inputs the loop reads, defined elsewhere and referenced here for
29
+ completeness:
30
+
31
+ - **id vocabulary** (id prefix/width, milestones, priorities, effort) — from
32
+ `trellis/backlog.config.json` (SPEC.md §7), not restated per playbook.
33
+ - **tasks layout** (`<tasksDir>/{active,completed/tasks,removed}/`, default
34
+ `trellis/`, plus the generated artifacts) — the in-root structure and the
35
+ `trellis/backlog.config.json` config home are fixed by the spec (SPEC.md §2);
36
+ only the root path is configurable via `tasksDir`.
37
+
38
+ ## How a repo declares its values
39
+
40
+ `trellis init` writes a "Loop contract" block into the onboarded repo's
41
+ `AGENTS.md` with that repo's values (e.g. `npx @taprootio/trellis generate` /
42
+ `npx @taprootio/trellis check`, a branch pattern, the attribution policy). A repo edits those values to
43
+ match its own tooling; the playbooks need no change to follow them, because they
44
+ only ever name the seam point.
45
+
46
+ Trellis itself is the reference instance — its `AGENTS.md` Loop contract block
47
+ holds the concrete values the playbooks' `e.g.` examples are drawn from.
48
+
49
+ ## How a playbook uses a seam point
50
+
51
+ > Rewrite the task file, then **regenerate the backlog** (the repo's `regenerate`
52
+ > command — e.g. `npm run backlog:readme`; see AGENTS.md) and commit it.
53
+
54
+ The instruction is "regenerate the backlog"; `npm run backlog:readme` is Trellis's
55
+ value, tagged as such. In an onboarded repo the same step resolves to that repo's
56
+ `regenerate` value with no edit to the playbook.
@@ -0,0 +1,39 @@
1
+ # Playbook: draft a PR title and description
2
+
3
+ A ready-to-use shortcut for generating a **copy-ready** PR title and description
4
+ that conform to [`.github/pull_request_template.md`](../../.github/pull_request_template.md).
5
+ Point any AI assistant at this file, or invoke the equivalent `pr-draft` MCP
6
+ prompt (served from this file by the Trellis MCP server) so any tool can run it.
7
+
8
+ ## What the assistant should gather first
9
+
10
+ - Commits on the branch: `git log --oneline <base>..HEAD` (base is usually `main`).
11
+ - The change surface: `git diff --stat <base>...HEAD`.
12
+ - The PR template: `.github/pull_request_template.md`.
13
+ - The referenced task file(s) under the backlog root (default `trellis/`).
14
+
15
+ ## Prompt
16
+
17
+ > Generate a pull-request **title** and **description** for the current branch,
18
+ > based only on its actual commits and diff (do not invent changes).
19
+ >
20
+ > - **Title:** `<ID>: <imperative summary>`, ≤ 72 characters, where `<ID>` is the
21
+ > repo's backlog id (its configured prefix + number). Lead with the primary
22
+ > backlog id; if the branch spans several items, name the primary and mention
23
+ > the others in the body.
24
+ > - **Description:** fill every section of `.github/pull_request_template.md`
25
+ > (Summary, Task, Changes, Verification, Follow-ups). Tick a checklist item
26
+ > only when the evidence supports it; otherwise leave it unchecked and say why.
27
+ > - **Attribution:** follow the repo's `attribution` policy (AGENTS.md); the
28
+ > default is none — no `Co-Authored-By:` trailers or AI "Generated with …"
29
+ > footers in the title or description.
30
+ > - **Output:** a single fenced `markdown` block whose first line is the title as
31
+ > `# <ID>: …`, followed by the completed template body — so it copies in one
32
+ > click.
33
+
34
+ ## Output shape
35
+
36
+ One copy-ready Markdown block:
37
+
38
+ - first line: `# <ID>: <summary>` (the title)
39
+ - then the filled template (Summary, Task, Changes, Verification, Follow-ups)
@@ -0,0 +1,76 @@
1
+ # Playbook: work a task
2
+
3
+ A ready-to-use shortcut for taking a Trellis task from the backlog root's
4
+ `active/` (the configured `tasksDir`, default `trellis/`) all the way to "ready
5
+ for your review." Invoke it with a backlog id — the repo's id prefix + number,
6
+ per `backlog.config.json`.
7
+ Point any AI assistant at this file, or invoke the equivalent `work-task` MCP
8
+ prompt (served from this file by the Trellis MCP server — see AGENTS.md §Roadmap).
9
+
10
+ The loop is fixed — do not improvise the order. There are exactly **two**
11
+ intentional pauses (steps 4 and 5); everything after the confirmed plan runs
12
+ without further check-ins. No file edits happen until you are on the task branch,
13
+ so `main` stays clean.
14
+
15
+ ## The loop
16
+
17
+ 1. **Ground in the repo's conventions.** Read AGENTS.md (canonical) and the
18
+ SPEC / docs relevant to what the task will touch. The *what* and *how* of this
19
+ repo live there; this playbook is only the orchestration. Where a step names a
20
+ seam point in code font (`regenerate`, `check`, `branch-naming`, `gates`,
21
+ `attribution`), it reads that repo's value from AGENTS.md — see
22
+ [`conventions.md`](conventions.md) for the contract.
23
+ 2. **Read the task.** Open `<ID>.md` under the backlog root's `active/` (the
24
+ configured `tasksDir`, default `trellis/`) in full — front-matter (milestone,
25
+ priority, effort, depends_on) and body (Scope, Notes, Risks). If
26
+ it isn't in `active/`, stop and say where it actually is. Confirm every
27
+ `depends_on` id is completed; surface any that aren't.
28
+ 3. **Verify it against the codebase.** Check the task hasn't drifted: do its
29
+ assumptions still hold, do referenced files/APIs still exist, is it still the
30
+ right thing to build and still workable? Note anything stale.
31
+ 4. **Propose refinements, then PAUSE.** Surface ambiguities, gaps, drift, and
32
+ concrete suggestions that would make the task clearer or more workable — then
33
+ **wait for the user's answers.**
34
+ 5. **Plan, with checkpoints, then PAUSE.** Using the agreed refinements, lay out
35
+ the implementation as ordered steps and call out the natural **commit
36
+ checkpoints**. **Confirm the plan with the user** before touching anything.
37
+ 6. **Start clean, then branch.** Confirm the working tree is clean and `main` is
38
+ current (`git fetch`, fast-forward `main`); if not, stop and surface it. Create
39
+ the task branch using the repo's `branch-naming` convention —
40
+ `<initials>/<id-lowercase>/<slug>` (see AGENTS.md for the repo's pattern and an
41
+ example). Branch from local `main`, not `origin/main`, so the upstream isn't
42
+ mis-set. Everything from here lands on the branch, never on `main`.
43
+ 7. **Rewrite the task on the branch.** Fold the user's refinements into
44
+ the task file under the backlog root's `active/` (Scope / Notes / Risks),
45
+ **regenerate the backlog**
46
+ (the repo's `regenerate` command — e.g. `npm run backlog:readme`; see
47
+ AGENTS.md), and commit it as the first checkpoint. This refined file is the
48
+ source of truth for the rest of the loop.
49
+ 8. **Work the task end-to-end.** Implement against the refined Scope, committing
50
+ at the checkpoints from step 5 and keeping the repo's `gates` green (its `check`
51
+ command plus tests/lint — e.g. `npm run backlog:check` + `node --test`;
52
+ see AGENTS.md). When the work lands, close the task out: move it to
53
+ `completed/tasks/`, set `status: completed` + `completed_on`, regenerate
54
+ (SPEC §4–§5).
55
+ 9. **Request a review.** Run `trellis/playbooks/code-review.md` over the branch and
56
+ capture its canonical JSON findings. Make it an **independent** pass — ideally a
57
+ separate agent or session, or at minimum re-ground from scratch — since author
58
+ self-review tends to rubber-stamp.
59
+ 10. **Fix the findings.** Resolve every `blocker` and `warning` (and `nit`s unless
60
+ waived); re-run the review until the array is `[]` or only waived items
61
+ remain. Keep the gates green.
62
+ 11. **Hand off for review.** Tell the user it's ready: the branch name, what
63
+ shipped, the review outcome, and the push + PR commands (PR body via
64
+ `trellis/playbooks/pr-draft.md`). Do **not** push or merge — that's the user's.
65
+
66
+ ## Notes
67
+
68
+ - The two pauses are the contract: refinement sign-off (step 4) and plan
69
+ confirmation (step 5). Both happen before any branch or file edit. If you hit a
70
+ genuine blocker mid-loop, stop and surface it — otherwise keep going.
71
+ - Because branching (step 6) precedes every edit (steps 7–8), the clean-tree
72
+ requirement always holds and `main` is never dirtied.
73
+ - Branch protection means work never lands on `main` directly; it always goes
74
+ through a PR with the required backlog check.
75
+ - Commits and PRs follow the repo's `attribution` policy (AGENTS.md); the default
76
+ is **none** — never add `Co-Authored-By:` trailers or "Generated with …" footers.