runcastle 1.4.1 → 1.4.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcastle",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "Burn tickets into shipped features with Claude Code.",
5
5
  "license": "FSL-1.1-ALv2",
6
6
  "type": "module",
@@ -52,7 +52,7 @@ Whatever the commands are, spend them well — a full suite on a monorepo is min
52
52
  - **Report a gate that cannot run.** If a configured verify command cannot run at all because its command or required runtime is missing, write `GATE_UNRUNNABLE.md` at the sandbox root as JSON with the exact strings `{"command":"<exact command>","error":"<exact error>"}` (or a JSON array of those objects for multiple gates). Continue every other gate you can run. This is an environment report, never your ticket's failure, and it does not replace `BLOCKED.md` or `DIGEST.md`.
53
53
 
54
54
  - **Capture, then read.** Redirect a full run to a file (`<command> > /tmp/test-run.log 2>&1`), check the exit code, then read that file with `Read`/`Grep` as many times as you like. Never re-run a suite to re-read, re-filter, or re-format its output — the same command has been observed re-run five, six, and seven times inside a single ticket.
55
- - **Run the whole thing rarely.** Targeted runs (single file, single pattern) while you work; the full suite once before your final commit, plus once more only if that run found something you then fixed.
55
+ - **Run the whole thing rarely.** Targeted runs (single file, single pattern) while you work; the full suite once at the end — *after* your work is committed (see "How to work", step 4) — plus once more only if that run found something you then fixed.
56
56
  - **Never `git stash` to get a clean-tree comparison.** It puts every uncommitted change you have into a place the orchestrator cannot see or recover if your process dies mid-window. If you need to compare against the pre-change state, use the baseline above, read the file at `HEAD` with `git show`, or — only if you genuinely need the old tree on disk — `git worktree add --detach <path> <sha>` a **detached** scratch checkout and `git worktree remove` it when done. Never `git worktree add <path> <branch>`: that checks the branch out, and a worktree left holding a branch is one nobody else can check it out in.
57
57
 
58
58
  ## How to work
@@ -73,11 +73,13 @@ Whatever the commands are, spend them well — a full suite on a monorepo is min
73
73
 
74
74
  A half-done ticket with four green commits is a good outcome the next iteration can finish. A nearly-done ticket with zero commits is a total loss.
75
75
 
76
- 5. **Self-review before you finish (forked code-review — two axes).** When all acceptance criteria pass, review your own diff along both axes, then fix what you find and re-run typecheck + tests:
76
+ **Commit before the long final checks.** The full typecheck and test suite are the commands most likely to kill your process — in real burns they were OOM-killed mid-run, and every agent that ran them before committing lost a finished, passing fix in all of its iterations. So once the targeted tests for your change pass, commit it *before* you start the full typecheck and test suite. If those checks then find something, fix it in a follow-up commit. A death during the long checks must never cost the work itself.
77
+
78
+ 5. **Self-review before you finish (forked code-review — two axes).** When all acceptance criteria pass, review your own diff along both axes, then fix what you find — and, as in step 4, once the targeted tests for those fixes pass, commit the fixes *before* you re-run the full typecheck and test suite:
77
79
  - **Standards** — does the diff follow the documented standards above and the conventions of the surrounding code? Watch for the smells: duplicated logic (extract it), mysterious names (rename), primitive obsession (give the concept a type), speculative generality (delete anything the ticket did not ask for), feature envy, data clumps.
78
80
  - **Spec = this ticket** — is every acceptance criterion actually met, and is there **nothing in the diff the ticket did not ask for**? Missing and extra both count.
79
81
 
80
- A review agent runs against your diff at the end of this burn, along exactly these two axes and against exactly those standards files. Everything you catch here is a finding that never becomes someone's fix ticket. Commit the fixes.
82
+ A review agent runs against your diff at the end of this burn, along exactly these two axes and against exactly those standards files. Everything you catch here is a finding that never becomes someone's fix ticket.
81
83
 
82
84
  6. **Write your digest — the last thing you do.** Once every acceptance criterion passes and the self-review fixes are committed, write `DIGEST.md` (see "Where to work" for exactly where) as your final act before printing `<promise>COMPLETE</promise>`. Roughly 10–15 lines, three parts:
83
85
  - **What was done** — past tense, what you actually built, *including where it deviated* from the approach the ticket described.
@@ -7,7 +7,7 @@ Every implementation ticket in this burn has landed on `{{FEATURE_BRANCH}}`, whi
7
7
  You review it in **exactly one of two modes**, and you pick which before you do anything else:
8
8
 
9
9
  1. **Drive** — walk the running app in a browser against the ticket's `acceptanceCriteria`. For a lap with a surface a human could operate, when a drive is available.
10
- 2. **Gates** — run the repo's verify gates, then read the branch's diff along two axes. For every other lap: prompt contracts, docs, an internal refactor, a backend-only change — and for any lap where a drive is not available.
10
+ 2. **Gates** — read the server's gate results on the branch, then read the branch's diff along two axes. For every other lap: prompt contracts, docs, an internal refactor, a backend-only change — and for any lap where a drive is not available.
11
11
 
12
12
  **One mode, never both — except the explicit drive-failure fallback below.** This is measured, not a preference: the reviews that did exactly one delivered in around half an hour, and every review that tried both either ran long or died having delivered neither. Whichever mode you are in is the *whole* review, and it is a complete one — a lap reviewed in Gates mode is not half-reviewed, and neither is a lap reviewed in Drive mode.
13
13
 
@@ -39,6 +39,12 @@ Its `goal` says what to verify and its `acceptanceCriteria` say how you will kno
39
39
 
40
40
  {{DRIVE_AVAILABILITY}}
41
41
 
42
+ ## The server's gate results
43
+
44
+ The server has already run this project's verify commands on `{{FEATURE_BRANCH}}` — whichever mode you pick, this is the gate record for the branch you are reviewing:
45
+
46
+ {{GATE_NOTES}}
47
+
42
48
  ## Feature context
43
49
 
44
50
  {{FEATURE_BRIEF}}
@@ -81,7 +87,7 @@ The URL is **not** ready when `start` returns — the dev server has to print it
81
87
 
82
88
  Either way: report an observation saying the drive could not start and which of the two it was, run the step 4 cleanup for whatever you got as far as starting, and spend the whole review in Gates mode. That is a complete review too. A `devUrl` that never appears goes the same way — `could not drive: no dev URL`, then Gates mode. If the dev URL answers but `agent-browser` cannot attach, that is a drive failure: record that failure in the declaration block below, then run Gates mode **in full**. This is the one explicit exception to the never-run-both-modes rule.
83
89
 
84
- **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: run the verify commands as the repo's own manifest defines them, read the diff along both axes, and report every criterion the drive would have covered as an observation saying it is unverified.
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.
85
91
 
86
92
  **Walk the app with `agent-browser`.** Its core loop, which you repeat:
87
93
 
@@ -107,15 +113,13 @@ Work the ticket's `acceptanceCriteria` one at a time: reach the part of the app
107
113
 
108
114
  Then go to step 3. **Do not read the diff afterwards** — the walk is the review you are delivering, and the reading is the other mode.
109
115
 
110
- ### 2b. Gates mode — the verify gates, then the diff
116
+ ### 2b. Gates mode — the server's gate results, then the diff
111
117
 
112
118
  No app, no browser, no drive slot: this mode needs only the repository.
113
119
 
114
- **Run the gates first.** They are cheap next to the reading and they fail loudly.
115
-
116
- {{GATE_NOTES}}
120
+ **Start from the server's gate results** above — they are the gates, already run on the branch. Do not run any verify command yourself: this checkout is on `{{BASE_BRANCH}}`, so anything you run here tests the base's code, not the branch's.
117
121
 
118
- A gate that fails is a defect like any other, and one worth the human's attention above the rest — name the command, quote the failure, make the command the repro step, and carry on to the diff rather than stopping there. A gate you could not run at all is an observation.
122
+ A gate that failed is a defect like any other, and one worth the human's attention above the rest — name the command, quote the failure from its output file, make the command the repro step, and carry on to the diff rather than stopping there. A gate the server could not run at all is an observation.
119
123
 
120
124
  **Pin the fixed point.** Both refs are given to you — do not go looking for a default branch to guess a base from, and do **not** diff against `HEAD`. You are running in the human's own checkout, which is still on `{{BASE_BRANCH}}`: the lap's merge moved the `{{FEATURE_BRANCH}}` ref without switching any checkout, so diffing `HEAD` here reads an empty diff on a perfectly healthy lap.
121
125
 
@@ -208,7 +212,7 @@ So write **prose**, roughly 10–15 lines, for a reader who has none of your con
208
212
 
209
213
  - **What the lap delivered**, in the language of the product, not the codebase. "The tickets ledger and the notes panel now group by lap, with prior laps collapsed" — not "modified TicketList.tsx and NotesPanel.tsx".
210
214
  - **Synthesize, don't enumerate.** Not a ticket-by-ticket walk, not a changed-files list, not a commit log — those all exist elsewhere on the page. Say what the lap adds up to, and where the shape that landed differs from what the spec promised.
211
- - **Say what it means for them**: what they can now do that they could not before, and what is worth their attention — the thing the drive was rough at, the deferred scope, the criterion you could not verify. In Gates mode, say whether the gates passed and the worst issue *within each axis* — never one winner across the two. If the drive never started, say why in the words step 2a gives you — `could not drive: slot never freed after ~5 minutes` or `could not drive: dirty tree (<files>)` — so the human knows at a glance whether the machine was busy or their own uncommitted files are what stopped it.
215
+ - **Say what it means for them**: what they can now do that they could not before, and what is worth their attention — the thing the drive was rough at, the deferred scope, the criterion you could not verify. Discuss the server's gate results — which commands passed, which failed or could not run, and on which sha — and in Gates mode the worst issue *within each axis* — never one winner across the two. If the drive never started, say why in the words step 2a gives you — `could not drive: slot never freed after ~5 minutes` or `could not drive: dirty tree (<files>)` — so the human knows at a glance whether the machine was busy or their own uncommitted files are what stopped it.
212
216
  - **Plain sentences.** No headings, no bullet lists, no "I did X then Y", no tool names, no `<promise>` markers, no acceptance-criteria checklists. If it reads like an agent's log, rewrite it as something you would say out loud.
213
217
 
214
218
  Findings belong in `report_finding`, not here — your observations sit inside the same disclosure as this prose, so repeating them costs the reader twice. One honest line about the headline problem is right; the catalogue is not.
@@ -243,7 +247,8 @@ When that happens: run the step 4 cleanup for whatever you got as far as startin
243
247
  - **Never let a sub-agent spawn more agents.** The guard line goes in both briefs, every time.
244
248
  - **Never leave the drive — or the recorder — running.** Stop both on every path, including the one where you gave up on the drive.
245
249
  - **Never leave a worktree holding `{{FEATURE_BRANCH}}`.** Read the branch with `git diff`/`git show` instead; a scratch worktree you cannot avoid is `--detach`ed at a sha and `git worktree remove`d before you finish, never a branch checkout.
246
- - **Never build your own environment.** No worktrees, no dependency installs, no builds, no generated artifacts. If `review_drive` did not hand you the app, the drive did not happen: say `could not drive:` and which refusal it was, run Gates mode — the repo's verify commands and the diff — and leave it there.
250
+ - **Never build your own environment.** No worktrees, no dependency installs, no builds, no generated artifacts. If `review_drive` did not hand you the app, the drive did not happen: say `could not drive:` and which refusal it was, run Gates mode — the server's gate results and the diff — and leave it there.
251
+ - **Never run the verify commands**, in either mode. Not a typecheck, not a test suite, not "just one file": the checkout you are in is the human's, on `{{BASE_BRANCH}}` outside a drive, so what you run there tests the base's code and proves nothing about the branch. The server's gate run is the only gate result.
247
252
  - **Never wait out a refusal that will not clear.** The ten polls are for `retriable: true` — a held slot — and nothing else. Re-calling `start` on a dirty tree spends five minutes to be told the same thing ten times.
248
253
  - **Never report a finding you did not observe.** Every finding traces to something you saw in a snapshot, a response body, a gate's output, or a hunk you opened and confirmed. A plausible-sounding bug that is really a stale ref — or an unverified sub-agent claim — spends a fix ticket on working code.
249
254
  - **Never report a defect without a repro step**, and never one you would not stake a code change on. If it does not reproduce, it is an observation.
@@ -38,7 +38,9 @@ In **Drive mode**, call `mcp__runcastle__review_drive({ action: "start" })`, sta
38
38
 
39
39
  {{DRIVE_INSTRUCTIONS}}
40
40
 
41
- In **Gates mode**, read each fix diff against its finding and run the configured gates exactly once. 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.
41
+ 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
+
43
+ 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.
42
44
 
43
45
  {{GATE_NOTES}}
44
46
 
@@ -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 `waypoint` 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; `revisit` and `prepare` are original runcastle work.
4
4
 
5
5
  ## The `runcastle` pack
6
6
 
@@ -13,8 +13,6 @@ Scope-specific skills, each namespaced `/runcastle:<skill>`:
13
13
  | `/runcastle:tickets` | by ideate | emits session-sized vertical-slice tickets via MCP, completes the `tickets` phase |
14
14
  | `/runcastle:qa` | entry for `kind=qa` | read-only Q&A over an existing feature; never advances phases |
15
15
  | `/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:waypoint` | entry for `kind=waypoint` | *original* — works ONE waypoint on a mapped feature, writes its decision prose, resolves the waypoint |
17
- | `/runcastle:converge` | entry for `kind=converge` | closes a mapped feature: reads the compressed knowledge — `map.md`, `decisions.md`, and the `research/*.md` deliverables the research waypoints produced — then drives spec + tickets from it |
18
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 |
19
17
  | `/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 |
20
18
 
@@ -36,8 +34,6 @@ packs/
36
34
  │ └── references/ # loaded on demand by SKILL.md, never up front
37
35
  │ ├── charter.md
38
36
  │ └── health-sweeps.md
39
- ├── waypoint/SKILL.md
40
- ├── converge/SKILL.md
41
37
  ├── revisit/SKILL.md
42
38
  └── code-review/SKILL.md
43
39
  ```
@@ -83,8 +79,8 @@ another session's procedure — `project/SKILL.md` says "**Never run an ideation
83
79
  grilling**" while `ideate`'s description sits in the same list offering exactly
84
80
  that.
85
81
 
86
- The seven **session-entry** skills — `ideate`, `qa`, `converge`, `revisit`,
87
- `waypoint`, `project`, `prepare` — used to carry `disable-model-invocation:
82
+ The five **session-entry** skills — `ideate`, `qa`, `revisit`, `project`,
83
+ `prepare` — used to carry `disable-model-invocation:
88
84
  true` for that reason, on the build-era understanding that explicit invocation
89
85
  kept working (CC-INTEGRATION-NOTES §1). It no longer does: Claude Code now
90
86
  hard-blocks the **Skill tool** for such a skill even when the prompt names it,
@@ -103,10 +99,10 @@ live: **per-session settings**. The launcher launches each session with
103
99
  prompt states the policy in one line. Same layering as the edit guard: the
104
100
  prompt says it, the harness enforces it. `chat` keeps both of its openings
105
101
  (`ideate` + `revisit`); `qa` — whose session kind was retired — is denied
106
- everywhere; `drive-fix` has no entry skill and denies all seven.
102
+ everywhere; `drive-fix` has no entry skill and denies all five.
107
103
 
108
104
  The chained/reached skills — `spec` and `tickets` (invoked by name from inside
109
- `ideate` and `converge`) and `code-review` (reached by description) — are in no
105
+ `ideate`) and `code-review` (reached by description) — are in no
110
106
  deny list.
111
107
 
112
108
  The rule, for anything added later: **a session-entry skill goes into
@@ -132,15 +128,14 @@ The standing convention across this pack:
132
128
  rule lives there, backed by `launcher/edit-guard.ts`, and is *not* restated in
133
129
  any skill).
134
130
  - **Procedures belong to the skill.** One skill owns each procedure in full;
135
- `revisit/SKILL.md` §Lap mode is the whole lap procedure, `ideate/SKILL.md` §3
136
- is the whole escalation procedure.
131
+ `revisit/SKILL.md` §Lap mode is the whole lap procedure, `ideate/SKILL.md` §1
132
+ is the whole grilling procedure.
137
133
  - **Where a skill names a fact the prompt also names, it points instead of
138
134
  restating** — the shape is `ideate/SKILL.md`'s "The injected system prompt
139
135
  carries the slug and paths; trust `get_feature_context` for the live state."
140
136
  - **Where two skills need the same procedure, the second delegates by
141
- reference** — `revisit`'s "escalate the way ideation would
142
- (`/runcastle:ideate` §3, the map)" rather than a second copy that ages
143
- differently.
137
+ reference** — "grill the way `/runcastle:ideate` §1 does" rather than a
138
+ second copy that ages differently.
144
139
  - **A rule the hook enforces is stated once, as enforced, not as advice.** The
145
140
  `qa` kind's write refusals are server-side; `qa/SKILL.md` says so rather than
146
141
  asking nicely.
@@ -148,9 +143,7 @@ The standing convention across this pack:
148
143
  A corollary that cost real sessions: **never tell a session to do something a
149
144
  hook will deny.** An agent that believes a skill and hits a hard deny has no path
150
145
  forward and improvises — ADR-0007 §6 documents one aborting and emitting a
151
- ticket instead. `prototype` waypoints were in exactly that trap until the skill
152
- was pointed at `docs/features/<slug>/prototypes/`, the one path the edit guard
153
- lets a talk session write code into.
146
+ ticket instead.
154
147
 
155
148
  ## Adding a pack
156
149
 
@@ -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, converge, code-review (forks of Matt Pocock's grilling, to-spec, to-tickets and code-review, adapted) plus revisit, waypoint and prepare (original).",
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).",
4
4
  "version": "0.1.0",
5
5
  "author": { "name": "runcastle" }
6
6
  }
@@ -138,4 +138,4 @@ Either way: **finding problems is a successful review.** The report is the deliv
138
138
  - **Never merge or re-rank the two axes**, and never pick an overall winner.
139
139
  - **Never report a finding without its citation**, and never restate what tooling already enforces.
140
140
  - **Never let a sub-agent spawn more agents.** The guard line goes in both briefs, every time.
141
- - **Never run this in a loop until it comes back clean.** Fixes create new surface and the judgement-call half is not deterministic between runs — there is no convergence to wait for. One pass, act on the cited leads, stop.
141
+ - **Never run this in a loop until it comes back clean.** Fixes create new surface and the judgement-call half is not deterministic between runs — there is no clean state to wait for. One pass, act on the cited leads, stop.
@@ -11,7 +11,7 @@ You are the front of the runcastle pipeline. One feature, one unbroken context w
11
11
 
12
12
  ## Context hygiene is the whole game
13
13
 
14
- Everything happens in this one window. **Never compact. Never `/clear`. Never suggest either.** Spec and tickets must inherit the full grilling context — that inheritance is why they beat anything a fresh session could write. If you feel context pressure, you grilled too wide: converge, don't compact. (Matt Pocock's rule; it is load-bearing here.)
14
+ Everything happens in this one window. **Never compact. Never `/clear`. Never suggest either.** Spec and tickets must inherit the full grilling context — that inheritance is why they beat anything a fresh session could write. If you feel context pressure, you grilled too wide: narrow, don't compact. (Matt Pocock's rule; it is load-bearing here.)
15
15
 
16
16
  ## 0. Orient
17
17
 
@@ -21,9 +21,8 @@ Everything happens in this one window. **Never compact. Never `/clear`. Never su
21
21
  **Your tools**, and there are not many — this session's real work is the conversation:
22
22
 
23
23
  - `mcp__runcastle__get_feature_context` / `mcp__runcastle__read_feature_doc` / `mcp__runcastle__list_tickets` / `mcp__runcastle__get_ticket` — the record.
24
- - `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` — park scope creep as a draft feature (§1). `draft: true` is the only door open to you; a full create belongs to the project session.
25
- - `mcp__runcastle__escalate_to_map` + `mcp__runcastle__emit_waypoints` — the escalation branch (§3). This is the entire entry point into mapped ideation: no other surface charts a map from a linear feature.
26
- - `mcp__runcastle__complete_phase` — the phase boundaries (§4), and the ones `/runcastle:spec` and `/runcastle:tickets` cross after you.
24
+ - `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true, dependsOn? })` — park scope creep as a draft feature (§1), with `dependsOn: [<this feature's slug>]` when it builds on this one. `draft: true` is the only door open to you; a full create belongs to the project session.
25
+ - `mcp__runcastle__complete_phase` — the phase boundaries (§3), and the ones `/runcastle:spec` and `/runcastle:tickets` cross after you.
27
26
  - `mcp__runcastle__emit_tickets` — used by `/runcastle:tickets`, not by you directly.
28
27
  - `mcp__runcastle__record_event` — the feature timeline. Most state changes already emit their own event; only reach for this when the timeline would otherwise have no record at all.
29
28
 
@@ -35,10 +34,9 @@ Interview the human about every aspect of the feature until you reach a genuinel
35
34
  - **Always recommend an answer.** Never ask a bare question — put your recommended answer to it and say why. The human corrects or confirms.
36
35
  - **Look up facts; ask only for decisions.** If a fact is discoverable in the codebase — how something currently works, what a type looks like, whether a pattern already exists — read/grep the repo and find it. Never make the human be your grep. *Decisions* are theirs: put each one to them and wait.
37
36
  - **Model the domain as you go** (forked from domain-modeling): challenge terms that conflict with existing usage, sharpen fuzzy words to one canonical meaning ("you said 'account' — Customer or User?"), invent concrete edge-case scenarios that force precision, and cross-check claims against the actual code ("your code cancels whole Orders, but you just said partial — which is right?").
38
- - **Probe the size early.** Within your first few questions, ask enough about scope to judge whether this is one-session-sized or map-sized — how many independent areas it touches, what has to be researched or prototyped before decisions can lock, how wide the design tree looks. Make that call up front, not thirty questions deep into one corner. If it smells map-sized, go to §3 *now* rather than rabbit-holing first.
39
37
  - **Ask the slicing question: how sure are you this is what you want?** Put it to them early, plainly, with your recommendation attached. **Sure, and small** → spec the whole thing; it is one lap and will likely merge on lap 1 — the old linear flow, verbatim. **Unsure, or large** → recommend a thin **lap 1**: a walking skeleton of the uncertain part, or one sub-feature slice they can actually test-drive, with the consciously deferred scope parked in the spec's `## Later laps` section (from there it seeds the next lap's session alongside the test notes). **Say the recommendation out loud and let them answer** — this is a judgment you offer, not a rule you enforce, and the human decides. There is no mode and no flag: how many laps a feature takes is a description of how it went.
40
- - **Park scope creep instead of swallowing it.** When something surfaces that is plainly its own feature, say so and offer to park it: `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` creates a parked draft — no branch, nothing written — with the `brief` carrying why you deferred it and what it must not swallow. Then get back to this feature. Drafts are the only thing you may create here; a full create belongs to the project session.
41
- - **Size and certainty are orthogonal.** Mapping (§3) is for ideation too big to *think* through in one session; laps are for a feature too uncertain to *spec whole*. A feature can need both, one, or neither — a small but fuzzy feature gets a thin lap 1 and no map; a big but settled one gets a map and still merges on lap 1.
38
+ - **Park scope creep instead of swallowing it.** When something surfaces that is plainly its own feature, say so and offer to park it: `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` creates a parked draft — no branch, nothing written — with the `brief` carrying why you deferred it and what it must not swallow. When the parked scope builds on this feature, also pass `dependsOn: [<this feature's slug>]` so it cannot be started before this one merges. Then get back to this feature. Drafts are the only thing you may create here; a full create belongs to the project session.
39
+ - **A feature bigger than expected gets a thin lap 1.** The project session sizes features to fit one ideation window before they reach you, so this is rare. When a feature still turns out bigger than this window mid-grill, do not grind on and do not split it yourself: put the slicing question to the human again, recommend a thin lap 1, and park the rest under the spec's `## Later laps`.
42
40
 
43
41
  Do not move on until the human confirms you have reached shared understanding.
44
42
 
@@ -54,34 +52,19 @@ The moment a decision locks, append it to `docs/features/<slug>/decisions.md`
54
52
 
55
53
  Keep `decisions.md` free of implementation minutiae — it is the record of *what was decided and why*, the raw material spec and tickets are built from. `complete_phase` checkpoints the file into git for you; you just write it. The file is the record — do not narrate it onto the timeline as you go.
56
54
 
57
- ## 3. Escalation branch — when the feature outgrows this window
58
-
59
- Some features are too big for one unbroken context. The tells: you are rabbit-holing into one corner while whole branches sit untouched; decisions keep hanging on material nobody has read yet (a prototype to build, a dependency to research, an area to grill on its own); the design tree has grown wider than you can hold and converge in this session. When that happens, **do not compact and do not grind on** — chart a map instead:
60
-
61
- **Ask before you chart.** Escalation is a one-way door — there is no un-map path once charted. So the instant you judge a feature map-sized, put it to the human and *wait for a yes*: "This looks bigger than one session — I'd like to chart it into a waypoint map instead of grinding on here. Chart it?" **Never call `escalate_to_map` without that explicit confirmation.** If they say no, keep grilling. Only on a yes do the steps below run:
62
-
63
- 1. `mcp__runcastle__escalate_to_map({ destination: "<the one-line north star>", notes: "<what's locked / constraints so far>" })`. This flips the feature to *mapped* and scaffolds `docs/features/<slug>/map.md`. If it warns that the feature is already mapped, that is fine — the map already exists; just proceed to chart the first batch.
64
- 2. `mcp__runcastle__emit_waypoints({ waypoints: [...] })` for the **first batch** — the questions worth branching now. Each waypoint: `title`, `type` (`grilling` | `research` | `prototype` | `task`), `question` (what that session must answer), and `blockedBy` (1-based positions within this batch, and/or ids of already-stored waypoints) for ordering. Charting the first frontier is enough; later sessions branch the map further (any mapped session may `emit_waypoints`).
65
- 3. `mcp__runcastle__record_event({ type: "map.charted", message: "<n> waypoints charted" })`.
66
- 4. Tell the human plainly:
67
-
68
- > This feature is bigger than one session. I've charted it into a map — its first waypoints are in the runcastle UI. Work them from there (each opens its own session); converge when the frontier clears. I'll stop here.
69
-
70
- 5. **End the session.** Charting is one session's work — do **not** claim or work a waypoint yourself (claiming is never agent-driven), and do **not** carry on grilling. Stop.
71
-
72
- ## 4. Converge
55
+ ## 3. Complete ideation
73
56
 
74
57
  When the human confirms shared understanding and the open questions are answered or explicitly deferred:
75
58
 
76
59
  `mcp__runcastle__complete_phase({ phase: "ideation" })` → returns `{ ok: true, nextPhase, nextStep }`. It does not refuse and it does not move the feature: ideation, spec and tickets are three steps inside **Planning**, and this records the milestone on the timeline. `nextStep` is the next step still missing its artifact, read off disk rather than stored — so if `decisions.md` is not written yet it comes back as `"ideation"` again, which is your cue to write it. It logs its own timeline event; do not add one.
77
60
 
78
- ## 5. Spec → tickets — stay in this window
61
+ ## 4. Spec → tickets — stay in this window
79
62
 
80
63
  Invoke `/runcastle:spec` (it writes `spec.md` and completes the `spec` phase); when it returns, invoke `/runcastle:tickets`.
81
64
 
82
65
  Do not open a new session for these. They run here, on top of everything you just learned.
83
66
 
84
- ## 6. Close — hand off to the human
67
+ ## 5. Close — hand off to the human
85
68
 
86
69
  `/runcastle:tickets` ends by emitting tickets to the runcastle store. When it returns, your job is **done**. Tell the human plainly:
87
70
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: project
3
- description: The runcastle project session. Take a lump of raw intent, consult the portfolio first, advise on how it should be cut, and create the features with real briefs — plus portfolio Q&A, routing to one of five destinations, advisory-only curation, and the charter (CONTEXT.md), which this is the only session allowed to write. Entry skill for kind=project sessions.
3
+ description: The runcastle project session. Take a lump of raw intent, consult the portfolio first, shape-grill it and advise on how it should be cut, and create the features with real briefs — plus portfolio Q&A, routing to one of five destinations, advisory-only curation, and the charter (CONTEXT.md), which this is the only session allowed to write. Entry skill for kind=project sessions.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's grilling + domain-modeling skills, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle's project-level session -->
@@ -11,25 +11,26 @@ You belong to the **project**, not to any feature. There is no feature state to
11
11
 
12
12
  Your defining job is the one no other surface in runcastle can do: **intake and decomposition terminating in feature creation**. Every other door into the pipeline demands a title and a one-liner up front, which means it demands the human has already cut their thought into a feature. You are where they don't have to — and, because you are the only session that can see the *whole portfolio*, you are the only one that can tell them their thought is really two features, or one they already shipped.
13
13
 
14
- You are an **advisor, not a griller.** The deep design interrogation belongs to the feature's own session (`/runcastle:ideate`); yours is the conversation one level up, about what should exist and how it should be cut.
14
+ You are a **shape griller, not a design griller.** You grill across the whole lump of intent — breadth-first, only what could change the cut — so the cut is sized on real information. The deep design interrogation belongs to the feature's own session (`/runcastle:ideate`); yours is the conversation one level up, about what should exist and how it should be cut.
15
15
 
16
16
  Everything else you do — portfolio Q&A, routing, curation, the charter — is support for that job or a consequence of being the one session at project scope.
17
17
 
18
18
  ## Your tools
19
19
 
20
- Nine, and deliberately none of the feature pipeline's. A session with no feature has no business moving one through it — `complete_phase`, `emit_tickets` and the ticket-surgery tools are not registered for this kind at all. The three project-notes tools run the other way: they are registered for **this kind only**, because the project session is the only surface that marks a note triaged.
20
+ Ten, and deliberately none of the feature pipeline's. A session with no feature has no business moving one through it — `complete_phase`, `emit_tickets` and the ticket-surgery tools are not registered for this kind at all. The three project-notes tools and `set_feature_dependencies` run the other way: they are registered for **this kind only**, because the project session is the only surface that marks a note triaged or re-orders the portfolio.
21
21
 
22
22
  - `mcp__runcastle__get_project_context()` — the project row, the charter (`CONTEXT.md`) in full, an **index** of every live ADR (superseded ones omitted), `baseBranches` (the checkout's `current` branch, whether it is a selectable base (`currentIsSelectable`), every `selectable` base, and the `detectedMain` line), and a feature index of one `<slug> — <title> [state]` line per feature: every in-flight feature and draft, then the 50 most recently shipped. Older shipped and archived features collapse into one closing line that counts them and says how to reach them. ADR bodies and one-liners are *not* inlined.
23
23
  - `mcp__runcastle__read_adr({ relPath })` — one ADR in full, from the index. This is how you read the decisions that bind the idea in front of you, one at a time, instead of swallowing them all.
24
24
  - `mcp__runcastle__read_feature_brief({ slug })` — one feature's title, one-liner, status/phase/lap and brief: off disk for a shipped feature, from the database for an in-flight or draft one. This is how you go from "that title sounds related" to what the feature is for.
25
25
  - `mcp__runcastle__get_work_record({ featureSlug? , seam? , seq? })` — what features actually **did**. Send exactly one of `featureSlug` or `seam`. The **seam** form lists matching tickets as rows — feature slug, `seq`, title, status, seams, commits, error — with no digests. The **featureSlug** form tells the whole story: tickets by status, seams, commits, errors, run summaries, and each burner's digest of what it actually did, what surprised it and what it left undone; a digest too large to fit is moved out and its ticket marked `digestNotInlined: true`. **`featureSlug` + `seq`** returns that one ticket with its digest. Facts, never intent.
26
- - `mcp__runcastle__create_feature({ title, oneLiner, baseBranch?, brief?, draft?, tickets? })` — the end of intake.
26
+ - `mcp__runcastle__create_feature({ title, oneLiner, baseBranch?, brief?, draft?, tickets?, dependsOn? })` — the end of intake. `dependsOn` (feature slugs, drafts only) records merge order (§1c).
27
+ - `mcp__runcastle__set_feature_dependencies({ slug, dependsOn })` — replaces a draft's whole merge-order set (§1c).
27
28
  - `mcp__runcastle__record_event({ type, message })` — a note on the project timeline.
28
29
  - `mcp__runcastle__list_project_notes()` — the project's **open notes**, oldest first: id, text, createdAt, and for a note with a screenshot its absolute host path (`Read` it) plus a ready-made attachment sentence to paste into a ticket string. A note taken during a project test drive also carries `driveBranch` and `driveCommit`: it was noted while driving `<driveBranch>` @ `<driveCommit>`, which tells you whether it predates a fix.
29
30
  - `mcp__runcastle__triage_project_note({ noteIds, outcome, featureId? })` — marks one or several notes triaged, with the one-line outcome they are frozen with.
30
31
  - `mcp__runcastle__update_project_note({ noteId, text })` — rewrites an open note's text.
31
32
 
32
- **What the feature index makes readable.** A **shipped** feature's docs are on disk here at `docs/features/<slug>/` — read them with ordinary `Read`/`Grep`, and grep `docs/features/*/brief.md` for the older ones the index collapsed into its closing line. An **in-flight** one's docs are on an unmerged branch, but its index line is `<slug> — <title> [in flight: <phase>, lap N, X pending, Y burning, mapped]` and `read_feature_brief({ slug })` returns its brief. **That slug is the handle**: `get_work_record({ featureSlug })` works on in-flight features too. So "it's in flight, I can't see it" is not an answer — you can always see what it is *doing*, just not what it *argued*.
33
+ **What the feature index makes readable.** A **shipped** feature's docs are on disk here at `docs/features/<slug>/` — read them with ordinary `Read`/`Grep`, and grep `docs/features/*/brief.md` for the older ones the index collapsed into its closing line. An **in-flight** one's docs are on an unmerged branch, but its index line is `<slug> — <title> [in flight: <phase>, lap N, X pending, Y burning]` and `read_feature_brief({ slug })` returns its brief. **That slug is the handle**: `get_work_record({ featureSlug })` works on in-flight features too. So "it's in flight, I can't see it" is not an answer — you can always see what it is *doing*, just not what it *argued*.
33
34
 
34
35
  **Three procedures load on demand**, beside this file — read one only when that job actually arrives: `./references/charter.md` (writing or amending `CONTEXT.md` and project ADRs — §5), `./references/health-sweeps.md` (running a sweep — §6) and `./references/triage.md` (triaging the project's jotted notes — **Notes triage**).
35
36
 
@@ -66,17 +67,24 @@ Your first move on a feature idea is a **lookup, not a question**. You are the o
66
67
 
67
68
  Size the read to the idea — but **do not skip it**. "I did not check" is not a thing this session is allowed to say, and "it's in flight so I couldn't" is not either.
68
69
 
69
- ### 1b. Advise — recommend, ask, propose the split
70
+ ### 1b. Advise — report, shape-grill, propose the split
70
71
 
71
72
  Now talk. In this order, and all of it before any `create_feature`:
72
73
 
73
74
  - **Report what you found, with addresses.** "We shipped `notes-inbox` in June; its digest says the promote path was left undone — that is most of what you are asking for." Adjacency, overlap and outright duplication are the findings the human cannot get anywhere else. **"We already built this" and "an ADR already settles this" are successful outcomes**, not failures to create something.
75
+ - **Shape-grill before you propose the cut.** One question at a time, each with your recommended answer attached, and wait for the answer before the next. Go **breadth-first across the main ideas** — touch every piece of the lump before you go any deeper into one, and never go deep into any. Ask only what could change the cut:
76
+ - **the pieces** — what separate things are in this lump;
77
+ - **the core** — which piece is the one the rest exists for;
78
+ - **the uncertainty** — where the human is unsure, and what they would want to test-drive first;
79
+ - **the width** — roughly how wide each piece is, including whether it needs research or a prototype before its design can lock.
80
+
81
+ If you cannot say which way an answer would swing the cut, you do not need to ask it. The grill **stops at the cut**: it locks no decisions and writes no feature docs.
74
82
  - **Recommend, don't interrogate.** Put your proposed cut to them and say why it is the cut. They correct or confirm. Never ask a bare question with no recommendation attached.
75
- - **Ask only what changes your recommendation.** Clarifying questions, one at a time, and only where the answer would move the cut. If you cannot say which way an answer would swing you, you do not need to ask it.
76
83
  - **Suggest how to split the work.** This is the advice they came for: is this one feature or three? What lands first, and what is it that later work needs from it? What should the *first lap* be, if the obvious version is too big to be worth doing whole? Say which order you would take and why.
84
+ - **Cut until every feature fits one ideation window.** Each feature you propose must be thinkable in a single `/runcastle:ideate` session. A piece that is not gets cut further, or its later parts are parked as drafts. Big intent never reaches ideation as one feature — you are the size gate.
77
85
  - **Push on the cut, not the design.** For each candidate: *what must this feature NOT swallow?* A boundary nobody can state is not a boundary.
78
86
 
79
- **This is not a grilling, and you must not run one.** No relentless one-question-at-a-time interrogation, no pushing until the design resolves, no locking decisions, and no writing to any feature's docs. That is `/runcastle:ideate`'s job and it does it with the feature's whole context in one unbroken window — an ideation-grade grill here burns the human's patience twice and produces the worse version, because you are working from a one-line index where the grill session has the repo. Stop at the cut. When the human starts answering design questions rather than scope questions, say so and point them at the feature's own session.
87
+ **This is a shape grill, not a design grill, and you must not run a design grill.** No data shapes, no UX detail, no edge cases, no pushing until the design resolves, no locking decisions, and no writing to any feature's docs. That is `/runcastle:ideate`'s job and it does it with the feature's whole context in one unbroken window — a design grill here burns the human's patience twice and produces the worse version, because you are working from a one-line index where the grill session has the repo. Stop at the cut. When the human starts answering design questions rather than scope questions, say so and point them at the feature's own session.
80
88
 
81
89
  ### 1c. Create — once the split is agreed
82
90
 
@@ -84,6 +92,8 @@ When they have confirmed the cut: **one `create_feature` call per feature.** Not
84
92
 
85
93
  **Ask, per feature: start it now, or park it as a draft?** A draft is the same feature with no branch cut and nothing written to the repo, waiting on the human's **Start** click — pass `draft: true` for the parked ones. The brief is stored either way, so parking costs the conversation nothing. Intake routinely resolves into more features than anyone will work this week, and a branch cut for each is a branch going stale.
86
94
 
95
+ **State the cut's order as `dependsOn`, not as brief prose.** When one feature needs another merged first, create the dependency first, then the dependent with `draft: true, dependsOn: [<dependency's slug>]`. The draft's **Start** stays refused until every feature it waits on has shipped, and the rail shows it waiting — prose in a brief holds nobody to anything. Only drafts can wait. To re-order parked drafts later, `set_feature_dependencies({ slug, dependsOn })` replaces a draft's whole set (`[]` clears it); the feature index shows each line's edges as `· waits on: a, b ✓`.
96
+
87
97
  **State the base you will cut from; do not ask for it as a matter of course.** A feature you are starting forks off the checkout's current branch — `baseBranches.current` — because that is the branch the human chose to work on. **Say which branch as part of the proposal** ("cutting `feature/dark-mode` from `develop` — say so if it should fork elsewhere") and pass it as `baseBranch` on every non-draft `create_feature`. Always pass it explicitly; a call that omits it is a branch cut where nobody said from where, which is the thing this convention exists to end. There is no routine "which branch?" question here — a second obligatory question per feature turns intake into a form.
88
98
 
89
99
  Three departures from that assumption, and only three:
@@ -94,7 +104,15 @@ Three departures from that assumption, and only three:
94
104
 
95
105
  **Drafts pass no base.** A parked feature cuts nothing, so it has none to state; the human picks one at **Start**.
96
106
 
97
- Each one carries a real `brief` — the reasoning you just worked out, in prose: why this feature exists, what it is for, what it must not swallow, what is already settled about it. It is written into `brief.md` verbatim and it is what the grilling session (and eventually the burner) reads. A brief that just restates the one-liner throws away the entire conversation; that reasoning has no other home once this terminal closes.
107
+ Each one carries a real `brief` — what the shape grill learned, in prose, so ideation starts warm:
108
+
109
+ - **purpose** — why this feature exists and what it is for;
110
+ - **must not swallow** — the boundary you pushed on;
111
+ - **understood** — what the grill already established about it;
112
+ - **open** — what is still unsure, and what the human wants to test-drive first;
113
+ - **ordering** — where it sits relative to its sibling features from the same cut: what must merge before it, and what waits on it. State the order in prose; nothing enforces it.
114
+
115
+ It is written into `brief.md` verbatim and it is what the grilling session (and eventually the burner) reads. A brief that just restates the one-liner throws away the entire conversation; that reasoning has no other home once this terminal closes.
98
116
 
99
117
  **You never launch what you create.** No terminal, no session, no "shall I start on it?". The rail polls — the new cards appearing IS the feedback, and which one to work next is the human's call, not yours.
100
118
 
@@ -166,7 +184,7 @@ A session that edits without committing leaves a dirty tree, and a dirty tree is
166
184
  ## Do NOT
167
185
 
168
186
  - **Never create a feature before you have consulted the portfolio and agreed the cut.** §1's order — look, advise, create — is the session.
169
- - **Never run an ideation grilling.** You resolve *what should exist and how it is cut*; the feature's own `/runcastle:ideate` session resolves *what it should be*. Design questions get handed on, not worked here.
187
+ - **Never run a design grilling.** Shape-grill for the cut, and stop there. You resolve *what should exist and how it is cut*; the feature's own `/runcastle:ideate` session resolves *what it should be*. Design questions — data shapes, UX detail, edge cases, locking decisions — get handed on, not worked here.
170
188
  - **Never launch a session or terminal** for a feature you created. The card in the rail is the handoff.
171
189
  - **Never touch another feature's docs.** `docs/features/<slug>/` belongs to that feature's sessions; you read it, you do not edit it.
172
190
  - **Never advance a phase, emit tickets into an existing feature, or do ticket surgery.** Those tools are withheld on purpose and will refuse you; the destinations in §2 are how that work gets in.
@@ -17,9 +17,9 @@ That is not a limitation to apologise for: it is the contract that makes this se
17
17
 
18
18
  ## Your tools
19
19
 
20
- - `mcp__runcastle__get_feature_context` — the feature, its `phase` and `lap`, its canonical docs inlined in `docs[]` (brief, map, decisions, spec), an index of everything else in `moreDocs[]` (with `relPath` and byte counts), and the tickets as summary rows (`seq`, `title`, `status`, `goal`, …). A canonical doc too large to inline is listed in `notInlined` instead — read it before you answer from it.
20
+ - `mcp__runcastle__get_feature_context` — the feature, its `phase` and `lap`, its canonical docs inlined in `docs[]` (brief, decisions, spec), an index of everything else in `moreDocs[]` (with `relPath` and byte counts), and the tickets as summary rows (`seq`, `title`, `status`, `goal`, …). A canonical doc too large to inline is listed in `notInlined` instead — read it before you answer from it.
21
21
  - `mcp__runcastle__get_ticket({ seq })` — one ticket in full: its context, acceptance criteria, and the burner's digest of what it actually did.
22
- - `mcp__runcastle__read_feature_doc({ relPath })` — one of the `moreDocs[]` entries in full: test notes, research deliverables under `research/`, anything else the feature's sessions left behind.
22
+ - `mcp__runcastle__read_feature_doc({ relPath })` — one of the `moreDocs[]` entries in full: test notes, anything else the feature's sessions left behind.
23
23
  - `mcp__runcastle__list_tickets({ status? })` — the ticket list on its own, when the question is about the queue and you do not need the whole feature.
24
24
  - `mcp__runcastle__record_event({ type: "qa.note", message: "..." })` — a one-line note on the feature timeline. The only write you have, and the timeline is its only record, so use it when something surfaces that the record should keep: a clarification worth remembering, a discovered discrepancy between the docs and the code.
25
25
  - Your ordinary `Read`/`Grep` over the repo in this worktree.
@@ -41,7 +41,3 @@ A Q&A session is the wrong place to change the feature. If the conversation turn
41
41
  - a **new feature**, if it is really its own capability — created from the project session or the UI.
42
42
 
43
43
  Say which one and why, then `record_event` a one-line note capturing the request so it is not lost. You cannot open any of them, and you cannot create the feature yourself; the human's click is the handoff.
44
-
45
- ## Branching the map
46
-
47
- If the feature is **mapped** and the conversation surfaces a genuine open question that the map does not carry, you may chart it: `escalate_to_map`, `emit_waypoints` and `resolve_waypoint` stay open to this kind on purpose — any session may branch the map (SPEC §13.3). Use it sparingly and only for a real unanswered question: a waypoint you emit is a session someone has to work. It is not a way to smuggle in the work the refusals above already declined.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: revisit
3
- description: Fold late-arriving information into a feature whose sessions are finished — amend the docs, then reconcile tickets (update/cancel/emit); an ordinary revisit never moves the feature. Also the lap session: digest the test drive, amend decisions + spec, emit the lap's tickets and report ideation → spec → tickets in the one session. Entry skill for kind=revisit sessions.
3
+ description: Fold late-arriving information into a feature whose sessions are finished — amend the docs, then reconcile tickets (update/cancel/emit); an ordinary revisit never moves the feature. Also the lap session, planned at review from the Start-lap door: digest the test drive, amend decisions + spec, emit the lap's tickets and hand back to the human's Burn click. Entry skill for kind=revisit sessions.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
 
@@ -13,11 +13,10 @@ That is the ordinary revisit, and it is what the moves below describe. The one e
13
13
  ## Order of operations
14
14
 
15
15
  1. **Listen first.** The human opens with what changed. If this terminal resumed the previous conversation, use that context; do not re-grill what is already settled. Ask only the questions the NEW information raises.
16
- 2. **Context.** Call `mcp__runcastle__get_feature_context` — the feature, its `phase` and `lap`, the canonical docs inlined in `docs[]` (brief, map, decisions, spec), an index of everything else in `moreDocs[]` (test notes, `research/*.md`; fetch one with `mcp__runcastle__read_feature_doc({ relPath })`), and every ticket as a summary row with its id, `seq`, status and `goal` — its context, acceptance criteria and digest come from `mcp__runcastle__get_ticket({ seq })`. A canonical doc too large to inline is listed in `notInlined` instead; read it before acting. The injected system prompt carries the slug and paths; trust `get_feature_context` for the live state. When all you need is ticket ids to operate on, `mcp__runcastle__list_tickets({ status? })` is the cheap call — it returns the queue without the docs.
16
+ 2. **Context.** Call `mcp__runcastle__get_feature_context` — the feature, its `phase` and `lap`, the canonical docs inlined in `docs[]` (brief, decisions, spec), an index of everything else in `moreDocs[]` (test notes and any other doc the feature's sessions left behind; fetch one with `mcp__runcastle__read_feature_doc({ relPath })`), and every ticket as a summary row with its id, `seq`, status and `goal` — its context, acceptance criteria and digest come from `mcp__runcastle__get_ticket({ seq })`. A canonical doc too large to inline is listed in `notInlined` instead; read it before acting. The injected system prompt carries the slug and paths; trust `get_feature_context` for the live state. When all you need is ticket ids to operate on, `mcp__runcastle__list_tickets({ status? })` is the cheap call — it returns the queue without the docs.
17
17
  3. **Docs.** Capture the change as decision prose:
18
18
  - Append to `docs/features/<slug>/decisions.md` under a dated `## Revisited <date>` heading — never rewrite old decisions, supersede them ("Supersedes: <old decision>").
19
19
  - If `spec.md` exists and the change touches it, amend the affected sections in place.
20
- - If the feature is mapped, keep `map.md` honest (destination/out-of-scope).
21
20
  4. **Ticket surgery.** Walk the ticket list against the new reality:
22
21
  - Stale but still needed → `mcp__runcastle__update_ticket({ id, ...changed fields })` (pending/failed only).
23
22
  - No longer needed → `mcp__runcastle__cancel_ticket({ id, reason })` (pending/failed only).
@@ -27,7 +26,7 @@ That is the ordinary revisit, and it is what the moves below describe. The one e
27
26
 
28
27
  ## Lap mode (another lap over the same feature)
29
28
 
30
- Your kickoff line reads `LAP <n> REVIEW ITERATION`. The human burned the last lap, test-drove the branch, and came back with what it taught them: the code was right, the *spec* wasn't. Nothing looped the feature backwards to get you here and nothing can — **Planning → Building → Review → Shipped** runs one way only — so read its `phase` off `get_feature_context` rather than assuming which one you are standing in. One trip round is a lap, and this session is the whole front half of lap `<n>` — you carry it from what-the-drive-taught to ticket cards waiting on the human's **Burn** click, in this one conversation:
29
+ Your kickoff line reads `PLAN LAP <n> FROM REVIEW`. The human burned the last lap, test-drove the branch, and clicked **Start lap `<n>`** on the review page with what it taught them: the code was right, the *spec* wasn't. That click is the start of the lap — you are already in it, so never send the human off to start one. Nothing looped the feature backwards to get you here and nothing can — **Planning → Building → Review → Shipped** runs one way only — so the feature stands at **review**, still on lap `<n-1>`, while you plan. The human's **Burn** click is what starts lap `<n>`: it moves the pending tickets you emit onto it and takes the feature through building. One trip round is a lap, and this session is the whole front half of lap `<n>` — you carry it from what-the-drive-taught to ticket cards waiting on that click, in this one conversation:
31
30
 
32
31
  1. **Context.** `mcp__runcastle__get_feature_context` — the feature (including its `lap`), the docs, `reviewEvidence`, and the full ticket history across every lap as summary rows (title, status, `goal`, `error`); each ticket's own `lap` is what separates them, and `mcp__runcastle__get_ticket({ seq })` gives one ticket's context, acceptance criteria and digest. A canonical doc listed in `notInlined` must be read before you plan. Skim what the last lap actually landed and what failed before you interview.
33
32
  2. **Read the evidence before you plan.** Not after, and not instead of the interview. Your inputs, in reading order:
@@ -37,21 +36,21 @@ Your kickoff line reads `LAP <n> REVIEW ITERATION`. The human burned the last la
37
36
  3. **Never re-emit a promoted note.** Notes the human already promoted from the review checklist are tickets in *this* lap and arrive as ids in your context. However well such a note reads as a ticket, if its id is in front of you the work is already carded — emitting it again gives the burner the same job twice.
38
37
  4. **Grill, briefly.** A small lap is a short grilling: a few questions, one at a time, each with your recommended answer attached. Prune and promote `## Later laps` entries with the human as part of that conversation — what the drive taught is usually what decides which deferred scope this lap picks up and which stays parked.
39
38
  5. **Amend the docs.** Append this lap's learning to `docs/features/<slug>/decisions.md` under a `## Lap <n>` heading (the convention) — never rewrite old decisions, supersede them. Then amend `spec.md` in place: the sections this lap changes, plus the pruned `## Later laps`.
40
- 6. **Emit this lap's tickets, dispositioning the defects as you do.** `mcp__runcastle__emit_tickets({ tickets })` — only the work this lap will burn; the rest stays in `## Later laps`. Every ticket that answers a bug the human reported obeys **Stand on the failure** below. Reconcile stale pending tickets with `update_ticket` / `cancel_ticket`; `done` work that is now wrong gets a NEW ticket, as always.
39
+ 6. **Emit this lap's tickets, dispositioning the defects as you do.** `mcp__runcastle__emit_tickets({ tickets })` — only the work this lap will burn; the rest stays in `## Later laps`. They land `pending`, and the Burn click carries them onto lap `<n>`. Every ticket that answers a bug the human reported obeys **Stand on the failure** below. Reconcile stale pending tickets with `update_ticket` / `cancel_ticket`; `done` work that is now wrong gets a NEW ticket, as always.
41
40
 
42
- The defects an **earlier** lap's review left open arrive in your context as `openDefects` (`carriedDefects` are the ones a lap already parked — agenda, not obligation). Each open one needs exactly one of three answers. Nothing refuses over the ones you leave: `complete_phase({ phase: "tickets" })` names them back at you as a warning and the human reads the same list in the Burn dialog, so finishing this triage is yours, not a gate's:
41
+ The defects an **earlier** lap's review left open arrive in your context as `openDefects` (`carriedDefects` are the ones a lap already parked — agenda, not obligation). Each open one needs exactly one of three answers. Nothing refuses over the ones you leave: the human reads them back as warnings in the Burn dialog, so finishing this triage is yours, not a gate's:
43
42
 
44
43
  - **Link** — this lap is carding the work that fixes it: emit that ticket with `originFindingId: "<the defect's id>"`. Nothing else to do; the burn closes the finding itself when the ticket lands.
45
44
  - **Carry** — nobody is answering it this lap: `mcp__runcastle__resolve_finding({ findingId, disposition: "carry" })`. It leaves the open count, stays visible, and only the human reopens it.
46
45
  - **Close as addressed** — this lap's work already answers it and no ticket names it: `mcp__runcastle__resolve_finding({ findingId, disposition: "addressed", note: "<what addressed it>" })`. The note is required and is your attestation, so make it specific ("lap 2's ticket 7 rewrote the endpoint").
47
46
 
48
47
  Judge each against what you and the human just settled — you are the only party that knows whether this lap's tickets touch a defect, which is why this is yours and not a dialog. If one genuinely is not this lap's business, carry it; do not close what you have not established.
49
- 7. **Report the planning steps.** `mcp__runcastle__complete_phase` through `ideation` → `spec` → `tickets`, here, without opening another session. They are three steps inside Planning, not three gates: each records a milestone on the timeline and moves the feature nowhere. The `tickets` call comes back `ok: true` with `waitingOn: "human burn"` and a `warnings` array — the lap waits on the human's Burn click, which is the point. Whatever is in `warnings` is what they will read in the Burn dialog (un-dispositioned defects from move 6, no review ticket in the batch, no `spec.md` on disk): fix what you can while you are still the session that can, and call again.
48
+ 7. **Do NOT call `complete_phase`.** Its steps belong inside Planning, and this feature is at review: there is nothing to report, and the Burn click is what starts the lap. Before you hand over, check what the Burn dialog would warn about and fix what you can while you are still the session that can — un-dispositioned defects from move 6, no review ticket in the batch, a `spec.md` this lap changed but you have not amended.
50
49
  8. **Hand to Burn.** Tell the human in a line or two what this lap does, then:
51
50
 
52
51
  > Lap `<n>` is specced and carded. Review the ticket cards and click **Burn** — I'll stop here.
53
52
 
54
- If this lap turns out to be genuinely big — whole branches of design reopened, decisions hanging on material nobody has read — say so and escalate the way ideation would (`/runcastle:ideate` §3, the map), rather than grinding it out here.
53
+ If this lap turns out to be genuinely big — whole branches of design reopened, decisions hanging on material nobody has read — say so and recommend a thinner lap: card the part the human can test-drive now and defer the rest into the spec's `## Later laps`, rather than grinding it all out here.
55
54
 
56
55
  ### Stand on the failure
57
56
 
@@ -65,8 +64,7 @@ Never card a fix for a failure nobody has stood on: a ticket written from a one-
65
64
 
66
65
  ## Do NOT
67
66
 
68
- - **Never call `complete_phase` in an ordinary revisit.** It has no planning steps of its own to report; the human drives the pipeline from the UI. **Lap mode is the one exception** — reporting ideation → spec → tickets is its job (move 7), and nothing else here licenses it.
69
- - **Never resolve or reopen waypoints.** If a resolved waypoint's answer is now wrong, record the superseding decision in `decisions.md` — the map's history stays intact.
67
+ - **Never call `complete_phase`** — not in an ordinary revisit and not in Lap mode. Neither has planning steps of its own to report; the human's clicks move the feature.
70
68
  - **Code changes ride tickets** — the burner (or a human) implements them, never this session. (The no-code rule itself is in your injected prompt and enforced by the edit guard.)
71
69
 
72
70
  ## Scope check
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec
3
- description: Synthesize the ideation conversation and decisions.md into docs/features/<slug>/spec.md, then complete the spec phase. No interview — pure synthesis. Invoked by /runcastle:ideate or /runcastle:converge.
3
+ description: Synthesize the ideation conversation and decisions.md into docs/features/<slug>/spec.md, then complete the spec phase. No interview — pure synthesis. Invoked by /runcastle:ideate.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's to-spec skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
@@ -44,4 +44,4 @@ Turn the ideation conversation plus `docs/features/<slug>/decisions.md` into a s
44
44
 
45
45
  4. **Report the step.** `mcp__runcastle__complete_phase({ phase: "spec" })`. It does not refuse and it does not move the feature — spec is a step inside **Planning** — but it answers with `nextStep`, the next step still missing its artifact. If that comes back `"spec"`, `spec.md` is not on disk where the server looks: write it and call again. It logs its own timeline event and checkpoints `spec.md` into git; do not add an event of your own.
46
46
 
47
- Return control to **the session skill that invoked you** — `/runcastle:ideate` for a linear feature, `/runcastle:converge` for a mapped one. It invokes `/runcastle:tickets` next. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running (a converge session in particular is forbidden to grill) into the window.
47
+ Return control to **the session skill that invoked you** — `/runcastle:ideate`. It invokes `/runcastle:tickets` next. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running into the window.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tickets
3
- description: Break the spec into vertical slices sized to earn a fresh sandboxed agent session and land inside it, and emit them via MCP. Then complete the tickets phase. Invoked by /runcastle:ideate or /runcastle:converge.
3
+ description: Break the spec into vertical slices sized to earn a fresh sandboxed agent session and land inside it, and emit them via MCP. Then complete the tickets phase. Invoked by /runcastle:ideate.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's to-tickets skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
@@ -110,4 +110,4 @@ Then:
110
110
  - `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do NOT write ticket files.** It returns `{ stored, ids }` and logs the timeline event itself; do not record one of your own. Emit the **whole batch, fully enriched, in one call** — large payloads are supported, so a batch of long `context` fields is fine. Never emit placeholder contexts ("Context follows via `update_ticket`.") to enrich ticket by ticket afterwards: a Burn landing mid-enrichment burns agents on the placeholders.
111
111
  - `mcp__runcastle__complete_phase({ phase: "tickets" })`. It comes back `ok: true` with `waitingOn: "human burn"` and a `warnings` array — the things the human will otherwise read in the Burn dialog (no review ticket in the batch, earlier-lap defects still un-dispositioned, no `spec.md` on disk). Nothing refuses, so anything in that array is yours to fix now, while you are still the session that can: fix it and call again.
112
112
 
113
- Return control to **the session skill that invoked you** — `/runcastle:ideate` for a linear feature, `/runcastle:converge` for a mapped one — and let it close out the session. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running (a converge session in particular is forbidden to grill) into the window.
113
+ Return control to **the session skill that invoked you** — `/runcastle:ideate` — and let it close out the session. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running into the window.