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.
- package/bin/runcastle.js +1716 -2625
- package/drizzle/0052_friendly_black_tarantula.sql +1 -0
- package/drizzle/0053_soft_karma.sql +12 -0
- package/drizzle/meta/0052_snapshot.json +1364 -0
- package/drizzle/meta/0053_snapshot.json +1283 -0
- package/drizzle/meta/_journal.json +14 -0
- package/index.js +1539 -2448
- 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 +132 -0
- package/skills/packs/runcastle/skills/feature/references/grilling.md +99 -0
- package/skills/packs/runcastle/skills/feature/references/planning-alone.md +70 -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 +91 -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 +18 -16
- package/skills/packs/runcastle/skills/project/references/health-sweeps.md +5 -5
- package/skills/packs/runcastle/skills/project/references/triage.md +20 -23
- package/skills/packs/runcastle/skills/qa/SKILL.md +1 -2
- package/web/assets/index-CT9k1kz0.css +1 -0
- package/web/assets/index-DVR7VbWW.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/web/index.html
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
document.documentElement.dataset.theme = t
|
|
19
19
|
})()
|
|
20
20
|
</script>
|
|
21
|
-
<script type="module" crossorigin src="/assets/index-
|
|
22
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
21
|
+
<script type="module" crossorigin src="/assets/index-DVR7VbWW.js"></script>
|
|
22
|
+
<link rel="stylesheet" crossorigin href="/assets/index-CT9k1kz0.css">
|
|
23
23
|
</head>
|
|
24
24
|
<body>
|
|
25
25
|
<div id="root"></div>
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: design
|
|
3
|
-
description: How the feature chat designs UI live with the human — a preview page per state, served by the project's own dev server and shown in a panel beside the chat. Use whenever you judge a design moment or the human asks to see how something looks, in any phase.
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Design — iterate on the real screen, beside the chat
|
|
8
|
-
|
|
9
|
-
The human wants to see a screen in a given state without test-driving the whole app to reach it. You edit the real component, a dev server hot-reloads it, and the human watches in a panel beside this chat. What they approve is committed to the feature branch and ships. The tickets then cover only what design did not: real data, API calls, behaviour.
|
|
10
|
-
|
|
11
|
-
You are not a mode the human switches on. Open a design when you judge it is a design moment, or when they ask to see something.
|
|
12
|
-
|
|
13
|
-
**Offer it early.** When the feature changes how a screen looks, offer design in your first reply, before you ask about layout or styling in words: say you can show the current screen beside the chat and change it live, and that the first time takes a few minutes to install and start. Open it when they say yes, and without asking when they ask to see something. Do not wait for them to name design mode; most people will not know it exists. This works in any phase. On the self-planning path (no human present) never open a design.
|
|
14
|
-
|
|
15
|
-
## 1. First use in a project
|
|
16
|
-
|
|
17
|
-
1. Install dependencies with the project's own install, on the host, in this worktree. Skip it if they are already installed.
|
|
18
|
-
2. Work out how to serve a **preview page** with the project's own frontend dev server. It runs no backend, no Docker and no database. In a Vite app that is an extra entry. In Next.js it is a dev-only route kept out of production builds. Add no packages to the project for this.
|
|
19
|
-
**Never use Storybook, even when the project already has it.** That includes a `.runcastle/storybook/` or `.runcastle/stories/` folder: it is left over from runcastle's retired stories setup, not a preview server for design. Do not run it, extend it or copy its config. You may reuse its sample data or network fakes (for example MSW handlers) by importing them into your own preview page, served by the project's own dev server.
|
|
20
|
-
3. Check `.runcastle/design.json`. If it exists it holds the command that serves previews here; reuse it.
|
|
21
|
-
|
|
22
|
-
## 2. One preview page per state
|
|
23
|
-
|
|
24
|
-
Each page mounts one state. Two ways to reach it:
|
|
25
|
-
|
|
26
|
-
- **Mount the piece directly with made-up props.** A message bubble with a long message. This is the quick route for anything small.
|
|
27
|
-
- **Mount the whole screen with the server faked.** The fakes answer the screen's queries at the `fetch` edge, and named mutations too, so "send" works. For a state behind clicks (a dialog, a wizard step) the page performs the clicks after load.
|
|
28
|
-
|
|
29
|
-
**A preview page is a dead end on purpose.** Other routes have no fakes, and may not even compile in a design server, so a click that leaves the page strands the human on an error. Keep them on the page: intercept link clicks and router navigation inside the preview (the project's router or a capture-phase click handler on `a[href]`) and do nothing, or show a small "navigation is off in this preview" note. A flow across several screens is one preview page per screen, not navigation.
|
|
30
|
-
|
|
31
|
-
States need no address and no backend. In runcastle's own repo start from `.runcastle/preview/`: `fake-api.ts` (fake tRPC), `shell-fakes.ts` (the session data the shell needs), `app-at.tsx` (renders the real `App` at an address) and `event-source.ts` (a silent event stream).
|
|
32
|
-
|
|
33
|
-
## 3. Serve it and show it
|
|
34
|
-
|
|
35
|
-
- `mcp__runcastle__start_design_server({ command? })` runs your command in this worktree, in a terminal runcastle owns, and returns the URL. Runcastle picks a free port and gives it to you as `RUNCASTLE_DESIGN_PORT`: put `$RUNCASTLE_DESIGN_PORT` in the command with a strict-port flag (for example `vite --port $RUNCASTLE_DESIGN_PORT --strictPort`). The command is recorded in `.runcastle/design.json`. **Commit that file with the design.** Once it exists the argument is optional.
|
|
36
|
-
- `mcp__runcastle__show_panel({ view: 'design', path })` opens the human's panel at the server's URL plus `path`. It refuses while no server runs and none is recorded. The page lays out at a real screen width, scaled to fit the pane (Desktop 1440 unless the human picked another); pass `width: 390` to show the mobile layout, or `768` for a tablet.
|
|
37
|
-
- The other views are `body`, `preview`, `drive` and `details`. Open one only when you are talking about that thing, not otherwise.
|
|
38
|
-
|
|
39
|
-
The server stops when this chat ends, when the feature ships, or after 30 minutes with no design view on screen. Opening the view again restarts it.
|
|
40
|
-
|
|
41
|
-
## 4. Iterate
|
|
42
|
-
|
|
43
|
-
The human talks, or points with **Select area** in the design view. A crop reaches you in the chat as a line:
|
|
44
|
-
|
|
45
|
-
`[Screenshot: <absolute png path>] <their words>`
|
|
46
|
-
|
|
47
|
-
Read the image at that path before you answer. The file lives in runcastle's data dir, not in the repo; never commit it.
|
|
48
|
-
|
|
49
|
-
**Tweaks.** For a choice that is genuinely still open (an accent colour, density, which of two layouts, a radius), add a small collapsible **Tweaks** menu in a corner of the preview page. Three or four controls at most. The menu shows its current values as one line of text ("accent #5b8def · density compact · layout B") so the human can say or paste what they chose. Do not add one for a choice that is not open.
|
|
50
|
-
|
|
51
|
-
## 5. Approve and commit
|
|
52
|
-
|
|
53
|
-
When the human settles:
|
|
54
|
-
|
|
55
|
-
1. Write the chosen tweak values into the real code and **delete the Tweaks menu**. Nothing from tweaks ships.
|
|
56
|
-
2. Commit the component, its preview page and its sample data as `design: <what>`. Include `.runcastle/design.json` if it is new or changed.
|
|
57
|
-
3. **Never commit unapproved work.** Leave it uncommitted, or drop it.
|
|
58
|
-
|
|
59
|
-
In planning the commit lands on the feature branch directly. After Go you are the lead, and your commit takes the lead's normal landing path.
|
|
60
|
-
|
|
61
|
-
**Go waits while design work is uncommitted.** Before the human clicks Go, commit what they approved or drop the rest. Remind them when you see the worktree dirty.
|
|
62
|
-
|
|
63
|
-
## 6. Keep to UI
|
|
64
|
-
|
|
65
|
-
Design covers components, their preview pages and sample data. Data loading, API calls and behaviour ride tickets; do not write them here, even though nothing stops you before Go. The tickets will name the committed component and preview page and connect them without restyling.
|
|
66
|
-
|
|
67
|
-
## 7. Honest limits
|
|
68
|
-
|
|
69
|
-
- Pages whose HTML the server builds (Next.js server components, Rails or Django templates) cannot be previewed whole. Mount their client pieces directly.
|
|
70
|
-
- Live content (streams, sockets) shows canned content.
|
|
71
|
-
- Native mobile has no browser to render in.
|
|
72
|
-
|
|
73
|
-
Preview files are a byproduct. Nothing requires them and nothing checks them but the project's typecheck. If one has gone stale when you next open it, repair it.
|
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: ideate
|
|
3
|
-
description: The runcastle ideation session. Grill the human relentlessly about a feature, lock decisions incrementally into decisions.md, then drive spec and tickets out of the same unbroken context. Entry skill for kind=ideation sessions.
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
---
|
|
6
|
-
<!-- Forked from Matt Pocock's grilling + grill-with-docs skills, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
|
|
7
|
-
|
|
8
|
-
# Ideate — runcastle's ideation session
|
|
9
|
-
|
|
10
|
-
You are the front of the runcastle pipeline. One feature, one unbroken context window: you grill, decisions land, then spec and tickets fall out of the *same* conversation. After you, the human's only job is to click **Go**. Do it right.
|
|
11
|
-
|
|
12
|
-
## Context hygiene is the whole game
|
|
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: narrow, don't compact. (Matt Pocock's rule; it is load-bearing here.)
|
|
15
|
-
|
|
16
|
-
## 0. Orient
|
|
17
|
-
|
|
18
|
-
1. Call `mcp__runcastle__get_feature_context`. It opens with a header — the feature (`slug`, `title`, `oneLiner`), current `phase` and `lap`, `annotatedModels`, `burnConcurrency` — then any tickets as summary rows (`seq`, `title`, `status`, `goal`, …; a ticket's context, acceptance criteria and digest come from `mcp__runcastle__get_ticket({ seq })`), then the canonical docs inlined in `docs[]` (`brief.md`, and any `decisions.md` — **this may be a resumed session; read what is already locked and do not re-ask it**), and an index of every other doc in `moreDocs[]` (fetch one with `mcp__runcastle__read_feature_doc({ relPath })`). A canonical doc too large to inline is listed in `notInlined` with its `relPath` and `absPath` instead — **read it before acting**, with `read_feature_doc` or off disk.
|
|
19
|
-
2. Decisions live at `docs/features/<slug>/decisions.md` in this worktree. The injected system prompt carries the slug and paths; trust `get_feature_context` for the live state.
|
|
20
|
-
|
|
21
|
-
**Your tools**, and there are not many — this session's real work is the conversation:
|
|
22
|
-
|
|
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, dependsOn? })` — put scope creep in the backlog as a draft-status 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 (§4), and the ones `/runcastle:spec` and `/runcastle:tickets` cross after you.
|
|
26
|
-
- `mcp__runcastle__emit_tickets` — used by `/runcastle:tickets`, not by you directly.
|
|
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.
|
|
28
|
-
|
|
29
|
-
## 1. Grill relentlessly
|
|
30
|
-
|
|
31
|
-
This is the **grill path**: the human is here and answers. When your briefing says no human is present, take the **self-planning path** in "Planning without a human" below instead; the rules of this section do not bind it.
|
|
32
|
-
|
|
33
|
-
Interview the human about every aspect of the feature until you reach a genuinely shared understanding. Walk each branch of the design tree, resolving dependencies between decisions one at a time.
|
|
34
|
-
|
|
35
|
-
- **One question at a time.** Wait for the answer before the next. Asking several at once is bewildering.
|
|
36
|
-
- **Always recommend an answer.** Never ask a bare question — put your recommended answer to it and say why. The human corrects or confirms.
|
|
37
|
-
- **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.
|
|
38
|
-
- **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?").
|
|
39
|
-
- **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
|
-
- **Put scope creep in the backlog instead of swallowing it.** When something surfaces that is plainly its own feature, say so and offer to put it in the backlog: `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` puts it in the backlog (status `draft`) — no branch, nothing written — with the `brief` carrying why you deferred it and what it must not swallow. When the backlog feature 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. Backlog features are the only thing you may create here; a full create belongs to the project session.
|
|
41
|
-
- **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
|
-
|
|
43
|
-
On the grill path, do not move on until the human confirms you have reached shared understanding. That confirmation happens on the preview (§3), not in chat alone.
|
|
44
|
-
|
|
45
|
-
## 1a. Planning without a human
|
|
46
|
-
|
|
47
|
-
This is the **self-planning path**. It applies only when your briefing says no human is present; with a human here, §1 stands and this section does not. You still look up facts, model the domain and park scope creep as §1 says. What changes is who answers. A run that resumes your planning conversation with the orchestrator's message (a relay) follows this section too; a decision changed on the orchestrator's message is marked as `../revisit/SKILL.md` says.
|
|
48
|
-
|
|
49
|
-
- **One turn.** Do the whole plan in this one turn. Never end the turn to ask a question or to report progress: nobody will reply, and ending the turn ends the run.
|
|
50
|
-
- **Frontier rounds.** Work in rounds. A round is every open question that does not depend on an unanswered one. Write each as you would put it to the human, with your recommended answer and the main alternative, then accept your own recommendations and record them. Then ask what those answers opened: that is the next round. Write each round to `decisions.md` as it is decided (§2), not at the end, so a run that dies keeps what it had.
|
|
51
|
-
- **Depth rule.** Record a decision only if it changes what the feature is or does for its users, or the human might reasonably overturn it. Anything an engineer would settle while building (wording, internal formats, test coverage) is not a question; it goes to the spec as detail.
|
|
52
|
-
- **Exactly one challenger.** When a round would be empty, run one separate agent: the Agent tool in Claude Code, `spawn_agent` in Codex. Give it only the brief and `decisions.md`. Ask it for (a) design branches never asked, within the depth rule, and (b) decisions it would have made differently, with its reason. Do not tell it what you expect to hear. Ask its branches as one more round and decide them, with whatever they open. A decision it disagrees with is marked challenged with its reason (§2), and your own answer stands. There is no second pass, and no requirement that the pass come back empty. If you have no tool that runs a separate agent, challenge your own plan as a separate pass and write in `decisions.md` that the challenge was not independent.
|
|
53
|
-
- **A refused tool is not a wait.** If a tool is refused, do not retry it and do not wait: decide on what you know, and treat that decision as a candidate to check first.
|
|
54
|
-
- **A timed-out tool is not a refused one.** A refusal is runcastle's answer; a timeout is no answer, and the call may have landed anyway. Retry it once, checking first that it did not land: `complete_phase` is safe to repeat (a step already reported comes back ok with a note), but before repeating `emit_tickets`, `update_ticket` or `cancel_ticket`, read `list_tickets` and send only what is missing. If the retry times out too, carry on and say so in the closing message. Never leave `complete_phase({ phase: "tickets" })` on a timeout: until it lands, Go is not armed.
|
|
55
|
-
- **Check first.** Pick at most five decisions for the human to check first: guesses about the human's intent that the brief did not settle, decisions that force several others, and decisions the challenger disagreed with. They open the preview's decisions part (`./references/preview.md`).
|
|
56
|
-
- **No design.** A self-planned feature never opens a design (`../design/SKILL.md`): nobody is there to look.
|
|
57
|
-
- **Straight through.** Then draw the preview and run its self-check (§3, steps 1 and 2), but do not tell the human to look and do not wait: complete ideation (§4), run `/runcastle:spec`, then `/runcastle:tickets` (§5). End with one closing message (§6) that covers the plan only: the plan was made without the human, how many decisions it holds, and which are to check first, plus anything that stopped you doing the job (a refused or failed tool the plan needed). Leave out the state of tools and connectors the plan did not need.
|
|
58
|
-
|
|
59
|
-
**Designing UI.** When the feature changes how a screen looks, offer design in your first reply, before grilling about layout in words. When a decision is about how a screen looks, show the real component instead of describing it. Follow `../design/SKILL.md`: it covers the preview page, the panel, Tweaks and committing what the human approves. Grill-path only.
|
|
60
|
-
|
|
61
|
-
## 2. Lock decisions incrementally
|
|
62
|
-
|
|
63
|
-
The moment a decision locks, append it to `docs/features/<slug>/decisions.md` — **immediately, one at a time, never batched at the end.** If this session dies mid-grill, the locked decisions must already be on disk. Append entries in this shape:
|
|
64
|
-
|
|
65
|
-
```markdown
|
|
66
|
-
## <n>. <short decision title>
|
|
67
|
-
**Decision:** <the choice, in the project's domain vocabulary>
|
|
68
|
-
**Why:** <the reason / the trade-off chosen over the alternatives>
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
A decision you made without the human (the self-planning path, §1a) keeps that shape and adds one line naming the main alternative it was chosen over. A decision the challenger disagreed with adds a further line with its reason:
|
|
72
|
-
|
|
73
|
-
```markdown
|
|
74
|
-
## <n>. <short decision title>
|
|
75
|
-
**Decision:** <the choice>
|
|
76
|
-
**Why:** <the reason>
|
|
77
|
-
**Assumed.** Alternative: <the main alternative>.
|
|
78
|
-
**Challenged:** <the challenger's reason>.
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
For example:
|
|
82
|
-
|
|
83
|
-
```markdown
|
|
84
|
-
## 4. Tags and dates are left out of the PDF
|
|
85
|
-
**Decision:** The PDF carries the title and body only.
|
|
86
|
-
**Why:** The brief asks for the note, not its filing.
|
|
87
|
-
**Assumed.** Alternative: a small footer with tags and the last-edited date.
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
When the human later confirms or changes such a decision, the session doing the edit removes its `**Assumed.**` and `**Challenged:**` lines; from then on it reads as any other decision.
|
|
91
|
-
|
|
92
|
-
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.
|
|
93
|
-
|
|
94
|
-
## 3. Draw the preview, then wait for the human to look
|
|
95
|
-
|
|
96
|
-
**The preview is what the human confirms shared understanding on.** It is one self-contained page, `docs/features/<slug>/preview.html`, that shows the feature and every decision; the human reads the feature by looking at it.
|
|
97
|
-
|
|
98
|
-
1. **Draw it.** When the decisions are locked, read `./references/preview.md` (the rules) and write `preview.html` from `./references/preview-starter.html`.
|
|
99
|
-
2. **Check it against `decisions.md`.** Every decision appears by its number and title. Fix the preview before anyone sees it.
|
|
100
|
-
|
|
101
|
-
On the self-planning path (§1a) steps 1 and 2 are the whole of this section: go straight on to §4. Steps 3 to 5 are the grill path.
|
|
102
|
-
|
|
103
|
-
3. **Tell the human to look, then wait.** Say the preview is on the feature's **Overview**, under the Go row, and that the "Preview ready" notice in Chat leads there. Then stop and wait for their answer.
|
|
104
|
-
4. **A correction is a decision edit plus a redraw.** Edit the decision in `decisions.md` and redraw the preview in the same move, then ask them to look again.
|
|
105
|
-
5. **Only after the human confirms**, complete ideation (§4).
|
|
106
|
-
|
|
107
|
-
**Never skip the preview on your own judgement** — not because the feature is small, not because the grill went quickly. A small feature gets a small preview. **If the human says to skip it**, write no file and go straight on to §4.
|
|
108
|
-
|
|
109
|
-
**The redraw rule.** While the feature is at planning, any later edit to `decisions.md` — in this session or after it — updates `preview.html` in the same move, before anything else.
|
|
110
|
-
|
|
111
|
-
## 4. Complete ideation
|
|
112
|
-
|
|
113
|
-
On the grill path, when the human confirms shared understanding on the preview and the open questions are answered or explicitly deferred. On the self-planning path (§1a), as soon as the preview passes its check, without waiting:
|
|
114
|
-
|
|
115
|
-
`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.
|
|
116
|
-
|
|
117
|
-
## 5. Spec → tickets — stay in this window
|
|
118
|
-
|
|
119
|
-
Invoke `/runcastle:spec` (it writes `spec.md` and completes the `spec` phase); when it returns, invoke `/runcastle:tickets`.
|
|
120
|
-
|
|
121
|
-
Do not open a new session for these. They run here, on top of everything you just learned.
|
|
122
|
-
|
|
123
|
-
## 6. Close — hand off to the human
|
|
124
|
-
|
|
125
|
-
`/runcastle:tickets` ends by emitting tickets to the runcastle store. When it returns, your job is **done**. Tell the human plainly:
|
|
126
|
-
|
|
127
|
-
> Tickets are in the runcastle UI. Review the ticket cards and click **Go** — the feature's agent takes it from there, dispatching rounds of workers. That is the next step — I will stop here.
|
|
128
|
-
|
|
129
|
-
On the self-planning path (§1a), the closing message also says that the plan was made without the human, how many decisions it holds, and which are to check first, by number and title. It covers the plan only. Report anything that stopped you doing the job: a tool the plan needed that was refused or failed, and what you decided without it. Do not report the state of tools and connectors the plan did not need, such as MCP servers that failed to connect or connectors waiting to be authorised: the session inherits the human's own, and they are not part of the plan. Then end the turn; the run is done.
|
|
130
|
-
|
|
131
|
-
Do **not** start building. Go and Merge are the only two clicks in the whole pipeline, and both belong to the human. (You do not write code here either — that rule is in your injected prompt and enforced by the edit guard, not restated as advice.)
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: lead
|
|
3
|
-
description: The runcastle feature lead: after Go, run rounds of workers until the feature is ready for the human to drive.
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Lead — running a feature in rounds
|
|
8
|
-
|
|
9
|
-
The human approved this feature's plan with **Go**. From here the feature is yours to build: you send out rounds of workers, each in its own sandbox, you are woken when a round comes back, you check what it did, and you plan the next one, until the feature is worth the human's time to drive. The human's whole picture is: talk to you, Go, rounds run, "Ready for you" with what to test, they drive and tell you what they found, more rounds or **Merge**. Keep to that picture; do not hand them anything outside it.
|
|
10
|
-
|
|
11
|
-
Everything below is guidance, not a rule the machine enforces. Use your judgement where it says so. What the server does refuse is listed under **What the server refuses**, and its refusals say why.
|
|
12
|
-
|
|
13
|
-
## 0. Orient
|
|
14
|
-
|
|
15
|
-
Call `mcp__runcastle__get_feature_context`. Besides the docs and the tickets, it carries what you run on:
|
|
16
|
-
|
|
17
|
-
- `rounds` — every round so far, numbered from 1: the tickets it held, how it ended, its gates.
|
|
18
|
-
- `latestReady` — your newest ready declaration, if any.
|
|
19
|
-
- `driveActive` — the human is test-driving this feature right now.
|
|
20
|
-
- `stopped` — the human pressed Stop and has not replied since (see §4).
|
|
21
|
-
- `waitingReason` — why the feature is waiting on the human, if it is.
|
|
22
|
-
- `burnConcurrency` — how many workers the project runs at once.
|
|
23
|
-
|
|
24
|
-
If this conversation resumed, you may already have the plan in context. Trust the record over your memory of it.
|
|
25
|
-
|
|
26
|
-
## 1. Delegate by default; do it yourself when it is small
|
|
27
|
-
|
|
28
|
-
A round is how work gets done here: each worker gets one ticket, a clean sandbox and the full brief. Send work out by default.
|
|
29
|
-
|
|
30
|
-
Do a change yourself when it is small enough not to need a ticket — a one-line fix a round left behind, a rename, a config value, a change that would not split into tickets at all. From Go on you may edit code in this checkout and install dependencies to check it. Before you commit:
|
|
31
|
-
|
|
32
|
-
- **Verify it with the project's own checks** — its typecheck and its tests, the same commands the workers run. Commit only green work.
|
|
33
|
-
- Use a conventional subject (`feat(scope): …`, `fix(scope): …`). **Never reword a `ticket(<seq>): …` or `runcastle: …` commit**: those belong to the machinery, and other branches already carry them.
|
|
34
|
-
- Your commits land on the feature branch through the same landing queue as a worker's. During a round they land between the workers' landings; while the human is driving the feature they wait until the drive stops.
|
|
35
|
-
|
|
36
|
-
If a "small" change keeps growing, stop and make it a ticket.
|
|
37
|
-
|
|
38
|
-
**Design.** When the human wants to see or change how a screen looks, show it live instead of sending a round: follow `../design/SKILL.md` (a preview page, the panel beside the chat, Tweaks). A design you commit takes the normal landing path above.
|
|
39
|
-
|
|
40
|
-
## 2. Plan a round
|
|
41
|
-
|
|
42
|
-
1. **Write the tickets** with `emit_tickets`, or reshape pending ones with `update_ticket` / `cancel_ticket`, following the tickets skill. Then **dispatch** with `dispatch_round({ seqs })`, naming exactly the pending tickets this round covers. Tickets you do not name wait for a later round. A ticket may stay blocked by tickets that landed or were cancelled in an earlier round: those count as satisfied, so keep those edges rather than re-emitting tickets. A blocker that is still pending or failed must be named in the same round.
|
|
43
|
-
2. **Every worker task names its deliverable and how it is checked.** A worker with no stated check reports success on its own terms. Say what is built and what proves it: the test that goes green, the command that passes, the screen that shows it.
|
|
44
|
-
3. **Title tickets `{group}: {task}`** — `Checkout: apply the coupon at payment`. The group is the area of the feature; the task is the one thing this worker does.
|
|
45
|
-
4. **Keep the round to `burnConcurrency` workers or fewer.** That is a soft cap, not a limit: workers past it only queue. A wider round also has more to reconcile when it comes back.
|
|
46
|
-
5. **Contract first when the work spans layers.** When workers on different layers must agree on a shape — types, an API, a schema — make one ticket land that contract in code (types, stubs, the route signature) and make the others `blockedBy` it in the same round. The engine runs the contract first and the rest in parallel against it. **Contracts live in code, never in a doc**: a document is a second thing to keep true, and workers trust the code.
|
|
47
|
-
6. **UI work names the design references.** A ticket that touches UI tells its worker which design docs to follow — the project's own design system docs, and any design references in this feature's docs directory — and asks it to plan the screen, build it, then critique it against those docs with screenshots before it finishes.
|
|
48
|
-
|
|
49
|
-
Workers do not talk to each other or to you. They report through their digests, their ticket status and their commits. A worker that finds the contract wrong stops and says so in its digest; reconciling it is your job, between rounds.
|
|
50
|
-
|
|
51
|
-
## 3. When a round comes back
|
|
52
|
-
|
|
53
|
-
You are woken with the round's outcome, each ticket's status, the verify-gate result, any review findings and where the digests are.
|
|
54
|
-
|
|
55
|
-
1. **Trust the record over the prose.** Ticket status, the gates and the commits are what happened; a digest is a worker's account of it. Read each worker's digest with `get_ticket({ seq })`, and where the two disagree, believe the record.
|
|
56
|
-
2. **Reconcile.** Did a worker report the contract wrong, or change something another worker relied on? Did a decision turn out wrong? Fix the contract in code, amend `decisions.md` and `spec.md` when what was decided changed, and update the pending tickets. A contract reported wrong is a reason to re-plan, not to push on.
|
|
57
|
-
3. **Decide the next move**:
|
|
58
|
-
- small leftovers → fix them yourself (§1);
|
|
59
|
-
- more work, or a failed ticket to retry → plan the next round (§2);
|
|
60
|
-
- a question only the human can answer → ask it in chat and call `wait_for_you({ why })`;
|
|
61
|
-
- the feature does what the plan says → get it reviewed and declare ready (§6).
|
|
62
|
-
|
|
63
|
-
A failed round is just what came back. Re-plan, retry or fix it yourself.
|
|
64
|
-
|
|
65
|
-
## 4. When to ask
|
|
66
|
-
|
|
67
|
-
There is no switch telling you how far to go on your own; you judge it.
|
|
68
|
-
|
|
69
|
-
- **Carry on through anything the plan already settled.** A fix round for what came back, a retry, a small fix you do yourself, the next round the plan already describes — do it, without asking.
|
|
70
|
-
- **Stop and ask before anything the plan did not settle**: a design decision, a scope change, a trade-off the human would have an opinion on. Say in chat what you need decided and what you would do, call `wait_for_you({ why })` with a few words ("which coupon rule wins?"), and end your turn. Their reply is the answer, and it may change the round.
|
|
71
|
-
- **After the human presses Stop, wait for their reply.** Dispatch nothing until they write to you; the server refuses a dispatch until then anyway.
|
|
72
|
-
|
|
73
|
-
## 5. The runaway guard
|
|
74
|
-
|
|
75
|
-
When a round makes no progress on the same failure as the round before it, **stop**. Do not dispatch a third attempt at the same thing. Write the human a short status:
|
|
76
|
-
|
|
77
|
-
- **Stuck** — what is failing, in a sentence.
|
|
78
|
-
- **Tried** — what the last rounds did about it.
|
|
79
|
-
- **Needed** — what you need from them: a decision, access, a fact.
|
|
80
|
-
|
|
81
|
-
Then call `wait_for_you({ why: "stuck: <a few words>" })` and wait.
|
|
82
|
-
|
|
83
|
-
## 6. Ready for the human
|
|
84
|
-
|
|
85
|
-
1. **Review first.** Run a round that includes a review ticket (`kind: "review"`, as the tickets skill describes) after the last change to the work. Its findings come to you: fix them in a round, fix them yourself, or tell the human about them.
|
|
86
|
-
2. **Declare ready** with `declare_ready({ scenario, whatToTest })`:
|
|
87
|
-
- `scenario` — a short title for the drive: "Checkout with a coupon".
|
|
88
|
-
- `whatToTest` — a short paragraph aimed at the drive: **what changed, where to look, and what would count as broken.** "Coupons now apply at payment. Open checkout with a cart over $20 and enter SAVE10: the total should drop by 10% before you pay. A coupon that applies twice, or a total that changes after payment, is broken."
|
|
89
|
-
3. **Relay every warning it returns** — a last round with failures, work nothing has reviewed since it changed. It never refuses; the warnings are yours to pass on.
|
|
90
|
-
|
|
91
|
-
## 7. While and after the human drives
|
|
92
|
-
|
|
93
|
-
Never dispatch while the feature is being driven (`driveActive`); the server refuses anyway. Your own commits wait until the drive stops.
|
|
94
|
-
|
|
95
|
-
The human's notes reach you as one message, with screenshots attached as files you can read, or as plain chat. Reply first with what you will do about each note, then do it: fix it yourself or run rounds. For a bug they report, reproduce it or trace it to a file and line before you card a fix; if you can do neither, make reproducing it the ticket's first acceptance criterion. "All good" is a valid message too. When you are satisfied again, declare ready with a new scenario.
|
|
96
|
-
|
|
97
|
-
## What is not yours
|
|
98
|
-
|
|
99
|
-
- **Merge is the human's.** So is starting a backlog feature. Never merge, and never start one.
|
|
100
|
-
- **Workers are not yours to talk to.** Coordinate through tickets and rounds only.
|
|
101
|
-
|
|
102
|
-
## What the server refuses
|
|
103
|
-
|
|
104
|
-
`dispatch_round` is refused, with the reason, while a round is already running, while the human is driving this feature, and after the human pressed Stop until they have replied. It is also refused when a ticket you name is blocked by a ticket outside the round that is still pending or failed (or does not exist): the refusal lists each such edge, and the fix is to name that blocker in the round too. `stop_round` stops the running round; work that already landed stays.
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
---
|
|
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); a revisit never moves the feature. Entry skill for kind=revisit sessions.
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Revisit
|
|
8
|
-
|
|
9
|
-
The human came back: they remembered a constraint, changed their mind, or learned something that the grilling/spec didn't capture. This session's job is to make the **record** and the **ticket queue** match the new reality — nothing more. The pipeline does not move; whatever phase the feature is in, it stays in.
|
|
10
|
-
|
|
11
|
-
Once the human gives Go the feature's chat is its lead and runs on the lead skill instead; this one serves a feature still in planning, a shipped one, and the errands your briefing names (a merge conflict to resolve, the orchestrator's message).
|
|
12
|
-
|
|
13
|
-
## Order of operations
|
|
14
|
-
|
|
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, 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
|
-
3. **Docs.** Capture the change as decision prose:
|
|
18
|
-
- Append to `docs/features/<slug>/decisions.md` under a dated `## Revisited <date>` heading — never rewrite old decisions, supersede them ("Supersedes: <old decision>").
|
|
19
|
-
- **Clear the assumed mark.** A decision the session made without the human carries an `**Assumed.**` line, and perhaps a `**Challenged:**` line. When the human confirms or changes such a decision, remove both lines from it in the same edit. Decisions the human did not touch keep theirs.
|
|
20
|
-
- **A change on the orchestrator's message is not the human's.** When your briefing says the message is from the orchestrator, the decision you change keeps its `**Assumed.**` line, or gains one, and adds `**Changed by the orchestrator:** <why>` with the orchestrator's reason. The human has still not seen it, and the record must say who decided. **A message that relays the human's own answer** is the human confirming: remove the marks from that decision as above, `**Changed by the orchestrator:**` included.
|
|
21
|
-
- If `spec.md` exists and the change touches it, amend the affected sections in place.
|
|
22
|
-
- **Redraw the preview.** When the feature is at `planning` and `docs/features/<slug>/preview.html` exists, update `preview.html` in the same move as the decisions edit, before anything else, following `../ideate/references/preview.md`, check-first group included: a decision that lost its assumed mark leaves that group. The human reads the plan off the preview, so a preview that disagrees with `decisions.md` misleads them at Go.
|
|
23
|
-
4. **Ticket surgery.** Walk the ticket list against the new reality:
|
|
24
|
-
- Stale but still needed → `mcp__runcastle__update_ticket({ id, ...changed fields })` (pending/failed only).
|
|
25
|
-
- No longer needed → `mcp__runcastle__cancel_ticket({ id, reason })` (pending/failed only).
|
|
26
|
-
- New work required → `mcp__runcastle__emit_tickets({ tickets })` (batch, same shape as ideation). Whether a ticket ships a scenario follows the same rule as planning: `../tickets/SKILL.md`, "Ship a scenario when the state is hard to reach".
|
|
27
|
-
- `done` work now wrong → emit a NEW ticket that corrects it. Never touch done/burning tickets.
|
|
28
|
-
5. **Close.** `mcp__runcastle__record_event({ type: "feature.revisited", message: "<one-line gist of what changed>" })`, then tell the human what state you left things in — especially, at planning, whether the plan is ready for them to click **Go**.
|
|
29
|
-
|
|
30
|
-
## Designing UI
|
|
31
|
-
|
|
32
|
-
When the human wants to see how a screen looks or change it, show the real component rather than describing it. When what they brought changes a screen, offer it before they ask. Follow `../design/SKILL.md`: a preview page, the panel beside the chat, and a `design: <what>` commit once they approve. Approved design is committed before Go.
|
|
33
|
-
|
|
34
|
-
## Stand on the failure
|
|
35
|
-
|
|
36
|
-
Every bug the human reports gets exactly one of three answers before you card a fix for it:
|
|
37
|
-
|
|
38
|
-
- **Reproduced** — you drove it and saw the failure. Say what you did and what happened.
|
|
39
|
-
- **Traced** — you found it in the code. Name the file and line, and what there is wrong.
|
|
40
|
-
- **Unreproduced** — you could not do either. Say so plainly, and then make reproducing it the **first** acceptance criterion of the ticket that fixes it, so the burner stands on the failure before it changes anything.
|
|
41
|
-
|
|
42
|
-
Never card a fix for a failure nobody has stood on: a ticket written from a one-line report and a guess sends an agent to change the code it guessed at, and the next drive hits the same bug. This is what the human's report buys — it is the report, not a diagnosis.
|
|
43
|
-
|
|
44
|
-
## Do NOT
|
|
45
|
-
|
|
46
|
-
- **Never call `complete_phase`** — a revisit has no planning steps of its own to report; the human's clicks move the feature.
|
|
47
|
-
- **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.)
|
|
48
|
-
|
|
49
|
-
## Scope check
|
|
50
|
-
|
|
51
|
-
If what the human brings is not an amendment but a whole new capability, say so — and park it rather than losing it:
|
|
52
|
-
|
|
53
|
-
> That reads like a new feature, not a revision of this one. I'll put it in the backlog so it isn't lost; you can start it from the runcastle UI when you want it, and it gets grilled there.
|
|
54
|
-
|
|
55
|
-
`mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` — a backlog feature (status `draft`), no branch cut and nothing written, with the `brief` carrying why you deferred it and what it must not swallow. `draft: true` is the only door open to you; a full create belongs to the project session.
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
---
|
|
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.
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
---
|
|
6
|
-
<!-- Forked from Matt Pocock's to-spec skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
|
|
7
|
-
|
|
8
|
-
# Spec
|
|
9
|
-
|
|
10
|
-
Turn the ideation conversation plus `docs/features/<slug>/decisions.md` into a spec at `docs/features/<slug>/spec.md`. **Do NOT interview the human** — you are inside the unbroken ideation window; synthesize what you already know. Do not compact or clear.
|
|
11
|
-
|
|
12
|
-
## Process
|
|
13
|
-
|
|
14
|
-
1. **Load state.** `mcp__runcastle__get_feature_context` for the feature, the locked `decisions.md`, and any existing `spec.md` (this may be a re-run). A canonical doc too large to inline arrives in `notInlined` instead of `docs[]` — read it (`mcp__runcastle__read_feature_doc({ relPath })` or the file at its `absPath`) before writing a word; tickets arrive as summary rows with their `goal`, and `mcp__runcastle__get_ticket({ seq })` holds a ticket's context, acceptance criteria and digest. Explore the codebase for its current state if you have not already; use the project's domain vocabulary throughout and respect any ADRs in the area you touch.
|
|
15
|
-
|
|
16
|
-
2. **Sketch the seams** — the public boundaries the feature will be tested at. Prefer *existing* seams to new ones; use the *highest* seam possible; the fewer across the codebase, the better — the ideal is one. If new seams are needed, propose them at the highest point you can. These seams flow straight into the tickets and the burner, so get them right here.
|
|
17
|
-
|
|
18
|
-
3. **Write `spec.md`** with exactly these sections:
|
|
19
|
-
|
|
20
|
-
```markdown
|
|
21
|
-
# <Feature title>
|
|
22
|
-
|
|
23
|
-
## Problem
|
|
24
|
-
The problem the user faces, from the user's perspective.
|
|
25
|
-
|
|
26
|
-
## Approach
|
|
27
|
-
The solution from the user's perspective, then the shape of it: modules built/modified, their interfaces, schema/API/contract decisions, key interactions. Prose, not a file-by-file plan.
|
|
28
|
-
|
|
29
|
-
## Seams
|
|
30
|
-
The interfaces to test at (from step 2) — each named, with what it lets you observe. Flag which are existing vs. new.
|
|
31
|
-
|
|
32
|
-
## Out of scope
|
|
33
|
-
What this feature explicitly does not cover.
|
|
34
|
-
|
|
35
|
-
## Open questions
|
|
36
|
-
Anything still unresolved or deliberately deferred.
|
|
37
|
-
|
|
38
|
-
## Later laps
|
|
39
|
-
Optional — include it when this is a thin lap 1. Scope consciously deferred
|
|
40
|
-
to a later lap; it seeds the next lap's session alongside the test notes.
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Do **not** hardcode specific file paths or code snippets — they go stale. Exception: if the ideation produced a snippet that pins a decision more precisely than prose can (a state machine, reducer, schema, type shape), inline the decision-rich parts and note it came from that discussion.
|
|
44
|
-
|
|
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
|
-
|
|
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.
|