@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 +21 -0
- package/README.md +77 -0
- package/SPEC.md +405 -0
- package/docs/import.md +273 -0
- package/package.json +59 -0
- package/profiles/taproot-ai-backlog.json +28 -0
- package/profiles/yaml-frontmatter.json +23 -0
- package/scripts/backlog-readme.mjs +84 -0
- package/scripts/pr-title-lint.mjs +69 -0
- package/scripts/trellis-history.mjs +127 -0
- package/scripts/trellis-import.mjs +141 -0
- package/scripts/trellis-init.mjs +287 -0
- package/scripts/trellis-mcp.mjs +222 -0
- package/scripts/trellis.mjs +62 -0
- package/src/backlog.mjs +667 -0
- package/src/cli.mjs +33 -0
- package/src/history.mjs +204 -0
- package/src/import.mjs +583 -0
- package/src/init.mjs +644 -0
- package/src/mcp.mjs +449 -0
- package/src/pr-title.mjs +47 -0
- package/src/profiles.mjs +68 -0
- package/src/prompts.mjs +189 -0
- package/templates/.github/pull_request_template.md +31 -0
- package/templates/trellis/branch-protection.md +116 -0
- package/templates/trellis/playbooks/code-review.md +64 -0
- package/templates/trellis/playbooks/conventions.md +56 -0
- package/templates/trellis/playbooks/pr-draft.md +39 -0
- package/templates/trellis/playbooks/work-task.md +76 -0
package/src/prompts.mjs
ADDED
|
@@ -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.
|