runcastle 1.5.3 → 1.5.5
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/bin/runcastle.js +2047 -2825
- package/drizzle/0052_friendly_black_tarantula.sql +1 -0
- package/drizzle/0053_soft_karma.sql +12 -0
- package/drizzle/0054_careless_stellaris.sql +3 -0
- package/drizzle/meta/0052_snapshot.json +1364 -0
- package/drizzle/meta/0053_snapshot.json +1283 -0
- package/drizzle/meta/0054_snapshot.json +1268 -0
- package/drizzle/meta/_journal.json +21 -0
- package/index.js +2045 -2823
- package/package.json +1 -1
- package/skills/packs/README.md +24 -29
- package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
- package/skills/packs/runcastle/skills/feature/SKILL.md +72 -0
- package/skills/packs/runcastle/skills/feature/references/design.md +158 -0
- package/skills/packs/runcastle/skills/feature/references/grilling.md +101 -0
- package/skills/packs/runcastle/skills/feature/references/planning-alone.md +120 -0
- package/skills/packs/runcastle/skills/{ideate → feature}/references/preview-starter.html +11 -16
- package/skills/packs/runcastle/skills/{ideate → feature}/references/preview.md +33 -8
- package/skills/packs/runcastle/skills/feature/references/rounds.md +93 -0
- package/skills/packs/runcastle/skills/feature/references/spec.md +54 -0
- package/skills/packs/runcastle/skills/feature/references/tickets.md +229 -0
- package/skills/packs/runcastle/skills/project/SKILL.md +106 -63
- package/skills/packs/runcastle/skills/project/references/health-sweeps.md +11 -10
- package/skills/packs/runcastle/skills/project/references/triage.md +32 -30
- package/skills/packs/runcastle/skills/qa/SKILL.md +1 -2
- package/web/assets/index-B1MyomR6.css +1 -0
- package/web/assets/index-CPgKzYyE.js +186 -0
- package/web/index.html +2 -2
- package/skills/packs/runcastle/skills/design/SKILL.md +0 -73
- package/skills/packs/runcastle/skills/ideate/SKILL.md +0 -131
- package/skills/packs/runcastle/skills/lead/SKILL.md +0 -104
- package/skills/packs/runcastle/skills/revisit/SKILL.md +0 -55
- package/skills/packs/runcastle/skills/spec/SKILL.md +0 -47
- package/skills/packs/runcastle/skills/tickets/SKILL.md +0 -147
- package/web/assets/index-BYSs5Hdy.js +0 -186
- package/web/assets/index-bpL_iO_C.css +0 -1
package/package.json
CHANGED
package/skills/packs/README.md
CHANGED
|
@@ -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; `
|
|
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:
|
|
12
|
-
| `/runcastle:
|
|
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:
|
|
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
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
|
|
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
|
-
│
|
|
38
|
-
|
|
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:
|
|
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
|
|
80
|
-
grilling**" while `
|
|
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
|
|
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`
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|
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
|
-
`
|
|
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
|
|
136
|
-
|
|
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
|
|
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
|
|
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
|
|
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. Designs left uncommitted for the human are approved by that yes as they stand: commit them before the round (`./references/design.md`, section 8).
|
|
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 that opens with a line saying it is 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. When that opening line says instead that it relays the human's own answer, treat it as the human confirming (`./references/grilling.md`). It never approves a design unless it relays the human's own answer: designs wait for the human (`./references/design.md`, section 8).
|
|
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,158 @@
|
|
|
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, design
|
|
21
|
+
by `./planning-alone.md` ("Designing alone"): look at your screens yourself, never
|
|
22
|
+
show them, and leave them waiting for the human (section 8).
|
|
23
|
+
|
|
24
|
+
## 1. First use in a project
|
|
25
|
+
|
|
26
|
+
1. Install dependencies with the project's own install, on the host, in this
|
|
27
|
+
worktree. Skip it if they are already installed.
|
|
28
|
+
2. Work out how to serve a **preview page** with the project's own frontend dev
|
|
29
|
+
server. It runs no backend, no Docker and no database. In a Vite app that is
|
|
30
|
+
an extra entry. In Next.js it is a dev-only route kept out of production
|
|
31
|
+
builds. Add no packages to the project for this.
|
|
32
|
+
**Never use Storybook, even when the project already has it.** That includes
|
|
33
|
+
a `.runcastle/storybook/` or `.runcastle/stories/` folder: it is left over
|
|
34
|
+
from runcastle's retired stories setup, not a preview server for design. Do
|
|
35
|
+
not run it, extend it or copy its config. You may reuse its sample data or
|
|
36
|
+
network fakes (for example MSW handlers) by importing them into your own
|
|
37
|
+
preview page, served by the project's own dev server.
|
|
38
|
+
3. Check `.runcastle/design.json`. If it exists it holds the command that serves
|
|
39
|
+
previews here; reuse it.
|
|
40
|
+
|
|
41
|
+
## 2. One preview page per state
|
|
42
|
+
|
|
43
|
+
Each page mounts one state. Two ways to reach it:
|
|
44
|
+
|
|
45
|
+
- **Mount the piece directly with made-up props.** A message bubble with a long
|
|
46
|
+
message. This is the quick route for anything small.
|
|
47
|
+
- **Mount the whole screen with the server faked.** The fakes answer the
|
|
48
|
+
screen's queries at the `fetch` edge, and named mutations too, so "send"
|
|
49
|
+
works. For a state behind clicks (a dialog, a wizard step) the page performs
|
|
50
|
+
the clicks after load.
|
|
51
|
+
|
|
52
|
+
**A preview page is a dead end on purpose.** Other routes have no fakes, and may
|
|
53
|
+
not even compile in a design server, so a click that leaves the page strands the
|
|
54
|
+
human on an error. Keep them on the page: intercept link clicks and router
|
|
55
|
+
navigation inside the preview (the project's router or a capture-phase click
|
|
56
|
+
handler on `a[href]`) and do nothing, or show a small "navigation is off in this
|
|
57
|
+
preview" note. A flow across several screens is one preview page per screen,
|
|
58
|
+
not navigation.
|
|
59
|
+
|
|
60
|
+
States need no address and no backend. In runcastle's own repo start from
|
|
61
|
+
`.runcastle/preview/`: `fake-api.ts` (fake tRPC), `shell-fakes.ts` (the session
|
|
62
|
+
data the shell needs), `app-at.tsx` (renders the real `App` at an address) and
|
|
63
|
+
`event-source.ts` (a silent event stream).
|
|
64
|
+
|
|
65
|
+
## 3. Serve it and show it
|
|
66
|
+
|
|
67
|
+
- `mcp__runcastle__start_design_server({ command? })` runs your command in this
|
|
68
|
+
worktree, in a terminal runcastle owns, and returns the URL. Runcastle picks a
|
|
69
|
+
free port and gives it to you as `RUNCASTLE_DESIGN_PORT`: put
|
|
70
|
+
`$RUNCASTLE_DESIGN_PORT` in the command with a strict-port flag (for example
|
|
71
|
+
`vite --port $RUNCASTLE_DESIGN_PORT --strictPort`). The command is recorded in
|
|
72
|
+
`.runcastle/design.json`. **Commit that file with the design.** Once it exists
|
|
73
|
+
the argument is optional.
|
|
74
|
+
- `mcp__runcastle__show_panel({ view: 'design', path })` opens the human's panel
|
|
75
|
+
at the server's URL plus `path`. It refuses while no server runs and none is
|
|
76
|
+
recorded. The page lays out at a real screen width, scaled to fit the pane
|
|
77
|
+
(Desktop 1440 unless the human picked another); pass `width: 390` to show the
|
|
78
|
+
mobile layout, or `768` for a tablet.
|
|
79
|
+
- The other views are `plan`, `work`, `review`, `details` and `drive`. Open one
|
|
80
|
+
only when you are talking about that thing, not otherwise.
|
|
81
|
+
|
|
82
|
+
The server stops when this chat ends, when the feature ships, or after 30
|
|
83
|
+
minutes with no design view on screen. Opening the view again restarts it.
|
|
84
|
+
|
|
85
|
+
## 4. Iterate
|
|
86
|
+
|
|
87
|
+
The human talks, or points with **Select area** in the design view. A crop
|
|
88
|
+
reaches you in the chat as a line:
|
|
89
|
+
|
|
90
|
+
`[Screenshot: <absolute png path>] <their words>`
|
|
91
|
+
|
|
92
|
+
Read the image at that path before you answer. The file lives in runcastle's
|
|
93
|
+
data dir, not in the repo; never commit it.
|
|
94
|
+
|
|
95
|
+
**Tweaks.** For a choice that is genuinely still open (an accent colour,
|
|
96
|
+
density, which of two layouts, a radius), add a small collapsible **Tweaks**
|
|
97
|
+
menu in a corner of the preview page. Three or four controls at most. The menu
|
|
98
|
+
shows its current values as one line of text ("accent #5b8def · density compact
|
|
99
|
+
· layout B") so the human can say or paste what they chose. Do not add one for a
|
|
100
|
+
choice that is not open. Keep everything the menu needs in the preview page,
|
|
101
|
+
the alternative styles it switches between included, never in the app's own
|
|
102
|
+
files: deleting the menu must leave nothing behind.
|
|
103
|
+
|
|
104
|
+
## 5. Approve and commit
|
|
105
|
+
|
|
106
|
+
When the human settles:
|
|
107
|
+
|
|
108
|
+
1. Write the chosen tweak values into the real code and **delete the Tweaks
|
|
109
|
+
menu**. Nothing from tweaks ships.
|
|
110
|
+
2. Commit the component, its preview page and its sample data as
|
|
111
|
+
`design: <what>`. Include `.runcastle/design.json` if it is new or changed.
|
|
112
|
+
3. **Never commit unapproved work.** Leave it uncommitted, or drop it. Before
|
|
113
|
+
you start a round, commit what the human approved or drop the rest, so
|
|
114
|
+
workers fork from the design they saw.
|
|
115
|
+
4. **Only the human approves.** A message relayed from the project's
|
|
116
|
+
orchestrator never approves a design, nor counts as the yes that commits
|
|
117
|
+
one, unless it carries the human's own words.
|
|
118
|
+
|
|
119
|
+
Your commit takes the normal landing path: during a round it lands between the
|
|
120
|
+
workers' landings, and while the human drives it waits until the drive stops.
|
|
121
|
+
|
|
122
|
+
## 6. Keep to UI
|
|
123
|
+
|
|
124
|
+
Design covers components, their preview pages and sample data. When the
|
|
125
|
+
behaviour behind a designed screen is worth handing to workers, the tickets name
|
|
126
|
+
the committed component and preview page and connect them without restyling
|
|
127
|
+
(`./tickets.md`); when it is small, wire it yourself after the design is
|
|
128
|
+
committed.
|
|
129
|
+
|
|
130
|
+
## 7. Honest limits
|
|
131
|
+
|
|
132
|
+
- Pages whose HTML the server builds (Next.js server components, Rails or Django
|
|
133
|
+
templates) cannot be previewed whole. Mount their client pieces directly.
|
|
134
|
+
- Live content (streams, sockets) shows canned content.
|
|
135
|
+
- Native mobile has no browser to render in.
|
|
136
|
+
|
|
137
|
+
Preview files are a byproduct. Nothing requires them and nothing checks them but
|
|
138
|
+
the project's typecheck. If one has gone stale when you next open it, repair it.
|
|
139
|
+
|
|
140
|
+
## 8. Designs that wait for the human
|
|
141
|
+
|
|
142
|
+
A self-planning run may leave designs uncommitted in this worktree, waiting for
|
|
143
|
+
the human: its closing message names them, and the plan preview opens each one.
|
|
144
|
+
When the human comes back:
|
|
145
|
+
|
|
146
|
+
- **They approve a screen**: commit it as section 5 says.
|
|
147
|
+
- **They ask for changes**: iterate as in section 4. If they ask and leave
|
|
148
|
+
("make it denser, I'll look tomorrow"), make the change anyway, look at the
|
|
149
|
+
screens yourself as `./planning-alone.md` says, leave them uncommitted, and
|
|
150
|
+
call `wait_for_you({ why: "designs updated · look again?" })`.
|
|
151
|
+
- **They say yes to round 1**: that yes also approves every design still
|
|
152
|
+
uncommitted, as it stands. Commit them before you dispatch, and say which
|
|
153
|
+
you committed. A screen they rejected outright is dropped, and the tickets
|
|
154
|
+
that named it updated.
|
|
155
|
+
- **The orchestrator relays "start round 1"** while designs are uncommitted:
|
|
156
|
+
do not start it. Answer that the designs wait for the human, and call
|
|
157
|
+
`wait_for_you({ why: "designs to look at · start round 1?" })` ("start the
|
|
158
|
+
work?" when there are no tickets).
|
|
@@ -0,0 +1,101 @@
|
|
|
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.** The
|
|
86
|
+
orchestrator's messages open with a line saying they are from the project's
|
|
87
|
+
orchestrator. When a message comes from it, a decision it changes keeps its
|
|
88
|
+
`**Assumed.**` line, or gains one, and adds
|
|
89
|
+
`**Changed by the orchestrator:** <why>`. The human has not seen it, and the
|
|
90
|
+
record must say who decided. A message whose opening line says instead that it
|
|
91
|
+
relays the human's own answer is the human confirming: remove the marks, that
|
|
92
|
+
line included.
|
|
93
|
+
|
|
94
|
+
**Later information supersedes, it does not rewrite.** When the human comes
|
|
95
|
+
back with something new after decisions were written — a constraint they
|
|
96
|
+
remembered, a change of mind — append it as a new decision that names the one it
|
|
97
|
+
replaces ("Supersedes: 4"), and amend `spec.md` where it is affected.
|
|
98
|
+
|
|
99
|
+
Keep `decisions.md` free of implementation minutiae: it records what was
|
|
100
|
+
decided and why. Whenever it changes and a `preview.html` exists, redraw the
|
|
101
|
+
preview in the same move (`./preview.md`). Commit it like any other doc.
|
|
@@ -0,0 +1,120 @@
|
|
|
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.** Whatever the plan says,
|
|
11
|
+
never call `dispatch_round` in this run: no round starts without the human's
|
|
12
|
+
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. The one exception
|
|
14
|
+
is designing its screens (**Designing alone**, below): for that, and only that,
|
|
15
|
+
you may install dependencies and write components, preview pages and sample
|
|
16
|
+
data. Write no other code.
|
|
17
|
+
|
|
18
|
+
- **One turn.** Do the whole plan in this one turn. Never end the turn to ask a
|
|
19
|
+
question or to report progress: nobody will reply, and ending the turn ends
|
|
20
|
+
the run.
|
|
21
|
+
- **Frontier rounds.** Work in question rounds. A question round is every open
|
|
22
|
+
question that does not depend on an unanswered one. Write each as you would
|
|
23
|
+
put it to the human, with your recommended answer and the main alternative,
|
|
24
|
+
then accept your own recommendations and record them. Then ask what those
|
|
25
|
+
answers opened: that is the next question round. Write each to
|
|
26
|
+
`decisions.md` as it is decided (`./grilling.md`, the `**Assumed.**` shape),
|
|
27
|
+
not at the end, so a run that dies keeps what it had.
|
|
28
|
+
- **Depth rule.** Record a decision only if it changes what the feature is or
|
|
29
|
+
does for its users, or the human might reasonably overturn it. Anything an
|
|
30
|
+
engineer would settle while building (wording, internal formats, test
|
|
31
|
+
coverage) is not a question; it goes to the spec as detail.
|
|
32
|
+
- **Exactly one challenger.** When a question round would be empty, run one
|
|
33
|
+
separate agent: the Agent tool in Claude Code, `spawn_agent` in Codex. Give it
|
|
34
|
+
only the brief and `decisions.md`. Ask it for (a) design branches never asked,
|
|
35
|
+
within the depth rule, and (b) decisions it would have made differently, with
|
|
36
|
+
its reason. Do not tell it what you expect to hear. Ask its branches as one
|
|
37
|
+
more question round and decide them, with whatever they open. A decision it
|
|
38
|
+
disagrees with gets a `**Challenged:**` line with its reason, and your own
|
|
39
|
+
answer stands. There is no second pass. If you have no tool that runs a
|
|
40
|
+
separate agent, challenge your own plan as a separate pass and write in
|
|
41
|
+
`decisions.md` that the challenge was not independent.
|
|
42
|
+
- **A refused tool is not a wait.** If a tool is refused, do not retry it and do
|
|
43
|
+
not wait: decide on what you know, and treat that decision as one to check
|
|
44
|
+
first.
|
|
45
|
+
- **A timed-out tool is not a refused one.** A timeout is no answer, and the
|
|
46
|
+
call may have landed anyway. Retry it once, checking first that it did not
|
|
47
|
+
land: before repeating `emit_tickets`, `update_ticket` or `cancel_ticket`,
|
|
48
|
+
read `list_tickets` and send only what is missing. If the retry times out
|
|
49
|
+
too, carry on and say so in the closing message.
|
|
50
|
+
- **Check first.** Pick at most five decisions for the human to check first:
|
|
51
|
+
guesses about their intent the brief did not settle, decisions that force
|
|
52
|
+
several others, and decisions the challenger disagreed with. They open the
|
|
53
|
+
preview's decisions part (`./preview.md`).
|
|
54
|
+
- **Design, but never show.** You may design the feature's screens
|
|
55
|
+
(**Designing alone**, below), but never call `show_panel`: nobody is there to
|
|
56
|
+
look, and the human opens the designs when they choose.
|
|
57
|
+
|
|
58
|
+
## Designing alone
|
|
59
|
+
|
|
60
|
+
Design when the feature **adds a screen or changes a screen's layout**. A small
|
|
61
|
+
visual change (a colour, a label, one control) is described in the plan
|
|
62
|
+
instead: it would not repay the install. A feature with no screen designs
|
|
63
|
+
nothing.
|
|
64
|
+
|
|
65
|
+
- **When.** Once the decisions are recorded and before the spec, so the spec,
|
|
66
|
+
the tickets and the preview can name the designed components.
|
|
67
|
+
- **How.** As `./design.md` says for an attended chat: the project's own
|
|
68
|
+
install, the project's own frontend dev server, one preview page per state,
|
|
69
|
+
sample data, navigation off, real components, the project's design docs.
|
|
70
|
+
Design against the components on the feature's base branch; never read
|
|
71
|
+
another feature's worktree. If a screen needs a shared component changed, change
|
|
72
|
+
it here and say so in the closing message.
|
|
73
|
+
- **Tweaks for taste.** Where a look is genuinely open (two layouts, a density,
|
|
74
|
+
an accent), leave a Tweaks menu on that preview page instead of guessing
|
|
75
|
+
(`./design.md`, Tweaks). **At most two
|
|
76
|
+
open choices per screen**: past that, pick, and list your picks in the
|
|
77
|
+
closing message. Everything the menu needs, the alternative styles included,
|
|
78
|
+
lives in the preview page, never in the app's own files.
|
|
79
|
+
- **Look at your own screens.** Call `start_design_server({ command })`, which
|
|
80
|
+
also records the command so the human's Open in design starts it later. Then
|
|
81
|
+
check every preview page. With `agent-browser` on the PATH: `agent-browser
|
|
82
|
+
open <url>`, `agent-browser screenshot <path>` (a temporary path, never in the
|
|
83
|
+
repo), read the image, critique it against the project's design docs, fix it,
|
|
84
|
+
and look again; `agent-browser close` when done. Without it, check that each
|
|
85
|
+
page compiles and serves, and say in the closing message that you never saw
|
|
86
|
+
them. The design server stops by itself when this run ends.
|
|
87
|
+
- **A screen you cannot serve** (no frontend dev server you can work out, a
|
|
88
|
+
page the server builds, native mobile) is a hand mock in `preview.html`, as
|
|
89
|
+
any plan draws one. Say so in the closing message. Do not stop or ask.
|
|
90
|
+
- **Leave the designs uncommitted.** Never commit a component, preview page,
|
|
91
|
+
sample data or `.runcastle/design.json` you designed: only the human's
|
|
92
|
+
approval commits them. Commit your docs by path, so nothing designed rides
|
|
93
|
+
along.
|
|
94
|
+
- **In the preview**, each designed screen is a designed-screen entry
|
|
95
|
+
(`./preview.md`, Designed screens) naming its component file and preview page.
|
|
96
|
+
A ticket that wires one follows `./tickets.md` on designed screens.
|
|
97
|
+
|
|
98
|
+
## Straight through, then ask
|
|
99
|
+
|
|
100
|
+
1. Write `spec.md` (`./spec.md`) once the decisions are recorded.
|
|
101
|
+
2. Write the tickets if the work is worth handing to workers (`./tickets.md`);
|
|
102
|
+
otherwise the plan says you will build it yourself.
|
|
103
|
+
3. Draw the preview and run its self-check (`./preview.md`).
|
|
104
|
+
4. Commit the docs.
|
|
105
|
+
5. **End with one closing chat message** that covers the plan only: that it was
|
|
106
|
+
made without the human, how many decisions it holds and which are to check
|
|
107
|
+
first (by number and title), whether it is tickets or work you will do
|
|
108
|
+
yourself, and anything that stopped you doing the job (a refused or failed
|
|
109
|
+
tool the plan needed, and what you decided without it). Leave out the state
|
|
110
|
+
of tools and connectors the plan did not need. **If you designed**, also
|
|
111
|
+
name each designed screen, the open Tweaks choices, any shared component you
|
|
112
|
+
changed, and whether you saw the screens in screenshots or only checked that
|
|
113
|
+
they compile, and say that the yes commits the designs as they stand. Close the message by asking
|
|
114
|
+
the human for their yes to start round 1 (or to start the work, if there are
|
|
115
|
+
no tickets).
|
|
116
|
+
6. Call `mcp__runcastle__wait_for_you({ why: "start round 1?" })`, or
|
|
117
|
+
`mcp__runcastle__wait_for_you({ why: "designs to look at · start round 1?" })`
|
|
118
|
+
if you designed. With no tickets, say "start the work?" in place of "start
|
|
119
|
+
round 1?". Then end the turn. The human answers in this chat when they
|
|
120
|
+
are back; their yes is what starts the work, in a later turn.
|