runcastle 1.5.3 → 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 (34) hide show
  1. package/bin/runcastle.js +1716 -2625
  2. package/drizzle/0052_friendly_black_tarantula.sql +1 -0
  3. package/drizzle/0053_soft_karma.sql +12 -0
  4. package/drizzle/meta/0052_snapshot.json +1364 -0
  5. package/drizzle/meta/0053_snapshot.json +1283 -0
  6. package/drizzle/meta/_journal.json +14 -0
  7. package/index.js +1539 -2448
  8. package/package.json +1 -1
  9. package/skills/packs/README.md +24 -29
  10. package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
  11. package/skills/packs/runcastle/skills/feature/SKILL.md +72 -0
  12. package/skills/packs/runcastle/skills/feature/references/design.md +132 -0
  13. package/skills/packs/runcastle/skills/feature/references/grilling.md +99 -0
  14. package/skills/packs/runcastle/skills/feature/references/planning-alone.md +70 -0
  15. package/skills/packs/runcastle/skills/{ideate → feature}/references/preview-starter.html +11 -16
  16. package/skills/packs/runcastle/skills/{ideate → feature}/references/preview.md +33 -8
  17. package/skills/packs/runcastle/skills/feature/references/rounds.md +91 -0
  18. package/skills/packs/runcastle/skills/feature/references/spec.md +54 -0
  19. package/skills/packs/runcastle/skills/feature/references/tickets.md +229 -0
  20. package/skills/packs/runcastle/skills/project/SKILL.md +18 -16
  21. package/skills/packs/runcastle/skills/project/references/health-sweeps.md +5 -5
  22. package/skills/packs/runcastle/skills/project/references/triage.md +20 -23
  23. package/skills/packs/runcastle/skills/qa/SKILL.md +1 -2
  24. package/web/assets/index-CT9k1kz0.css +1 -0
  25. package/web/assets/index-DVR7VbWW.js +186 -0
  26. package/web/index.html +2 -2
  27. package/skills/packs/runcastle/skills/design/SKILL.md +0 -73
  28. package/skills/packs/runcastle/skills/ideate/SKILL.md +0 -131
  29. package/skills/packs/runcastle/skills/lead/SKILL.md +0 -104
  30. package/skills/packs/runcastle/skills/revisit/SKILL.md +0 -55
  31. package/skills/packs/runcastle/skills/spec/SKILL.md +0 -47
  32. package/skills/packs/runcastle/skills/tickets/SKILL.md +0 -147
  33. package/web/assets/index-BYSs5Hdy.js +0 -186
  34. package/web/assets/index-bpL_iO_C.css +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcastle",
3
- "version": "1.5.3",
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",
@@ -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,13 +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 |
17
- | `/runcastle:design` | by the feature chat's entry skills | *original* — live UI design beside the chat: preview pages, the design server and panel, Tweaks, `design:` commits; not an entry skill |
14
+ | `/runcastle:prepare` | entry for `kind=prepare` | *original* — establishes the project's prepared settings and drive machinery with the human |
18
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 |
19
16
 
20
17
  Layout (the verified plugin format — only `plugin.json` lives inside `.claude-plugin/`; `skills/` is a sibling at the plugin root):
@@ -26,16 +23,17 @@ packs/
26
23
  ├── .claude-plugin/
27
24
  │ └── plugin.json # name: "runcastle" → sets the /runcastle: namespace
28
25
  └── skills/
29
- ├── ideate/SKILL.md
30
- ├── spec/SKILL.md
31
- ├── tickets/SKILL.md
26
+ ├── feature/
27
+ │ ├── SKILL.md
28
+ │ └── references/ # grilling, spec, tickets, rounds, planning-alone, preview, design
32
29
  ├── qa/SKILL.md
33
30
  ├── project/
34
31
  │ ├── SKILL.md
35
32
  │ └── references/ # loaded on demand by SKILL.md, never up front
36
33
  │ ├── charter.md
37
- │ └── health-sweeps.md
38
- ├── revisit/SKILL.md
34
+ │ ├── health-sweeps.md
35
+ │ └── triage.md
36
+ ├── prepare/SKILL.md
39
37
  └── code-review/SKILL.md
40
38
  ```
41
39
 
@@ -68,7 +66,7 @@ The session launcher spawns Claude Code with the pack's **root** directory (the
68
66
  claude ... --plugin-dir "<abs path>/packages/skills/packs/runcastle" ...
69
67
  ```
70
68
 
71
- 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).
72
70
 
73
71
  ### Entry-skill isolation — settings deny rules, not `disable-model-invocation`
74
72
 
@@ -76,11 +74,11 @@ Every skill's `description` is loaded into every session in the pack, whether or
76
74
  not that session can use it. That is cheap for a skill the model might genuinely
77
75
  need and risky for one it must not run: a session carrying the descriptions of
78
76
  every other kind's entry skill is one description-match away from running
79
- another session's procedure — `project/SKILL.md` says "**Never run an ideation
80
- 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
81
79
  that.
82
80
 
83
- The five **session-entry** skills — `ideate`, `qa`, `revisit`, `project`,
81
+ The **session-entry** skills — today `feature`, `qa`, `project` and
84
82
  `prepare` — used to carry `disable-model-invocation:
85
83
  true` for that reason, on the build-era understanding that explicit invocation
86
84
  kept working (CC-INTEGRATION-NOTES §1). It no longer does: Claude Code now
@@ -98,13 +96,11 @@ live: **per-session settings**. The launcher launches each session with
98
96
  — for every entry skill that is not that kind's own (`entrySkillsFor` /
99
97
  `entrySkillDenyRules` in `packages/server/src/launcher`), and each injected
100
98
  prompt states the policy in one line. Same layering as the edit guard: the
101
- prompt says it, the harness enforces it. `chat` keeps both of its openings
102
- (`ideate` + `revisit`); `qa` — whose session kind was retired — is denied
103
- 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.
104
102
 
105
- The chained/reached skills — `spec` and `tickets` (invoked by name from inside
106
- `ideate`) and `code-review` (reached by description) — are in no
107
- deny list.
103
+ The reached skill — `code-review` (reached by description) — is in no deny list.
108
104
 
109
105
  The rule, for anything added later: **a session-entry skill goes into
110
106
  `ENTRY_SKILLS` and the per-kind table in
@@ -129,14 +125,13 @@ The standing convention across this pack:
129
125
  rule lives there, backed by `launcher/edit-guard.ts`, and is *not* restated in
130
126
  any skill).
131
127
  - **Procedures belong to the skill.** One skill owns each procedure in full;
132
- `revisit/SKILL.md` §Lap mode is the whole lap procedure, `ideate/SKILL.md` §1
133
- is the whole grilling procedure.
128
+ `feature/references/grilling.md` is the whole grilling procedure.
134
129
  - **Where a skill names a fact the prompt also names, it points instead of
135
- restating** — the shape is `ideate/SKILL.md`'s "The injected system prompt
136
- 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."
137
132
  - **Where two skills need the same procedure, the second delegates by
138
- reference** — "grill the way `/runcastle:ideate` §1 does" rather than a
139
- second copy that ages differently.
133
+ reference** — "grill the way `feature/references/grilling.md` does" rather
134
+ than a second copy that ages differently.
140
135
  - **A rule the hook enforces is stated once, as enforced, not as advice.** The
141
136
  `qa` kind's write refusals are server-side; `qa/SKILL.md` says so rather than
142
137
  asking nicely.
@@ -150,7 +145,7 @@ ticket instead.
150
145
 
151
146
  1. Create `packs/<pack-name>/.claude-plugin/plugin.json` with at least `{ "name": "<pack-name>" }` — `name` sets the `/<pack-name>:` namespace.
152
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.
153
- 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`).
154
149
  4. Pass another `--plugin-dir` for it at launch.
155
150
 
156
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, prepare and lead (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.
@@ -16,9 +16,6 @@
16
16
  --color-primary:#eceef4;--color-on-primary:#0a0c11;
17
17
  --color-accent:#4f8ef7;--color-accent-text:#7aaafa;--color-accent-subtle:#16213a;
18
18
  --color-success:#46b67a;--color-warning:#e2a53b;--color-danger:#f06363;--color-danger-subtle:#2a1515;
19
- --color-phase-ideation:#ec7cb5;--color-phase-spec:#35c2b8;--color-phase-implementation:#f08a4b;
20
- --color-phase-planning:var(--color-phase-spec);--color-phase-building:var(--color-phase-implementation);
21
- --color-phase-review:var(--color-accent);--color-phase-shipped:var(--color-success);
22
19
  --color-scrim:rgb(0 0 0 / 0.6);
23
20
  --font-sans:"Geist Variable","Segoe UI Variable",system-ui,-apple-system,sans-serif;
24
21
  --font-mono:"Geist Mono Variable","Cascadia Code",ui-monospace,Consolas,monospace;
@@ -28,11 +25,10 @@
28
25
  color-scheme:light;
29
26
  --color-canvas:#f4f6f9;--color-surface:#ffffff;--color-surface-raised:#ffffff;--color-surface-hover:#eef1f6;--color-surface-selected:#e4e8f1;--color-surface-inset:#f4f6f9;
30
27
  --color-border:#dfe3ec;--color-border-subtle:rgb(22 30 50 / 0.07);--color-border-strong:#cdd2de;
31
- --color-text:#161a24;--color-text-secondary:#4c5366;--color-text-tertiary:#5c6377;--color-text-disabled:#a4aab9;--color-icon:#6a7185;
28
+ --color-text:#161a24;--color-text-secondary:#596075;--color-text-tertiary:#676e82;--color-text-disabled:#a4aab9;--color-icon:#7a8195;
32
29
  --color-primary:#161a24;--color-on-primary:#ffffff;
33
- --color-accent:#2f6fe4;--color-accent-text:#2463d6;--color-accent-subtle:#ecf2fe;
34
- --color-success:#1e8e53;--color-warning:#a86a06;--color-danger:#c42828;--color-danger-subtle:#fdeeee;
35
- --color-phase-ideation:#c13d7e;--color-phase-spec:#0b7f78;--color-phase-implementation:#b44f0c;
30
+ --color-accent:#3a7ef0;--color-accent-text:#2361d4;--color-accent-subtle:#ecf2fe;
31
+ --color-success:#23955a;--color-warning:#c06e00;--color-danger:#df5050;--color-danger-subtle:#fdeeee;
36
32
  --color-scrim:rgb(22 26 36 / 0.28);
37
33
  }
38
34
  @media (prefers-color-scheme:light){
@@ -40,11 +36,10 @@
40
36
  color-scheme:light;
41
37
  --color-canvas:#f4f6f9;--color-surface:#ffffff;--color-surface-raised:#ffffff;--color-surface-hover:#eef1f6;--color-surface-selected:#e4e8f1;--color-surface-inset:#f4f6f9;
42
38
  --color-border:#dfe3ec;--color-border-subtle:rgb(22 30 50 / 0.07);--color-border-strong:#cdd2de;
43
- --color-text:#161a24;--color-text-secondary:#4c5366;--color-text-tertiary:#5c6377;--color-text-disabled:#a4aab9;--color-icon:#6a7185;
39
+ --color-text:#161a24;--color-text-secondary:#596075;--color-text-tertiary:#676e82;--color-text-disabled:#a4aab9;--color-icon:#7a8195;
44
40
  --color-primary:#161a24;--color-on-primary:#ffffff;
45
- --color-accent:#2f6fe4;--color-accent-text:#2463d6;--color-accent-subtle:#ecf2fe;
46
- --color-success:#1e8e53;--color-warning:#a86a06;--color-danger:#c42828;--color-danger-subtle:#fdeeee;
47
- --color-phase-ideation:#c13d7e;--color-phase-spec:#0b7f78;--color-phase-implementation:#b44f0c;
41
+ --color-accent:#3a7ef0;--color-accent-text:#2361d4;--color-accent-subtle:#ecf2fe;
42
+ --color-success:#23955a;--color-warning:#c06e00;--color-danger:#df5050;--color-danger-subtle:#fdeeee;
48
43
  --color-scrim:rgb(22 26 36 / 0.28);
49
44
  }
50
45
  }
@@ -141,7 +136,7 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
141
136
  <header id="what">
142
137
  <h1>[Feature title]</h1>
143
138
  <p class="lede">[One or two sentences: what this feature is, in the human's words.]</p>
144
- <div class="meta"><span>Lap 1</span><span><span data-dcount></span> decisions</span><span class="mono">docs/features/[slug]/preview.html</span></div>
139
+ <div class="meta"><span><span data-dcount></span> decisions</span><span class="mono">docs/features/[slug]/preview.html</span></div>
145
140
  <nav class="jump" aria-label="Parts of this preview">
146
141
  <a href="#show">The show</a><a href="#decided">Decided <b data-dcount></b></a><a href="#left-out">Left out</a><a href="#hood">Under the hood</a>
147
142
  </nav>
@@ -183,8 +178,8 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
183
178
  <section id="left-out">
184
179
  <h2>What is left out</h2>
185
180
  <div class="out2">
186
- <div><h3>Later laps</h3><ul>
187
- <li><i class="dot"></i>[Deferred to a later lap]</li>
181
+ <div><h3>Later</h3><ul>
182
+ <li><i class="dot"></i>[Deferred to a backlog feature]</li>
188
183
  </ul></div>
189
184
  <div><h3>Not this feature</h3><ul>
190
185
  <li><i class="dot"></i>[Out of scope]</li>
@@ -259,7 +254,7 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
259
254
 
260
255
  // a pin opens its decision; in-page links scroll by script, because hash
261
256
  // navigation is unreliable inside the app's sandboxed frame
262
- function go(el) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }) }
257
+ function reveal(el) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }) }
263
258
  document.addEventListener('click', function (e) {
264
259
  // Open in design: the app listens for exactly this message and opens the design panel
265
260
  var od = e.target.closest('[data-open-design]')
@@ -271,7 +266,7 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
271
266
  if (pin) {
272
267
  e.preventDefault()
273
268
  var d = $('#d' + pin.dataset.d)
274
- d.open = true; go(d); d.classList.add('flash')
269
+ d.open = true; reveal(d); d.classList.add('flash')
275
270
  setTimeout(function () { d.classList.remove('flash') }, 1400)
276
271
  return
277
272
  }