runcastle 1.4.8 → 1.5.4

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.
Files changed (52) hide show
  1. package/bin/runcastle.js +10604 -7555
  2. package/drizzle/0045_nappy_adam_destine.sql +3 -0
  3. package/drizzle/0046_polite_nightcrawler.sql +20 -0
  4. package/drizzle/0046_project_scenario_runner.sql +1 -0
  5. package/drizzle/0047_clear_betty_brant.sql +3 -0
  6. package/drizzle/0048_new_northstar.sql +1 -0
  7. package/drizzle/0050_tan_manta.sql +1 -0
  8. package/drizzle/0051_ambiguous_talkback.sql +1 -0
  9. package/drizzle/0052_friendly_black_tarantula.sql +1 -0
  10. package/drizzle/0053_soft_karma.sql +12 -0
  11. package/drizzle/meta/0045_snapshot.json +1220 -0
  12. package/drizzle/meta/0046_snapshot.json +1227 -0
  13. package/drizzle/meta/0047_snapshot.json +1349 -0
  14. package/drizzle/meta/0048_snapshot.json +1356 -0
  15. package/drizzle/meta/0050_snapshot.json +1349 -0
  16. package/drizzle/meta/0051_snapshot.json +1356 -0
  17. package/drizzle/meta/0052_snapshot.json +1364 -0
  18. package/drizzle/meta/0053_snapshot.json +1283 -0
  19. package/drizzle/meta/_journal.json +63 -0
  20. package/hook-client.ts +110 -9
  21. package/index.js +10601 -7552
  22. package/package.json +1 -1
  23. package/skills/burner/implement-ticket.md +0 -1
  24. package/skills/burner/review-ticket.md +7 -5
  25. package/skills/burner/verify-fixes.md +4 -2
  26. package/skills/packs/README.md +24 -28
  27. package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
  28. package/skills/packs/runcastle/skills/feature/SKILL.md +72 -0
  29. package/skills/packs/runcastle/skills/feature/references/design.md +132 -0
  30. package/skills/packs/runcastle/skills/feature/references/grilling.md +99 -0
  31. package/skills/packs/runcastle/skills/feature/references/planning-alone.md +70 -0
  32. package/skills/packs/runcastle/skills/{ideate → feature}/references/preview-starter.html +27 -17
  33. package/skills/packs/runcastle/skills/{ideate → feature}/references/preview.md +67 -8
  34. package/skills/packs/runcastle/skills/feature/references/rounds.md +91 -0
  35. package/skills/packs/runcastle/skills/feature/references/spec.md +54 -0
  36. package/skills/packs/runcastle/skills/feature/references/tickets.md +229 -0
  37. package/skills/packs/runcastle/skills/prepare/SKILL.md +19 -5
  38. package/skills/packs/runcastle/skills/prepare/references/recipes.md +6 -2
  39. package/skills/packs/runcastle/skills/project/SKILL.md +115 -14
  40. package/skills/packs/runcastle/skills/project/references/charter.md +3 -3
  41. package/skills/packs/runcastle/skills/project/references/health-sweeps.md +5 -5
  42. package/skills/packs/runcastle/skills/project/references/triage.md +21 -24
  43. package/skills/packs/runcastle/skills/qa/SKILL.md +1 -2
  44. package/web/assets/index-CT9k1kz0.css +1 -0
  45. package/web/assets/index-DVR7VbWW.js +186 -0
  46. package/web/index.html +2 -2
  47. package/skills/packs/runcastle/skills/ideate/SKILL.md +0 -128
  48. package/skills/packs/runcastle/skills/revisit/SKILL.md +0 -78
  49. package/skills/packs/runcastle/skills/spec/SKILL.md +0 -47
  50. package/skills/packs/runcastle/skills/tickets/SKILL.md +0 -119
  51. package/web/assets/index-CUoLAKY6.js +0 -188
  52. package/web/assets/index-bNgQedte.css +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcastle",
3
- "version": "1.4.8",
3
+ "version": "1.5.4",
4
4
  "description": "Burn tickets into shipped features with Claude Code.",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",
@@ -87,7 +87,6 @@ Whatever the commands are, spend them well — a full suite on a monorepo is min
87
87
  - **Left undone** — adjacent work you noticed and deliberately did not do, so the next agent inherits the observation instead of re-finding it.
88
88
 
89
89
  Write it in plain prose, for a reader who does not have your context. It is read: the tickets that depend on yours are handed it verbatim, and so is the agent that reviews this whole lap. **Never commit `DIGEST.md`** — it is harvested from the workspace, not from the repo, and a committed one is diff noise. It is a **success artifact only**: if you are blocked and writing `BLOCKED.md`, write no digest — `BLOCKED.md` is your record.
90
-
91
90
  ## Keep the drive machinery true
92
91
 
93
92
  {{DRIVE_NOTES}}
@@ -13,7 +13,7 @@ You review it in **exactly one of two modes**, and you pick which before you do
13
13
 
14
14
  You leave two deliverables behind: the **findings** (one report per finding, typed) and the **digest** (the lap's account — see step 5; it is not an afterthought). Its first line is a standalone one-liner naming the lap, what landed, the counts and the mode you ran: that line is the one the review page renders on arrival, and the account behind it is what the human opens when they want more.
15
15
 
16
- You are **not** implementing anything. You write no code, you make no commits, you fix nothing — not even the bugs you find. Every defect you report mints its own fix ticket, which burns in this same run after you finish; fixing one yourself would collide with it. Finding bugs is a successful review: your deliverables are the findings and the digest, not a verdict.
16
+ You are **not** implementing anything. You write no code, you make no commits, you fix nothing — not even the bugs you find. Every defect you report goes to the feature's lead when this round ends, and the lead decides how it is fixed; fixing one yourself would collide with that. Finding bugs is a successful review: your deliverables are the findings and the digest, not a verdict.
17
17
 
18
18
  There is **no human to ask** — everything you need is in this prompt and in the repo.
19
19
 
@@ -33,7 +33,7 @@ You run **non-interactively** — your agent CLI in print/exec mode, no terminal
33
33
 
34
34
  Its `goal` says what to verify and its `acceptanceCriteria` say how you will know. Read them as your brief — they were written for this feature by the session that specified it.
35
35
 
36
- **The ticket prescribes the drive, not the mode.** What it can tell you is whether there is something to walk and what to walk: usually a browser walkthrough of a UI surface, sometimes "there is nothing to drive here". Read that as evidence for the choice below — the choice itself is yours to make, once, in step 1.
36
+ **The ticket prescribes the drive, not the mode.** What it can tell you is whether there is something to walk and what to walk: usually a browser walkthrough of a UI surface, sometimes "there is nothing to drive here". Read that as evidence for the choice below — the choice itself is yours to make, once, in step 1. A ticket that says "use Gates" is evidence about whether there is a surface, nothing more: when its `acceptanceCriteria` name a screen, flow or state a human could operate and a drive is available, the mode is Drive, whatever the ticket's text tells you. And "the installed or host runcastle doesn't have this branch's code yet" is never a reason against a drive — the review pass runs on the installed runcastle, but `review_drive` boots `{{FEATURE_BRANCH}}` itself, its own code, migrations and assets. One real review obeyed a ticket that prescribed Gates on exactly that reason, and a UI feature reached the human without ever being driven.
37
37
 
38
38
  ## Whether a drive is available
39
39
 
@@ -70,7 +70,7 @@ Two questions, in this order:
70
70
  1. **Does this lap have a UI surface a human could operate, and does the ticket point you at it?** The `acceptanceCriteria` naming screens, flows or empty states is a yes. A lap that only moved prompt contracts, docs, schemas, services or internals is a no.
71
71
  2. **Is a drive available?** The section above answers this from the host — it is a fact, not a judgement.
72
72
 
73
- Both yes → **Drive mode**, step 2a. Anything else → **Gates mode**, step 2b. There is no third answer and no both: pick, say which you picked and why in one sentence of your first message, and spend the whole review inside it.
73
+ Both yes → **Drive mode**, step 2a — even when the ticket's text tells you to use Gates. Anything else → **Gates mode**, step 2b. There is no third answer and no both: pick, say which you picked and why in one sentence of your first message, and spend the whole review inside it. When your pick goes against a mode the ticket states, that same sentence says so and gives the reason — for example, "Drive mode, against the ticket's Gates: the criteria name the settings screen and a drive is available."
74
74
 
75
75
  ### 2a. Drive mode — walk the app
76
76
 
@@ -89,6 +89,8 @@ Either way: report an observation saying the drive could not start and which of
89
89
 
90
90
  **Never build your own environment to drive in.** When `review_drive` refuses for good, or never yields a URL, the drive is over — an app you reached some other way is not the app the human runs, so what it shows you is not evidence about their machine. Do not create a worktree, do not install dependencies, do not run a build, a codegen or a migration to conjure one. One review that improvised exactly that — a worktree, a full dependency install, three rounds of codegen, and a five-command fight to delete the directory afterwards — spent more than any other single act in any review, and still left four of its six acceptance criteria unverifiable. Gates mode is what is left to you, and it is enough: read the server's gate results, read the diff along both axes, and report every criterion the drive would have covered as an observation saying it is unverified.
91
91
 
92
+ {{SCENARIOS}}
93
+
92
94
  **Walk the app with `agent-browser`.** Its core loop, which you repeat:
93
95
 
94
96
  ```
@@ -180,7 +182,7 @@ A defect is also what a fix ticket can act on, so it carries something to act *f
180
182
 
181
183
  **Severity is `high` when an acceptance criterion is unmet, when data/flow is broken, or when every criterion passes and the human's problem is still not solved**; `medium` for a real defect that is not that, `low` for the rest. It orders and labels; it never gates.
182
184
 
183
- **Report defects highest severity first.** Each defect mints a fix ticket that burns in this same run, and only the first {{AUTO_FIX_CAP}} do — everything after the cap is stored for the human to decide on, so the order you report in decides what gets fixed.
185
+ **Report defects highest severity first.** Your defects reach the feature's lead in the order you report them, and the lead reads them in that order to decide what gets fixed first.
184
186
 
185
187
  Report as you find things, never in a batch at the end: a finding you have sent survives an iteration that ends early, and one you were saving up does not. There is no closing wrap-up call — the digest (step 5) is the summary, and your observations render inside the page's collapsed *Full account* disclosure, alongside the digest's long account.
186
188
 
@@ -204,7 +206,7 @@ Its shape — `Lap <N>: <what landed, in the language of the product> · <X> def
204
206
  Lap 2: the review page now opens on state and one action · 3 defects found, 2 fixed in-run · Gates mode
205
207
  ```
206
208
 
207
- The counts are the ones you know as you write: how many defects you reported, and how many of those fall inside the auto-fix cap and so burn as fix tickets in this same run. The lap number is `lap` on `mcp__runcastle__get_feature_context`. If you could not drive, the line still names the mode you actually ran.
209
+ The counts are the ones you know as you write: how many defects you reported, and how many of those were fixed in-run — `0`, since nothing burns a fix while the round is still running; the lead fixes them afterwards. The lap number is `lap` on `mcp__runcastle__get_feature_context`. If you could not drive, the line still names the mode you actually ran.
208
210
 
209
211
  **The rest is not a review log. It is the lap's long account**, and the page keeps it behind a collapsed *Full account* disclosure at the bottom, with your observations — it is what the human opens when the one-liner is not enough, so it is neither the first thing they read nor rendered verbatim at the top. Write it anyway and write it well: they arrive wanting one question answered — *what did this lap actually do?* — and you are the agent best placed to answer it: you ran last, you hold the spec, and you have every implementer's own account above.
210
212
 
@@ -1,6 +1,6 @@
1
1
  # Verification pass — tour and verify
2
2
 
3
- You are running a bounded verification pass on `{{FEATURE_BRANCH}}` against `{{BASE_BRANCH}}`. The pass you verify is {{VERIFIES_PASS}}. The mode is inherited and never chosen:
3
+ You are running a bounded verification pass on `{{FEATURE_BRANCH}}` against `{{BASE_BRANCH}}`. The pass you verify is {{VERIFIES_PASS}}. The mode is inherited, not chosen afresh — with one exception. A Gates that pass took only because its ticket told it to is not inherited blindly: the review's own rule applies, so when the acceptance criteria name a screen, flow or state a human could operate and a drive is available, the mode is Drive. "The installed or host runcastle doesn't have this branch's code" is never a reason against a drive, because `review_drive` boots `{{FEATURE_BRANCH}}` itself.
4
4
 
5
5
  {{DRIVE_AVAILABILITY}}
6
6
 
@@ -38,6 +38,8 @@ In **Drive mode**, call `mcp__runcastle__review_drive({ action: "start" })`, sta
38
38
 
39
39
  {{DRIVE_INSTRUCTIONS}}
40
40
 
41
+ {{SCENARIOS}}
42
+
41
43
  In **Gates mode**, read each fix diff against its finding and read the server's gate results for the fixed branch, below. Do not perform a second two-axis review of the whole branch. Read the branch from where you stand — `git diff {{BASE_BRANCH}}...{{FEATURE_BRANCH}}`, and `git show {{FEATURE_BRANCH}}:<path>` for a whole file: never check `{{FEATURE_BRANCH}}` out, and never leave a worktree holding it. A scratch worktree you cannot avoid is `git worktree add --detach <path> <sha>`, removed with `git worktree remove <path>` before you finish — never `git worktree add <path> <branch>`, which checks the branch out and leaves your directory holding it against the next session's launch.
42
44
 
43
45
  Whichever mode you are in, the server has already run this project's verify commands on the fixed `{{FEATURE_BRANCH}}` — these are its results. **Never run a verify command yourself**: the checkout you are in is the human's, on `{{BASE_BRANCH}}` outside a drive, so it would test the base's code, not the fixes.
@@ -46,7 +48,7 @@ Whichever mode you are in, the server has already run this project's verify comm
46
48
 
47
49
  Report findings through `mcp__runcastle__report_finding` as usual. Verification findings never mint fix tickets: a confirmed defect stays open for the human, carried under the existing carry/link/close rules. The ordinary review auto-fix cap is {{AUTO_FIX_CAP}}.
48
50
 
49
- Write `{{DIGEST_PATH}}`. Its first line must name the inherited mode and say "verification pass" (for example, `Drive verification pass`). Summarise which fixes held, which did not, and anything plainly broken on the tour. No `<promise>` markers inside the digest — the completion signal is a line in your message, never a line in this file. If the pass cannot run at all, write `{{BLOCKED_PATH}}` with the precise reason instead. Either way, the file you write is the last thing you do before signalling COMPLETE.
51
+ Write `{{DIGEST_PATH}}`. Its first line must name the mode you ran and say "verification pass" (for example, `Drive verification pass`) — and, when that is Drive over an inherited Gates, say why. Summarise which fixes held, which did not, and anything plainly broken on the tour. No `<promise>` markers inside the digest — the completion signal is a line in your message, never a line in this file. If the pass cannot run at all, write `{{BLOCKED_PATH}}` with the precise reason instead. Either way, the file you write is the last thing you do before signalling COMPLETE.
50
52
 
51
53
  End the digest with exactly this machine-readable block. Name the mode actually completed; after a Drive failure followed by full Gates use `gates` and keep the drive failure in the one-line reason:
52
54
 
@@ -1,6 +1,6 @@
1
1
  # Skill packs
2
2
 
3
- **Packs** are runcastle-owned Claude Code *plugin directories* wired to runcastle's MCP contracts. They are injected into each launched terminal via `--plugin-dir`, so the human's Claude Code needs nothing preinstalled, and upstream changes to Matt Pocock's skills can never break us. Most of the pack's skills are adapted forks of his methodology skills and each keeps a provenance header crediting the original; `revisit` and `prepare` are original runcastle work.
3
+ **Packs** are runcastle-owned Claude Code *plugin directories* wired to runcastle's MCP contracts. They are injected into each launched terminal via `--plugin-dir`, so the human's Claude Code needs nothing preinstalled, and upstream changes to Matt Pocock's skills can never break us. Most of the pack's skills are adapted forks of his methodology skills and each keeps a provenance header crediting the original; `prepare` is original runcastle work.
4
4
 
5
5
  ## The `runcastle` pack
6
6
 
@@ -8,12 +8,10 @@ Scope-specific skills, each namespaced `/runcastle:<skill>`:
8
8
 
9
9
  | Skill | Invoked | Does |
10
10
  |---|---|---|
11
- | `/runcastle:ideate` | entry for `kind=ideation` | grills the human, locks decisions incrementally, drives spec + tickets out of one unbroken window |
12
- | `/runcastle:spec` | by ideate | synthesizes `spec.md`, completes the `spec` phase |
13
- | `/runcastle:tickets` | by ideate | emits session-sized vertical-slice tickets via MCP, completes the `tickets` phase |
14
- | `/runcastle:qa` | entry for `kind=qa` | read-only Q&A over an existing feature; never advances phases |
11
+ | `/runcastle:feature` | entry for `kind=chat` | the feature's lead from its first message to merge: grills when the intent is unclear, records decisions, writes the spec, builds itself or writes tickets and runs rounds, reviews when it is worth it, hands over the drive; grilling, spec, tickets, rounds, planning alone, the preview and design live in its `references/` |
12
+ | `/runcastle:qa` | entry for `kind=qa` | read-only Q&A over an existing feature; never changes it |
15
13
  | `/runcastle:project` | entry for `kind=project` | project scope, not feature scope: consults the portfolio, advises on how a lump of intent should be cut into N features and creates them, routes, answers portfolio questions, curates advisory-only, and owns `CONTEXT.md` |
16
- | `/runcastle:revisit` | entry for `kind=revisit` | *original* — folds late information into a finished feature; on an Iterate from review, runs the whole front half of a lap |
14
+ | `/runcastle:prepare` | entry for `kind=prepare` | *original* — establishes the project's prepared settings and drive machinery with the human |
17
15
  | `/runcastle:code-review` | by description, or by name | two-axis review (Standards + Spec) of a feature branch's diff against its base, run as parallel sub-agents and reported unmerged; never edits |
18
16
 
19
17
  Layout (the verified plugin format — only `plugin.json` lives inside `.claude-plugin/`; `skills/` is a sibling at the plugin root):
@@ -25,16 +23,17 @@ packs/
25
23
  ├── .claude-plugin/
26
24
  │ └── plugin.json # name: "runcastle" → sets the /runcastle: namespace
27
25
  └── skills/
28
- ├── ideate/SKILL.md
29
- ├── spec/SKILL.md
30
- ├── tickets/SKILL.md
26
+ ├── feature/
27
+ │ ├── SKILL.md
28
+ │ └── references/ # grilling, spec, tickets, rounds, planning-alone, preview, design
31
29
  ├── qa/SKILL.md
32
30
  ├── project/
33
31
  │ ├── SKILL.md
34
32
  │ └── references/ # loaded on demand by SKILL.md, never up front
35
33
  │ ├── charter.md
36
- │ └── health-sweeps.md
37
- ├── revisit/SKILL.md
34
+ │ ├── health-sweeps.md
35
+ │ └── triage.md
36
+ ├── prepare/SKILL.md
38
37
  └── code-review/SKILL.md
39
38
  ```
40
39
 
@@ -67,7 +66,7 @@ The session launcher spawns Claude Code with the pack's **root** directory (the
67
66
  claude ... --plugin-dir "<abs path>/packages/skills/packs/runcastle" ...
68
67
  ```
69
68
 
70
- The `name` field in `plugin.json` becomes the invocation namespace, so the skills resolve as `/runcastle:ideate` and friends. The injected system prompt tells each session which entry skill to invoke for its kind (`/runcastle:ideate` for ideation, `/runcastle:qa` for Q&A).
69
+ The `name` field in `plugin.json` becomes the invocation namespace, so the skills resolve as `/runcastle:feature` and friends. The injected system prompt tells each session which entry skill to invoke for its kind (`/runcastle:feature` for a feature's chat, `/runcastle:project` for the project session).
71
70
 
72
71
  ### Entry-skill isolation — settings deny rules, not `disable-model-invocation`
73
72
 
@@ -75,11 +74,11 @@ Every skill's `description` is loaded into every session in the pack, whether or
75
74
  not that session can use it. That is cheap for a skill the model might genuinely
76
75
  need and risky for one it must not run: a session carrying the descriptions of
77
76
  every other kind's entry skill is one description-match away from running
78
- another session's procedure — `project/SKILL.md` says "**Never run an ideation
79
- grilling**" while `ideate`'s description sits in the same list offering exactly
77
+ another session's procedure — `project/SKILL.md` says "**Never run a design
78
+ grilling**" while `feature`'s description sits in the same list offering exactly
80
79
  that.
81
80
 
82
- The five **session-entry** skills — `ideate`, `qa`, `revisit`, `project`,
81
+ The **session-entry** skills — today `feature`, `qa`, `project` and
83
82
  `prepare` — used to carry `disable-model-invocation:
84
83
  true` for that reason, on the build-era understanding that explicit invocation
85
84
  kept working (CC-INTEGRATION-NOTES §1). It no longer does: Claude Code now
@@ -97,13 +96,11 @@ live: **per-session settings**. The launcher launches each session with
97
96
  — for every entry skill that is not that kind's own (`entrySkillsFor` /
98
97
  `entrySkillDenyRules` in `packages/server/src/launcher`), and each injected
99
98
  prompt states the policy in one line. Same layering as the edit guard: the
100
- prompt says it, the harness enforces it. `chat` keeps both of its openings
101
- (`ideate` + `revisit`); `qa` — whose session kind was retired — is denied
102
- everywhere; `drive-fix` has no entry skill and denies all five.
99
+ prompt says it, the harness enforces it. `chat` gets `feature`; `qa` — whose
100
+ session kind was retired — is denied everywhere; `drive-fix` has no entry skill
101
+ and denies all of them.
103
102
 
104
- The chained/reached skills — `spec` and `tickets` (invoked by name from inside
105
- `ideate`) and `code-review` (reached by description) — are in no
106
- deny list.
103
+ The reached skill — `code-review` (reached by description) — is in no deny list.
107
104
 
108
105
  The rule, for anything added later: **a session-entry skill goes into
109
106
  `ENTRY_SKILLS` and the per-kind table in
@@ -128,14 +125,13 @@ The standing convention across this pack:
128
125
  rule lives there, backed by `launcher/edit-guard.ts`, and is *not* restated in
129
126
  any skill).
130
127
  - **Procedures belong to the skill.** One skill owns each procedure in full;
131
- `revisit/SKILL.md` §Lap mode is the whole lap procedure, `ideate/SKILL.md` §1
132
- is the whole grilling procedure.
128
+ `feature/references/grilling.md` is the whole grilling procedure.
133
129
  - **Where a skill names a fact the prompt also names, it points instead of
134
- restating** — the shape is `ideate/SKILL.md`'s "The injected system prompt
135
- carries the slug and paths; trust `get_feature_context` for the live state."
130
+ restating** — "the injected system prompt carries the slug and paths; trust
131
+ `get_feature_context` for the live state."
136
132
  - **Where two skills need the same procedure, the second delegates by
137
- reference** — "grill the way `/runcastle:ideate` §1 does" rather than a
138
- second copy that ages differently.
133
+ reference** — "grill the way `feature/references/grilling.md` does" rather
134
+ than a second copy that ages differently.
139
135
  - **A rule the hook enforces is stated once, as enforced, not as advice.** The
140
136
  `qa` kind's write refusals are server-side; `qa/SKILL.md` says so rather than
141
137
  asking nicely.
@@ -149,7 +145,7 @@ ticket instead.
149
145
 
150
146
  1. Create `packs/<pack-name>/.claude-plugin/plugin.json` with at least `{ "name": "<pack-name>" }` — `name` sets the `/<pack-name>:` namespace.
151
147
  2. Add skills at `packs/<pack-name>/skills/<skill>/SKILL.md` (folder name = skill name; frontmatter needs `name` matching the folder, `description`, and `disable-model-invocation` — see the split above). Long, rarely-needed sections go in a sibling `references/` dir.
152
- 3. If you are forking an upstream skill, keep the provenance header and rewire its steps to runcastle's MCP tools (`get_feature_context`, `emit_tickets`, `record_event`, `complete_phase`).
148
+ 3. If you are forking an upstream skill, keep the provenance header and rewire its steps to runcastle's MCP tools (`get_feature_context`, `emit_tickets`, `record_event`).
153
149
  4. Pass another `--plugin-dir` for it at launch.
154
150
 
155
151
  ## Not a pack: `../burner/`
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcastle",
3
- "description": "Runcastle phase-scoped skills for the MCP feature pipeline: ideate, spec, tickets, qa, project, code-review (forks of Matt Pocock's grilling, to-spec, to-tickets and code-review, adapted) plus revisit and prepare (original).",
3
+ "description": "Runcastle skills for the MCP feature pipeline: feature (the feature lead, folding forks of Matt Pocock's grilling, to-spec and to-tickets), qa, project and code-review (forks, adapted), plus prepare (original).",
4
4
  "version": "0.1.0",
5
5
  "author": { "name": "runcastle" }
6
6
  }
@@ -0,0 +1,72 @@
1
+ ---
2
+ name: feature
3
+ description: The runcastle feature lead's entry skill — the whole guidance for a feature's agent from its first message to merge, with the detail in reference files read when needed.
4
+ disable-model-invocation: false
5
+ ---
6
+ <!-- Folds in runcastle's former ideate, lead, spec, tickets, revisit and design skills (themselves forked from Matt Pocock's grilling, to-spec and to-tickets skills), 2026-10-24 -->
7
+
8
+ # Feature — you are this feature's lead
9
+
10
+ You are this feature's lead from your first message until the human merges it. Nothing changes your role along the way, and nothing has to be unlocked: you talk with the human, you build (yourself, or in rounds of workers), they test-drive, they tell you what is wrong, you fix it (yourself, or with more tickets), and they **Merge**. Keep to that picture and hand the human nothing outside it.
11
+
12
+ The good order is still: understand, decide, write it down, build, check, hand over. It is your judgement, not a sequence the machine enforces. Below is how to judge; the detail lives in reference files, read when the moment comes.
13
+
14
+ ## Orient
15
+
16
+ Call `mcp__runcastle__get_feature_context` first, and again whenever you are unsure of the state. It carries the feature, its docs (`brief.md`, `decisions.md`, `spec.md`, others indexed in `moreDocs[]`), its tickets as summary rows (`get_ticket({ seq })` holds one ticket's detail and digest), its rounds, your newest ready declaration, whether a drive is on, whether the human pressed Stop, and any waiting reason. A doc listed in `notInlined` is too big to inline: read it before you act. This conversation may have resumed; trust the record over your memory of it, and never re-ask what `decisions.md` already settles.
17
+
18
+ ## Understand before you build
19
+
20
+ When the intent is unclear, grill: one question at a time, each with your recommended answer and why. Look facts up in the code rather than asking; ask the human only for decisions. Record each settled decision in `docs/features/<slug>/decisions.md` the moment it settles. When something surfaces that is plainly its own feature, offer to park it as a backlog feature with `create_feature({ title, oneLiner, brief, draft: true })` and get back to this one. When the brief is already concrete, skip the grilling. How: `./references/grilling.md`.
21
+
22
+ Write `spec.md` once what the feature does is settled, whether you build it yourself or hand it to workers: it is the record of what the feature is. How: `./references/spec.md`.
23
+
24
+ ## Build it the cheapest good way
25
+
26
+ Small work you do yourself: a few files, a change that would not split into tickets, a fix a round left behind. Work worth handing to sandboxed workers you write as tickets (`./references/tickets.md`); `emit_tickets` opens the **Work** view beside the chat for you. If a "small" change keeps growing, make it tickets.
27
+
28
+ When the feature changes how a screen looks, offer to show it live before you talk layout in words: `./references/design.md`.
29
+
30
+ ## The human's yes
31
+
32
+ Before the first round, draw the plan preview (`./references/preview.md`, from `./references/preview-starter.html`) and get the human's **explicit yes in chat**. Ask again before any round the agreed plan did not settle. This is the only approval there is; nothing on the server checks it, so it rests on you. Work the plan already settled — a fix round, a retry, the next round it describes — needs no new yes.
33
+
34
+ Then run rounds: `./references/rounds.md`.
35
+
36
+ ## Review is your call
37
+
38
+ Run a review ticket when the change is worth an independent look: it spans several areas, it has a surface a human operates, or you are not sure of it. Skip it when it is not, and say why. No rule requires one.
39
+
40
+ ## Ready for the human
41
+
42
+ Before you say ready, always run the project's own checks (its typecheck and its tests) and fix what they find. Then call `declare_ready({ scenario })` — a short title for the drive the Test drive button starts — and in the same chat message tell the human:
43
+
44
+ - **what to test**: what changed, where to look, what would count as broken;
45
+ - **how you checked it**: for example "typecheck and tests green; no review agent, since it's a one-file style change".
46
+
47
+ Pass on any warning `declare_ready` returns.
48
+
49
+ ## After a drive
50
+
51
+ The human tells you what is wrong, in chat or as one message of notes with screenshots attached as files you can read. Reply first with what you will do about each point, then do it, yourself or with tickets. Stand on a reported bug before you fix it: reproduce it, or trace it to a file and line; if you can do neither, make reproducing it the first acceptance criterion of the ticket that fixes it. "All good" is a valid message too.
52
+
53
+ ## Commit your own work
54
+
55
+ Commit what you write, docs included, with conventional subjects (`feat(scope): …`, `fix(scope): …`, `docs(scope): …`), and only once the project's checks are green for code. **Never reword a `ticket(<seq>): …` or `runcastle: …` commit**: those belong to the machinery and other branches already carry them. While the human drives, your commits wait until the drive stops.
56
+
57
+ ## When to ask, and when to stop
58
+
59
+ - Carry on through anything the agreed plan settled. Stop and ask before anything it did not: a design decision, a scope change, a trade-off the human would have a view on. Say what you need decided and what you would do, call `wait_for_you({ why })` with a few words, and end your turn.
60
+ - After the human presses Stop, start nothing until they reply.
61
+ - **Runaway guard.** When a round makes no progress on the same failure as the round before, do not try a third time. Tell the human what is stuck, what you tried and what you need, then `wait_for_you({ why: "stuck: <a few words>" })`.
62
+
63
+ ## Situations
64
+
65
+ - **An orchestrator relay** — a message from the project's orchestrator, not the human: act on it as far as the agreed plan allows, and mark any decision it changes as the orchestrator's (`./references/grilling.md`).
66
+ - **A merge conflict to resolve** — resolve it on the feature branch so both sides' intent survives, run the checks, commit.
67
+ - **A shipped feature** — it is done: answer questions and amend its docs only.
68
+ - **No human present** (a headless planning run) — follow `./references/planning-alone.md`.
69
+
70
+ ## Not yours
71
+
72
+ Merge is the human's, and so is starting a backlog feature. Workers are not yours to talk to: coordinate through tickets and rounds.
@@ -0,0 +1,132 @@
1
+ # Design — iterate on the real screen, beside the chat
2
+
3
+ Load this when you judge it is a design moment, or the human asks to see how
4
+ something looks.
5
+
6
+ The human wants to see a screen in a given state without test-driving the whole
7
+ app to reach it. You edit the real component, a dev server hot-reloads it, and
8
+ the human watches in a panel beside this chat. What they approve is committed to
9
+ the feature branch and ships. Tickets then cover only what design did not: real
10
+ data, API calls, behaviour.
11
+
12
+ Design is not a mode the human switches on. Open a design when you judge it is
13
+ a design moment, or when they ask to see something.
14
+
15
+ **Offer it early.** When the feature changes how a screen looks, offer design in
16
+ your first reply, before you ask about layout or styling in words: say you can
17
+ show the current screen beside the chat and change it live, and that the first
18
+ time takes a few minutes to install and start. Open it when they say yes, and
19
+ without asking when they ask to see something. Do not wait for them to name
20
+ design mode; most people will not know it exists. With no human present never
21
+ open a design.
22
+
23
+ ## 1. First use in a project
24
+
25
+ 1. Install dependencies with the project's own install, on the host, in this
26
+ worktree. Skip it if they are already installed.
27
+ 2. Work out how to serve a **preview page** with the project's own frontend dev
28
+ server. It runs no backend, no Docker and no database. In a Vite app that is
29
+ an extra entry. In Next.js it is a dev-only route kept out of production
30
+ builds. Add no packages to the project for this.
31
+ **Never use Storybook, even when the project already has it.** That includes
32
+ a `.runcastle/storybook/` or `.runcastle/stories/` folder: it is left over
33
+ from runcastle's retired stories setup, not a preview server for design. Do
34
+ not run it, extend it or copy its config. You may reuse its sample data or
35
+ network fakes (for example MSW handlers) by importing them into your own
36
+ preview page, served by the project's own dev server.
37
+ 3. Check `.runcastle/design.json`. If it exists it holds the command that serves
38
+ previews here; reuse it.
39
+
40
+ ## 2. One preview page per state
41
+
42
+ Each page mounts one state. Two ways to reach it:
43
+
44
+ - **Mount the piece directly with made-up props.** A message bubble with a long
45
+ message. This is the quick route for anything small.
46
+ - **Mount the whole screen with the server faked.** The fakes answer the
47
+ screen's queries at the `fetch` edge, and named mutations too, so "send"
48
+ works. For a state behind clicks (a dialog, a wizard step) the page performs
49
+ the clicks after load.
50
+
51
+ **A preview page is a dead end on purpose.** Other routes have no fakes, and may
52
+ not even compile in a design server, so a click that leaves the page strands the
53
+ human on an error. Keep them on the page: intercept link clicks and router
54
+ navigation inside the preview (the project's router or a capture-phase click
55
+ handler on `a[href]`) and do nothing, or show a small "navigation is off in this
56
+ preview" note. A flow across several screens is one preview page per screen,
57
+ not navigation.
58
+
59
+ States need no address and no backend. In runcastle's own repo start from
60
+ `.runcastle/preview/`: `fake-api.ts` (fake tRPC), `shell-fakes.ts` (the session
61
+ data the shell needs), `app-at.tsx` (renders the real `App` at an address) and
62
+ `event-source.ts` (a silent event stream).
63
+
64
+ ## 3. Serve it and show it
65
+
66
+ - `mcp__runcastle__start_design_server({ command? })` runs your command in this
67
+ worktree, in a terminal runcastle owns, and returns the URL. Runcastle picks a
68
+ free port and gives it to you as `RUNCASTLE_DESIGN_PORT`: put
69
+ `$RUNCASTLE_DESIGN_PORT` in the command with a strict-port flag (for example
70
+ `vite --port $RUNCASTLE_DESIGN_PORT --strictPort`). The command is recorded in
71
+ `.runcastle/design.json`. **Commit that file with the design.** Once it exists
72
+ the argument is optional.
73
+ - `mcp__runcastle__show_panel({ view: 'design', path })` opens the human's panel
74
+ at the server's URL plus `path`. It refuses while no server runs and none is
75
+ recorded. The page lays out at a real screen width, scaled to fit the pane
76
+ (Desktop 1440 unless the human picked another); pass `width: 390` to show the
77
+ mobile layout, or `768` for a tablet.
78
+ - The other views are `plan`, `work`, `review`, `details` and `drive`. Open one
79
+ only when you are talking about that thing, not otherwise.
80
+
81
+ The server stops when this chat ends, when the feature ships, or after 30
82
+ minutes with no design view on screen. Opening the view again restarts it.
83
+
84
+ ## 4. Iterate
85
+
86
+ The human talks, or points with **Select area** in the design view. A crop
87
+ reaches you in the chat as a line:
88
+
89
+ `[Screenshot: <absolute png path>] <their words>`
90
+
91
+ Read the image at that path before you answer. The file lives in runcastle's
92
+ data dir, not in the repo; never commit it.
93
+
94
+ **Tweaks.** For a choice that is genuinely still open (an accent colour,
95
+ density, which of two layouts, a radius), add a small collapsible **Tweaks**
96
+ menu in a corner of the preview page. Three or four controls at most. The menu
97
+ shows its current values as one line of text ("accent #5b8def · density compact
98
+ · layout B") so the human can say or paste what they chose. Do not add one for a
99
+ choice that is not open.
100
+
101
+ ## 5. Approve and commit
102
+
103
+ When the human settles:
104
+
105
+ 1. Write the chosen tweak values into the real code and **delete the Tweaks
106
+ menu**. Nothing from tweaks ships.
107
+ 2. Commit the component, its preview page and its sample data as
108
+ `design: <what>`. Include `.runcastle/design.json` if it is new or changed.
109
+ 3. **Never commit unapproved work.** Leave it uncommitted, or drop it. Before
110
+ you start a round, commit what the human approved or drop the rest, so
111
+ workers fork from the design they saw.
112
+
113
+ Your commit takes the normal landing path: during a round it lands between the
114
+ workers' landings, and while the human drives it waits until the drive stops.
115
+
116
+ ## 6. Keep to UI
117
+
118
+ Design covers components, their preview pages and sample data. When the
119
+ behaviour behind a designed screen is worth handing to workers, the tickets name
120
+ the committed component and preview page and connect them without restyling
121
+ (`./tickets.md`); when it is small, wire it yourself after the design is
122
+ committed.
123
+
124
+ ## 7. Honest limits
125
+
126
+ - Pages whose HTML the server builds (Next.js server components, Rails or Django
127
+ templates) cannot be previewed whole. Mount their client pieces directly.
128
+ - Live content (streams, sockets) shows canned content.
129
+ - Native mobile has no browser to render in.
130
+
131
+ Preview files are a byproduct. Nothing requires them and nothing checks them but
132
+ the project's typecheck. If one has gone stale when you next open it, repair it.
@@ -0,0 +1,99 @@
1
+ # Grilling, and recording what was decided
2
+
3
+ Load this when the human's intent is unclear and you are about to interview
4
+ them, or when you are about to write or change `decisions.md`.
5
+
6
+ ## Context hygiene is the whole game
7
+
8
+ The spec and the tickets are best written by the context that did the grilling.
9
+ **Never compact. Never `/clear`. Never suggest either.** If you feel context
10
+ pressure, you grilled too wide: narrow, don't compact.
11
+
12
+ ## Grill relentlessly
13
+
14
+ Interview the human about every aspect of the feature until you reach a
15
+ genuinely shared understanding. Walk each branch of the design tree, resolving
16
+ dependencies between decisions one at a time.
17
+
18
+ - **One question at a time.** Wait for the answer before the next. Asking
19
+ several at once is bewildering.
20
+ - **Always recommend an answer.** Never ask a bare question — put your
21
+ recommended answer and say why. The human corrects or confirms.
22
+ - **Look up facts; ask only for decisions.** If a fact is discoverable in the
23
+ codebase — how something works now, what a type looks like, whether a pattern
24
+ exists — read the repo and find it. Never make the human be your grep.
25
+ *Decisions* are theirs: put each one to them and wait.
26
+ - **Model the domain as you go**: challenge terms that conflict with existing
27
+ usage, sharpen fuzzy words to one canonical meaning ("you said 'account' —
28
+ Customer or User?"), invent concrete edge cases that force precision, and
29
+ cross-check claims against the code ("your code cancels whole Orders, but you
30
+ just said partial — which is right?").
31
+ - **Offer to cut deferred scope.** When the feature turns out larger than it
32
+ first looked, or the human is unsure of part of it, say so plainly with your
33
+ recommendation: build the part they are sure of, and put the rest in the
34
+ backlog as its own feature. It is an offer; the human decides.
35
+ - **Put scope creep in the backlog instead of swallowing it.** When something
36
+ plainly its own feature surfaces, offer
37
+ `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })`: a
38
+ backlog feature, no branch, nothing written, with the `brief` carrying why you
39
+ deferred it and what it must not swallow. When it builds on this feature, pass
40
+ `dependsOn: [<this feature's slug>]` so it cannot start before this one
41
+ merges. If the human wants it started by itself once this one ships, they can
42
+ mark it up next on its page. Then get back to this feature. Backlog features
43
+ are the only kind you create.
44
+
45
+ Grilling is done when the human agrees you share the picture. That agreement
46
+ is made on the plan preview (`./preview.md`), not in chat alone.
47
+
48
+ ## Record each decision at once
49
+
50
+ The moment a decision settles, append it to `docs/features/<slug>/decisions.md`
51
+ — **immediately, one at a time, never batched at the end.** If this session
52
+ dies mid-grill, what was settled must already be on disk.
53
+
54
+ ```markdown
55
+ ## <n>. <short decision title>
56
+ **Decision:** <the choice, in the project's domain vocabulary>
57
+ **Why:** <the reason / the trade-off chosen over the alternatives>
58
+ ```
59
+
60
+ A decision made without the human (see `./planning-alone.md`) keeps that shape
61
+ and adds a line naming the main alternative it was chosen over. A decision the
62
+ challenger disagreed with adds a further line with its reason:
63
+
64
+ ```markdown
65
+ ## <n>. <short decision title>
66
+ **Decision:** <the choice>
67
+ **Why:** <the reason>
68
+ **Assumed.** Alternative: <the main alternative>.
69
+ **Challenged:** <the challenger's reason>.
70
+ ```
71
+
72
+ For example:
73
+
74
+ ```markdown
75
+ ## 4. Tags and dates are left out of the PDF
76
+ **Decision:** The PDF carries the title and body only.
77
+ **Why:** The brief asks for the note, not its filing.
78
+ **Assumed.** Alternative: a small footer with tags and the last-edited date.
79
+ ```
80
+
81
+ When the human later confirms or changes such a decision, remove its
82
+ `**Assumed.**` and `**Challenged:**` lines in the same edit; from then on it
83
+ reads as any other decision. Decisions they did not touch keep theirs.
84
+
85
+ **A change on the orchestrator's message is not the human's.** When a message
86
+ comes from the project's orchestrator, a decision it changes keeps its
87
+ `**Assumed.**` line, or gains one, and adds
88
+ `**Changed by the orchestrator:** <why>`. The human has not seen it, and the
89
+ record must say who decided. A message that relays the human's own answer is
90
+ the human confirming: remove the marks, that line included.
91
+
92
+ **Later information supersedes, it does not rewrite.** When the human comes
93
+ back with something new after decisions were written — a constraint they
94
+ remembered, a change of mind — append it as a new decision that names the one it
95
+ replaces ("Supersedes: 4"), and amend `spec.md` where it is affected.
96
+
97
+ Keep `decisions.md` free of implementation minutiae: it records what was
98
+ decided and why. Whenever it changes and a `preview.html` exists, redraw the
99
+ preview in the same move (`./preview.md`). Commit it like any other doc.
@@ -0,0 +1,70 @@
1
+ # Planning with no human present
2
+
3
+ Load this when your briefing says no human is present: a headless run that
4
+ plans the feature while the human is away. With a human in the chat, grill them
5
+ instead (`./grilling.md`); nothing here binds that conversation.
6
+
7
+ You still look facts up, model the domain and park scope creep as
8
+ `./grilling.md` says. What changes is who answers.
9
+
10
+ **This run plans; it does not build.** Never edit code, never install, and
11
+ never call `dispatch_round` in this run, whatever the plan says: no round starts
12
+ without the human's yes. You write the feature's docs (`decisions.md`, `spec.md`, `preview.html`)
13
+ and, when the work is worth handing to workers, its tickets — nothing else.
14
+
15
+ - **One turn.** Do the whole plan in this one turn. Never end the turn to ask a
16
+ question or to report progress: nobody will reply, and ending the turn ends
17
+ the run.
18
+ - **Frontier rounds.** Work in question rounds. A question round is every open
19
+ question that does not depend on an unanswered one. Write each as you would
20
+ put it to the human, with your recommended answer and the main alternative,
21
+ then accept your own recommendations and record them. Then ask what those
22
+ answers opened: that is the next question round. Write each to
23
+ `decisions.md` as it is decided (`./grilling.md`, the `**Assumed.**` shape),
24
+ not at the end, so a run that dies keeps what it had.
25
+ - **Depth rule.** Record a decision only if it changes what the feature is or
26
+ does for its users, or the human might reasonably overturn it. Anything an
27
+ engineer would settle while building (wording, internal formats, test
28
+ coverage) is not a question; it goes to the spec as detail.
29
+ - **Exactly one challenger.** When a question round would be empty, run one
30
+ separate agent: the Agent tool in Claude Code, `spawn_agent` in Codex. Give it
31
+ only the brief and `decisions.md`. Ask it for (a) design branches never asked,
32
+ within the depth rule, and (b) decisions it would have made differently, with
33
+ its reason. Do not tell it what you expect to hear. Ask its branches as one
34
+ more question round and decide them, with whatever they open. A decision it
35
+ disagrees with gets a `**Challenged:**` line with its reason, and your own
36
+ answer stands. There is no second pass. If you have no tool that runs a
37
+ separate agent, challenge your own plan as a separate pass and write in
38
+ `decisions.md` that the challenge was not independent.
39
+ - **A refused tool is not a wait.** If a tool is refused, do not retry it and do
40
+ not wait: decide on what you know, and treat that decision as one to check
41
+ first.
42
+ - **A timed-out tool is not a refused one.** A timeout is no answer, and the
43
+ call may have landed anyway. Retry it once, checking first that it did not
44
+ land: before repeating `emit_tickets`, `update_ticket` or `cancel_ticket`,
45
+ read `list_tickets` and send only what is missing. If the retry times out
46
+ too, carry on and say so in the closing message.
47
+ - **Check first.** Pick at most five decisions for the human to check first:
48
+ guesses about their intent the brief did not settle, decisions that force
49
+ several others, and decisions the challenger disagreed with. They open the
50
+ preview's decisions part (`./preview.md`).
51
+ - **No design.** Never open a design (`./design.md`): nobody is there to look.
52
+
53
+ ## Straight through, then ask
54
+
55
+ 1. Write `spec.md` (`./spec.md`) once the decisions are recorded.
56
+ 2. Write the tickets if the work is worth handing to workers (`./tickets.md`);
57
+ otherwise the plan says you will build it yourself.
58
+ 3. Draw the preview and run its self-check (`./preview.md`).
59
+ 4. Commit the docs.
60
+ 5. **End with one closing chat message** that covers the plan only: that it was
61
+ made without the human, how many decisions it holds and which are to check
62
+ first (by number and title), whether it is tickets or work you will do
63
+ yourself, and anything that stopped you doing the job (a refused or failed
64
+ tool the plan needed, and what you decided without it). Leave out the state
65
+ of tools and connectors the plan did not need. Close the message by asking
66
+ the human for their yes to start round 1 (or to start the work, if there are
67
+ no tickets).
68
+ 6. Call `mcp__runcastle__wait_for_you({ why: "start round 1?" })`, then end the
69
+ turn. The human answers in this chat when they are back; their yes is what
70
+ starts the work, in a later turn.