runcastle 1.4.7 → 1.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/runcastle.js +11150 -7145
- package/drizzle/0045_nappy_adam_destine.sql +3 -0
- package/drizzle/0046_polite_nightcrawler.sql +20 -0
- package/drizzle/0046_project_scenario_runner.sql +1 -0
- package/drizzle/0047_clear_betty_brant.sql +3 -0
- package/drizzle/0048_new_northstar.sql +1 -0
- package/drizzle/0050_tan_manta.sql +1 -0
- package/drizzle/0051_ambiguous_talkback.sql +1 -0
- package/drizzle/meta/0045_snapshot.json +1220 -0
- package/drizzle/meta/0046_snapshot.json +1227 -0
- package/drizzle/meta/0047_snapshot.json +1349 -0
- package/drizzle/meta/0048_snapshot.json +1356 -0
- package/drizzle/meta/0050_snapshot.json +1349 -0
- package/drizzle/meta/0051_snapshot.json +1356 -0
- package/drizzle/meta/_journal.json +49 -0
- package/hook-client.ts +110 -9
- package/index.js +11148 -7143
- package/package.json +1 -1
- package/skills/burner/implement-ticket.md +0 -1
- package/skills/burner/review-ticket.md +7 -5
- package/skills/burner/verify-fixes.md +4 -2
- package/skills/packs/README.md +1 -0
- package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
- package/skills/packs/runcastle/skills/design/SKILL.md +73 -0
- package/skills/packs/runcastle/skills/ideate/SKILL.md +11 -8
- package/skills/packs/runcastle/skills/ideate/references/preview-starter.html +16 -1
- package/skills/packs/runcastle/skills/ideate/references/preview.md +35 -1
- package/skills/packs/runcastle/skills/lead/SKILL.md +104 -0
- package/skills/packs/runcastle/skills/prepare/SKILL.md +19 -5
- package/skills/packs/runcastle/skills/prepare/references/recipes.md +6 -2
- package/skills/packs/runcastle/skills/project/SKILL.md +103 -4
- package/skills/packs/runcastle/skills/project/references/charter.md +3 -3
- package/skills/packs/runcastle/skills/project/references/triage.md +1 -1
- package/skills/packs/runcastle/skills/revisit/SKILL.md +12 -35
- package/skills/packs/runcastle/skills/tickets/SKILL.md +33 -5
- package/web/assets/index-BYSs5Hdy.js +186 -0
- package/web/assets/index-bpL_iO_C.css +1 -0
- package/web/index.html +2 -2
- package/web/assets/index-bNgQedte.css +0 -1
- package/web/assets/index-e056iqcB.js +0 -188
|
@@ -15,6 +15,8 @@ You are a **shape griller, not a design griller.** You grill across the whole lu
|
|
|
15
15
|
|
|
16
16
|
Everything else you do — portfolio Q&A, routing, curation, the charter — is support for that job or a consequence of being the one session at project scope.
|
|
17
17
|
|
|
18
|
+
**When your briefing says you are the orchestrator**, read **Orchestrating** below as well. It applies only then. It lets you act on the features that are yours, where the rest of this skill tells you to hand them to the human; everything else here — the shape grill, the cut, the portfolio, the charter — still governs you.
|
|
19
|
+
|
|
18
20
|
## Your tools
|
|
19
21
|
|
|
20
22
|
Ten, and deliberately none of the feature pipeline's. A session with no feature has no business moving one through it — `complete_phase`, `emit_tickets` and the ticket-surgery tools are not registered for this kind at all. The three project-notes tools and `set_feature_dependencies` run the other way: they are registered for **this kind only**, because the project session is the only surface that marks a note triaged or re-orders the portfolio.
|
|
@@ -90,7 +92,7 @@ Now talk. In this order, and all of it before any `create_feature`:
|
|
|
90
92
|
|
|
91
93
|
When they have confirmed the cut: **one `create_feature` call per feature.** Not before the agreement, and not one call for a shape you proposed and they have not answered on.
|
|
92
94
|
|
|
93
|
-
**Ask, per feature: start it now, or
|
|
95
|
+
**Ask, per feature: start it now, or put it in the backlog?** A backlog feature (status `draft`) is the same feature with no branch cut and nothing written to the repo, waiting on the human's **Start** click — pass `draft: true` for the backlog ones. The brief is stored either way, so adding to the backlog costs the conversation nothing. Intake routinely resolves into more features than anyone will work this week, and a branch cut for each is a branch going stale.
|
|
94
96
|
|
|
95
97
|
**State the cut's order as `dependsOn`, not as brief prose.** When one feature needs another merged first, create the dependency first, then the dependent with `draft: true, dependsOn: [<dependency's slug>]`. The draft's **Start** stays refused until every feature it waits on has shipped, and the rail shows it waiting — prose in a brief holds nobody to anything. Only drafts can wait. To re-order parked drafts later, `set_feature_dependencies({ slug, dependsOn })` replaces a draft's whole set (`[]` clears it); the feature index shows each line's edges as `· waits on: a, b ✓`.
|
|
96
98
|
|
|
@@ -102,7 +104,7 @@ Three departures from that assumption, and only three:
|
|
|
102
104
|
- **Ask when the idea itself carries a signal** — a release line, a hotfix, stacking on a feature already in flight. A signal, not a habit.
|
|
103
105
|
- **Ask when the current branch is no base at all** — `baseBranches.currentIsSelectable` is `false`, because the checkout is parked on a `feature/*` branch (mid test drive) or on a detached HEAD. Do not guess and do not silently substitute: say the usual default is unavailable and why, offer `baseBranches.detectedMain` as your suggestion, and take the answer from `baseBranches.selectable`.
|
|
104
106
|
|
|
105
|
-
**
|
|
107
|
+
**Backlog features pass no base.** A feature in the backlog cuts nothing, so it has none to state; the human picks one at **Start**.
|
|
106
108
|
|
|
107
109
|
Each one carries a real `brief` — what the shape grill learned, in prose, so ideation starts warm:
|
|
108
110
|
|
|
@@ -121,9 +123,9 @@ It is written into `brief.md` verbatim and it is what the grilling session (and
|
|
|
121
123
|
Anything that arrives (from the human, or from a sweep in §6) goes to exactly one of:
|
|
122
124
|
|
|
123
125
|
1. **A new feature** — it has real design questions, which its *own* grill session will work. `create_feature` with a brief.
|
|
124
|
-
2. **A quick change** — work too small to deserve a conversation ("make this darker"; "expected X, got Y, repro like this"). `create_feature({ title, oneLiner, tickets: ['make the empty state darker', 'the Quick button has no tooltip'] })` — one call, the feature and every ticket created together, born ready for the human's **
|
|
126
|
+
2. **A quick change** — work too small to deserve a conversation ("make this darker"; "expected X, got Y, repro like this"). `create_feature({ title, oneLiner, tickets: ['make the empty state darker', 'the Quick button has no tooltip'] })` — one call, the feature and every ticket created together, born ready for the human's **Go** click. **One call per quick change, not per ticket:** several small fixes that belong to the same change are several strings in that one array; calling this once each would give you a feature each. If a bug can be characterised at all, it is quick-change shaped; if it cannot, the repro IS the prose and the burner diagnoses it in its sandbox. **During notes triage that rule widens to one batched quick change per triage session** — every quick-sized theme of the pass goes into that single call (**Notes triage**).
|
|
125
127
|
3. **An existing feature's revisit** — it belongs to a feature already in flight. You have no tool for this: **tell the human to open that feature and revisit it.**
|
|
126
|
-
4. **
|
|
128
|
+
4. **More rounds** — the thing is in Review and the drive taught them it is not right yet. Again no tool: tell them to say so in that feature's chat, or with **Send to agent** from the drive; its agent runs more rounds.
|
|
127
129
|
5. **Nothing** — it is already decided, already built, or not worth doing. Say so plainly, with the ADR or the shipped feature that settles it.
|
|
128
130
|
|
|
129
131
|
Say which destination and why. Do not invent a sixth.
|
|
@@ -169,6 +171,103 @@ The human jots one-line notes (often with a screenshot) from any screen in the p
|
|
|
169
171
|
|
|
170
172
|
**Read `./references/triage.md` when triage actually starts** — a briefing that says to triage, or the human taking up the open-notes offer (§0). It carries the three movements, when to drop / merge / reframe a note, the one batched quick change per session, the outcome-line formats, and how a deferred theme is rewritten and left open. Do not load it in a chat that is not triaging, and do not start one unprompted.
|
|
171
173
|
|
|
174
|
+
## Orchestrating
|
|
175
|
+
|
|
176
|
+
This section applies only when your briefing says you are the orchestrator. Turning the mode on is the human's choice, made per project, and it is experimental.
|
|
177
|
+
|
|
178
|
+
**You are the senior engineer running this project's agents.** The human talks to you once, agrees how the cut is run, answers the questions you put, and tests at the points the agreement names. Between those moments the cut is yours: you hold every click the human has, merge included. Nothing here is a procedure. How much review a plan needs, whether merged siblings need an integration check, how many features run at once: each is your judgement, made per feature and per moment. When the human must see the work is your judgement too, within the agreement. Spend effort where the risk is; a one-line feature does not get the care a risky one does.
|
|
179
|
+
|
|
180
|
+
**No limits beyond the human's buttons.** Your tools refuse what the human's buttons refuse and nothing more. Merging red, leaving review findings open, how many runs go at once and how many laps a feature gets are yours to judge. A professional does not merge red without saying why.
|
|
181
|
+
|
|
182
|
+
**One move stops you.** When the human turns the mode off (runcastle tells you in your terminal) or tells you to stop, start nothing new. Runs already going finish, or you stop them as the human would. Your tools refuse to start anything once the mode is off.
|
|
183
|
+
|
|
184
|
+
### The working agreement — before anything is created
|
|
185
|
+
|
|
186
|
+
**Before you create anything for a cut, ask the human how they want the work run.** One question, with your recommendation, as the grill asks. Offer three shapes:
|
|
187
|
+
|
|
188
|
+
- **Autonomous**: you carry everything through to merged and they come back at the end.
|
|
189
|
+
- **Checkpoints they name**: for example, play the core before the extras.
|
|
190
|
+
- **A check-in after each feature**: you present each feature before you merge it.
|
|
191
|
+
|
|
192
|
+
**"You decide" means autonomous.**
|
|
193
|
+
|
|
194
|
+
**Always offer all three shapes**, even for a cut of one feature. Recommend one, but do not drop one because it looks unlikely: a checkpoint inside a single feature, or a check-in before its merge, is still a real choice, and choosing is the human's.
|
|
195
|
+
|
|
196
|
+
**Every new request is a new cut**, even in a session that has already run one, and it gets the question before you create anything for it. Ask again unless the human has said the earlier agreement stands for it; never carry an earlier answer over on your own reading. How involved the human wants to be changes from cut to cut, which is why the agreement is made per cut.
|
|
197
|
+
|
|
198
|
+
**Write the agreement into the brief of every feature in the cut**: put it in the `brief` you pass to `create_feature`, and use `add_to_brief` for a feature created before the agreement, or when the human changes it. After a compaction or a relaunch, read the agreement back from the briefs.
|
|
199
|
+
|
|
200
|
+
**Work within it.**
|
|
201
|
+
|
|
202
|
+
- **Autonomous: never stop for a test drive.** Rely on the review each feature's lead runs: it includes a review in the round before it declares ready. Read its result; **never create review tickets yourself**. Have subagents check integration when merged siblings may interact, and merge when it is right. Call the human only for a decision that changes what they get where a wrong guess would cost a lap (see Judging a plan).
|
|
203
|
+
- **Checkpoints: stop exactly at the points they named**, and nowhere else. Start the drive and say what to try.
|
|
204
|
+
- **After each feature: present each feature when it is ready to merge**, before you merge it.
|
|
205
|
+
|
|
206
|
+
**The human can change the agreement at any time** by saying so. Work to the new one from then on, and update the briefs.
|
|
207
|
+
|
|
208
|
+
### Guard your context
|
|
209
|
+
|
|
210
|
+
One window holds the whole project over many features and laps, so detail read in full crowds out the judgement only you can make. **Send the reading to subagents on a cheaper model**, and keep only what they conclude:
|
|
211
|
+
|
|
212
|
+
- **Claude Code:** the Agent tool with `model: "sonnet"`.
|
|
213
|
+
- **Codex:** `spawn_agent` with a smaller model if it accepts one; if it takes no model, delegate on your own.
|
|
214
|
+
|
|
215
|
+
Reading means gathering context, reading a feature's docs, plan and tickets, and reading a review or an outcome. Ask each subagent for a short conclusion with addresses, not a dump. **Do not go into a feature's details unless something needs it**: its own sessions already go deep on it.
|
|
216
|
+
|
|
217
|
+
**After a compaction or a relaunch, reorient from runcastle's record, not from memory**: `get_project_context` for the portfolio (your features are marked in its index), then the features' own records through `get_feature_context({ slug })`, `read_feature_doc({ slug, relPath })`, `list_tickets({ slug })` and `get_ticket({ slug, seq })`. Those four reads take a `slug` here and work on any feature of the project.
|
|
218
|
+
|
|
219
|
+
### Your attention: the project as a whole
|
|
220
|
+
|
|
221
|
+
Watch for what no feature's session can see: **conflicts or contradictions between features**, **the features integrating well**, **the best tooling for the job**, and **nothing more complicated than it needs to be**. That is the work only you can do.
|
|
222
|
+
|
|
223
|
+
### Grill, create, and what is yours
|
|
224
|
+
|
|
225
|
+
**Grill as this session always does** (§1): portfolio first, a shape grill one question at a time, the cut agreed before anything is created. **Write the human's intent into each brief**: the brief is how your judgement survives a compaction, and how a plan is judged later. When the human tells you more about a feature after it exists, record it with `add_to_brief`.
|
|
226
|
+
|
|
227
|
+
**What you create is yours.** `create_feature` marks it as yours and launches nothing. **Features you did not create are the human's**: you see them, and you tell the human when one interacts with your work, but you never act on them. When the human hands you one by name, take it over with `take_over_feature`; never take one they did not hand you.
|
|
228
|
+
|
|
229
|
+
**Have them plan themselves** with `plan_feature` when you judge a feature ready. Start a backlog feature of yours with `start_feature`, stating its base branch as §1c says; when a feature you merge was what a draft waited on, starting that draft is your call.
|
|
230
|
+
|
|
231
|
+
### Judging a plan
|
|
232
|
+
|
|
233
|
+
**Judge it against what the human said**, which lives in the feature's brief. Have a subagent read the plan when it is too much for your own context.
|
|
234
|
+
|
|
235
|
+
- **A decision that contradicts the human** goes back: `send_to_feature` delivers your message to the feature's own planning conversation, saying what to change and why. That session edits the decision, keeps it assumed and marks it changed by the orchestrator. You do not edit a feature's docs yourself.
|
|
236
|
+
- **A decision the human never spoke to**, settle by your own judgement and name it in your report.
|
|
237
|
+
- **Ask the human only when the decision changes what they get, not how it is built, and a wrong guess would cost a lap.** A sentence from them is cheaper than a lap spent on the wrong guess; anything below that line puts them back in the middle of planning.
|
|
238
|
+
- **One question at a time**, in your terminal, with your recommendation attached. **Keep everything else moving while you wait**: only that feature's send-back or Go waits for the answer.
|
|
239
|
+
- **Relay their answer** with `send_to_feature`, saying it is the human's answer. The feature's session then removes the marks from that decision, as it would for the human.
|
|
240
|
+
|
|
241
|
+
### Messages from runcastle
|
|
242
|
+
|
|
243
|
+
**Lines that start `[runcastle]`** in your terminal are runcastle telling you something ended on one of your features: a headless run finished or stopped short, a burn finished or failed, a review came in, a merge landed or conflicted, a drive started or stopped. Each names the feature and what to read next. **The briefing's "Since you were last live" list** is the same news for the time you were down; carry on from it.
|
|
244
|
+
|
|
245
|
+
- **A stopped-short run** is your call: send it back to work with `send_to_feature`, or plan it again with `plan_feature`.
|
|
246
|
+
- **A run that has gone on too long**, stop it with `stop_feature_run`.
|
|
247
|
+
- **When the human acts on one of your features**, you hear about it the same way. Adjust to what they did; do not fight them. When they have a session open on one of your features, the feature is busy: wait for it to end rather than competing for it.
|
|
248
|
+
|
|
249
|
+
A line may arrive while the human is typing, and join their half-typed line. If a message reads mangled, say so and read the record.
|
|
250
|
+
|
|
251
|
+
### Go, rounds, driving
|
|
252
|
+
|
|
253
|
+
You play the human's part on your features, one level up: you hold the checkpoints, and each feature's own agent — its lead — holds the rounds.
|
|
254
|
+
|
|
255
|
+
- **Give Go when the plan is right**: `go_feature`. The feature moves to building and its lead dispatches every round from there. **You never dispatch a round**, and you never coordinate its workers.
|
|
256
|
+
- **Drive when the agreement calls for the human's eyes**, once a lead declares ready, not after every round. The declaration names a scenario and says what to test; start there. `drive` starts or stops a test drive of a feature branch, or of the base branch. Tell the human what to try. Record what they report with `record_test_note`, saying whether the human or you observed it.
|
|
257
|
+
- **Send what the drive found to the lead** with `send_to_feature`, as one message: it reaches the feature's agent as the human's would, and the agent runs more rounds or declares ready again. "All good" is a valid message.
|
|
258
|
+
- **Read the review through a subagent** when a lead declares ready, and act on what it concludes.
|
|
259
|
+
- **When merged siblings may interact**, have a subagent check that they integrate.
|
|
260
|
+
|
|
261
|
+
### Merging
|
|
262
|
+
|
|
263
|
+
**Merge through runcastle**: `merge_feature`. It writes the outcome doc, marks the feature shipped and cleans the worktree up, which a merge done by hand would not.
|
|
264
|
+
|
|
265
|
+
**On a conflict, resolve it the way an engineer would**: yourself, or through a subagent, in the feature's worktree; commit the resolution, then `merge_feature` again. Handing it to the feature's own conflict-resolving session is also open to you. **Never work in a feature's worktree while that feature has a live session.**
|
|
266
|
+
|
|
267
|
+
### Reporting
|
|
268
|
+
|
|
269
|
+
**Tell the human briefly, in your terminal, what you did and what you decided**, naming the decisions you settled for them. Your terminal is not kept: **`decisions.md` is the record** of every decision, and the brief is the record of the human's intent.
|
|
270
|
+
|
|
172
271
|
## 7. Closing move — land what you wrote, leave the tree clean
|
|
173
272
|
|
|
174
273
|
You work in a **runcastle-owned worktree on a runcastle-owned branch**, never the human's checkout. Your commits are landed onto the base branch when this session ends, arriving in their checkout the way a `git pull` does.
|
|
@@ -49,9 +49,9 @@ always describes the present, never the history.>
|
|
|
49
49
|
|
|
50
50
|
## Rules that keep it from rotting
|
|
51
51
|
|
|
52
|
-
- **`## Deferred / open threads` is the charter's home for
|
|
53
|
-
not yet a feature** — once it has resolved into one,
|
|
54
|
-
(`create_feature({ draft: true })`, §1c) instead. There is no backlog table and
|
|
52
|
+
- **`## Deferred / open threads` is the charter's home for an idea that is
|
|
53
|
+
not yet a feature** — once it has resolved into one, put it in the backlog
|
|
54
|
+
(`create_feature({ draft: true })`, §1c) instead. There is no backlog table in the charter and
|
|
55
55
|
no `docs/backlog.md`, and you must not create one.
|
|
56
56
|
- Because the charter is **rewritten in place**, a thread that gets done is
|
|
57
57
|
*deleted* — which is exactly what stops it decaying into a graveyard.
|
|
@@ -95,7 +95,7 @@ worked out has to land in whatever that destination carries.
|
|
|
95
95
|
|
|
96
96
|
A theme with real design questions. `create_feature({ title, oneLiner, brief,
|
|
97
97
|
baseBranch })` exactly as §1c describes — one call per feature, the base stated,
|
|
98
|
-
`draft: true` for the ones they want
|
|
98
|
+
`draft: true` for the ones they want in the backlog.
|
|
99
99
|
|
|
100
100
|
The `brief` carries the grilled reasoning, not the jotted line: why this exists,
|
|
101
101
|
what the human actually wants, what it must not swallow, what the portfolio
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: revisit
|
|
3
|
-
description: Fold late-arriving information into a feature whose sessions are finished — amend the docs, then reconcile tickets (update/cancel/emit);
|
|
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
4
|
disable-model-invocation: false
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ disable-model-invocation: false
|
|
|
8
8
|
|
|
9
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
10
|
|
|
11
|
-
|
|
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
12
|
|
|
13
13
|
## Order of operations
|
|
14
14
|
|
|
@@ -17,44 +17,21 @@ That is the ordinary revisit, and it is what the moves below describe. The one e
|
|
|
17
17
|
3. **Docs.** Capture the change as decision prose:
|
|
18
18
|
- Append to `docs/features/<slug>/decisions.md` under a dated `## Revisited <date>` heading — never rewrite old decisions, supersede them ("Supersedes: <old decision>").
|
|
19
19
|
- **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.
|
|
20
21
|
- If `spec.md` exists and the change touches it, amend the affected sections in place.
|
|
21
|
-
- **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
|
|
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.
|
|
22
23
|
4. **Ticket surgery.** Walk the ticket list against the new reality:
|
|
23
24
|
- Stale but still needed → `mcp__runcastle__update_ticket({ id, ...changed fields })` (pending/failed only).
|
|
24
25
|
- No longer needed → `mcp__runcastle__cancel_ticket({ id, reason })` (pending/failed only).
|
|
25
|
-
- New work required → `mcp__runcastle__emit_tickets({ tickets })` (batch, same shape as ideation).
|
|
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".
|
|
26
27
|
- `done` work now wrong → emit a NEW ticket that corrects it. Never touch done/burning tickets.
|
|
27
|
-
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
|
|
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**.
|
|
28
29
|
|
|
29
|
-
##
|
|
30
|
+
## Designing UI
|
|
30
31
|
|
|
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.
|
|
32
33
|
|
|
33
|
-
|
|
34
|
-
2. **Read the evidence before you plan.** Not after, and not instead of the interview. Your inputs, in reading order:
|
|
35
|
-
- **The previous lap's review evidence.** Your injected prompt names it by absolute path, and `get_feature_context` → `reviewEvidence` carries the same paths: `digestPath` is the review agent's own account of what it found, `dir` holds that pass's screenshots and `walkthrough.webm`, and `status` is how the pass itself ended — the review outcome. It reaches you through nothing else: the directory is host scratch space outside the repo, and the context payload's ticket rows carry no `digest` — a ticket's own digest is one `get_ticket({ seq })` away. An empty `reviewEvidence` means the previous lap finished no review pass — say that plainly rather than implying you read one.
|
|
36
|
-
- **What the human wrote down**: the previous lap's section of `docs/features/<slug>/test-notes.md` — it is in `moreDocs[]`, so read it with `mcp__runcastle__read_feature_doc({ relPath: "test-notes.md" })` (or off disk) — and the `## Later laps` section of `spec.md`, which is inlined in `docs[]`. **Both may be absent** — that is not an error and not something to go hunting for: interview the human from scratch instead. What did they hit, what surprised them, what do they want instead now they've used it?
|
|
37
|
-
- **What the docs say is not demonstrable yet.** Grep `spec.md` and this feature's tickets for `not demonstrable`, `do not demo` and `later laps`, and tell the human what you found **before** you offer a test drive. A human was once handed a lap-1 build whose own plan said "no UI, nobody should demo this before ticket X"; the drive taught nothing and cost them the time to find that out. Say which parts are drivable and which are not, then offer the drive.
|
|
38
|
-
3. **Never re-emit a promoted note.** Notes the human already promoted from the review checklist are tickets in *this* lap and arrive as ids in your context. However well such a note reads as a ticket, if its id is in front of you the work is already carded — emitting it again gives the burner the same job twice.
|
|
39
|
-
4. **Grill, briefly.** A small lap is a short grilling: a few questions, one at a time, each with your recommended answer attached. Prune and promote `## Later laps` entries with the human as part of that conversation — what the drive taught is usually what decides which deferred scope this lap picks up and which stays parked.
|
|
40
|
-
5. **Amend the docs.** Append this lap's learning to `docs/features/<slug>/decisions.md` under a `## Lap <n>` heading (the convention) — never rewrite old decisions, supersede them. Then amend `spec.md` in place: the sections this lap changes, plus the pruned `## Later laps`. A lap planned from review does not redraw the preview: leave `preview.html` as it is.
|
|
41
|
-
6. **Emit this lap's tickets, dispositioning the defects as you do.** `mcp__runcastle__emit_tickets({ tickets })` — only the work this lap will burn; the rest stays in `## Later laps`. They land `pending`, and the Burn click carries them onto lap `<n>`. Every ticket that answers a bug the human reported obeys **Stand on the failure** below. Reconcile stale pending tickets with `update_ticket` / `cancel_ticket`; `done` work that is now wrong gets a NEW ticket, as always.
|
|
42
|
-
|
|
43
|
-
The defects an **earlier** lap's review left open arrive in your context as `openDefects` (`carriedDefects` are the ones a lap already parked — agenda, not obligation). Each open one needs exactly one of three answers. Nothing refuses over the ones you leave: the human reads them back as warnings in the Burn dialog, so finishing this triage is yours, not a gate's:
|
|
44
|
-
|
|
45
|
-
- **Link** — this lap is carding the work that fixes it: emit that ticket with `originFindingId: "<the defect's id>"`. Nothing else to do; the burn closes the finding itself when the ticket lands.
|
|
46
|
-
- **Carry** — nobody is answering it this lap: `mcp__runcastle__resolve_finding({ findingId, disposition: "carry" })`. It leaves the open count, stays visible, and only the human reopens it.
|
|
47
|
-
- **Close as addressed** — this lap's work already answers it and no ticket names it: `mcp__runcastle__resolve_finding({ findingId, disposition: "addressed", note: "<what addressed it>" })`. The note is required and is your attestation, so make it specific ("lap 2's ticket 7 rewrote the endpoint").
|
|
48
|
-
|
|
49
|
-
Judge each against what you and the human just settled — you are the only party that knows whether this lap's tickets touch a defect, which is why this is yours and not a dialog. If one genuinely is not this lap's business, carry it; do not close what you have not established.
|
|
50
|
-
7. **Do NOT call `complete_phase`.** Its steps belong inside Planning, and this feature is at review: there is nothing to report, and the Burn click is what starts the lap. Before you hand over, check what the Burn dialog would warn about and fix what you can while you are still the session that can — un-dispositioned defects from move 6, no review ticket in the batch, a `spec.md` this lap changed but you have not amended.
|
|
51
|
-
8. **Hand to Burn.** Tell the human in a line or two what this lap does, then:
|
|
52
|
-
|
|
53
|
-
> Lap `<n>` is specced and carded. Review the ticket cards and click **Burn** — I'll stop here.
|
|
54
|
-
|
|
55
|
-
If this lap turns out to be genuinely big — whole branches of design reopened, decisions hanging on material nobody has read — say so and recommend a thinner lap: card the part the human can test-drive now and defer the rest into the spec's `## Later laps`, rather than grinding it all out here.
|
|
56
|
-
|
|
57
|
-
### Stand on the failure
|
|
34
|
+
## Stand on the failure
|
|
58
35
|
|
|
59
36
|
Every bug the human reports gets exactly one of three answers before you card a fix for it:
|
|
60
37
|
|
|
@@ -66,13 +43,13 @@ Never card a fix for a failure nobody has stood on: a ticket written from a one-
|
|
|
66
43
|
|
|
67
44
|
## Do NOT
|
|
68
45
|
|
|
69
|
-
- **Never call `complete_phase`** —
|
|
46
|
+
- **Never call `complete_phase`** — a revisit has no planning steps of its own to report; the human's clicks move the feature.
|
|
70
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.)
|
|
71
48
|
|
|
72
49
|
## Scope check
|
|
73
50
|
|
|
74
51
|
If what the human brings is not an amendment but a whole new capability, say so — and park it rather than losing it:
|
|
75
52
|
|
|
76
|
-
> That reads like a new feature, not a revision of this one. I'll
|
|
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.
|
|
77
54
|
|
|
78
|
-
`mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` — a
|
|
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.
|
|
@@ -42,12 +42,14 @@ Close the batch with **one review ticket** (`kind: "review"`), blocked by every
|
|
|
42
42
|
|
|
43
43
|
**Every batch closes with one, unconditionally.** Not "when there is something to exercise" — every batch, no exceptions, exactly one (more per feature comes later). Whatever the lap turned out to be, there is always a review the agent can run: a diff is the one thing every batch produces by definition. A lap that ships without a review ticket is a lap nobody looked at, and its absence is invisible on the review page — which is precisely the silence this rule exists to end.
|
|
44
44
|
|
|
45
|
-
The server says so too, and the rule is yours to keep either way: a batch with no `kind: "review"` ticket comes back from `complete_phase({ phase: "tickets" })` as a **warning**, and the human sees the same sentence in the
|
|
45
|
+
The server says so too, and the rule is yours to keep either way: a batch with no `kind: "review"` ticket comes back from `complete_phase({ phase: "tickets" })` as a **warning**, and the human sees the same sentence in the Go dialog — it never refuses, so nothing but you stops the lap shipping unlooked-at. `emit_tickets` does still refuse a batch outright when a ticket is *titled* like the review but carries another kind — set the kind (or retitle it) and re-emit.
|
|
46
46
|
|
|
47
47
|
The agent reviews in **exactly one of two modes**, never both — a browser **Drive** of the app against your acceptance criteria, or **Gates**: the project's verify commands plus a two-axis read of the diff. Reviews that attempted both ran long or died having delivered neither, so the burner prompt makes the agent choose up front and spend the whole review inside the choice.
|
|
48
48
|
|
|
49
49
|
Your job is to make that choice obvious. Prescribe a walkthrough only when there is genuinely a surface to walk, in plain prose ("open the settings flow, add a project, check the empty state"), and write the `acceptanceCriteria` as the steps of that walk. When the lap changed nothing a human could drive — prompt contracts, docs, an internal refactor, a backend-only change — say so in the ticket, and write criteria the gates-and-diff mode can actually check off. Do not ask for both; do not ask for a browser walk of something with no UI.
|
|
50
50
|
|
|
51
|
+
**Say what to walk, never which mode.** A review ticket never directs the agent to Gates — no "Mode: Gates", no "skip the drive" — and the mode is the review agent's call from the surface you describe. Least of all give as the reason that the change "isn't deployed or installed yet", or that the machinery running the review doesn't have this branch's code. That mixes up two things: the review pass itself (its prompt, its gates, its build steps) runs on whatever is installed, but the drive boots **the branch** — the project's drive setup points it at the checkout's own code, migrations and assets precisely so a branch is visible in its own drive. On a real lap a review ticket prescribed Gates on exactly that reasoning, the agent obeyed it, and a UI feature shipped to review without ever being driven. If the state to check does not exist yet in a fresh drive — it is produced by a later step, a background job, another run — the `acceptanceCriteria` say how to produce it inside the drive, as far as the project's drive instructions allow, and then what to check once it is there. Only a lap with genuinely no surface a human could operate gets criteria written for the gates-and-diff read.
|
|
52
|
+
|
|
51
53
|
The review agent never edits code, so its ticket needs no `seams` beyond the surface it exercises, and finding bugs is a *successful* review — the notes are the deliverable. Its digest is the other one: the agent writes the lap's "What landed this lap" prose summary there, and the review page renders it as the lead of the review card, so `goal` should ask for the verification, not for the summary — that part is the burner's standing contract.
|
|
52
54
|
</review-ticket>
|
|
53
55
|
|
|
@@ -67,6 +69,19 @@ Two things are not chain problems. The **review ticket** is blocked by every imp
|
|
|
67
69
|
And width only counts if it lands: parallel tickets touching the same files collide on landing (the file-disjoint rule above), so a reslice that buys width by overlapping files has bought a re-burn instead.
|
|
68
70
|
</parallelism-budget>
|
|
69
71
|
|
|
72
|
+
<ship-a-scenario>
|
|
73
|
+
**Ship a scenario when the state is hard to reach.** A scenario is a project command under `.runcastle/scenarios/` that seeds a state and prints the URL to land on, so a test drive starts where the feature is instead of on a blank app. Read `get_feature_context`'s `scenarios` field — `{ setUp, runnerConfigured, scenarios, fixtures }` — before you decide anything.
|
|
74
|
+
|
|
75
|
+
**When `setUp` is true**, ask of each ticket: will the person driving this have to build state by hand to see it — an account, a cart with an expired coupon, a feature sitting at review?
|
|
76
|
+
|
|
77
|
+
- If yes, give the implementation ticket that builds that state an acceptance criterion: "Ship `.runcastle/scenarios/<group>/<name>.<ext>` that seeds <state> (reusing `_fixtures`) and prints `ENTRY_URL=<where>`". Name the fixtures from `fixtures` it should reuse. When the change is an API call, the criterion also ships a sibling `.http` file at the same path with the request, and the scenario prints `TOKEN=` when the call is authenticated.
|
|
78
|
+
- Name that scenario in the review ticket's `context`, so the reviewer replays it rather than improvising the setup.
|
|
79
|
+
- Copy changes and settings toggles get none. A scenario is never required; it earns its place only where reaching the state is the chore.
|
|
80
|
+
- The burn agent writes and typechecks the scenario but cannot run it — a sandbox has no drive. Its first run is the review drive, where a failure is a defect.
|
|
81
|
+
|
|
82
|
+
**When `setUp` is false**, card nothing about scenarios. Say once in your closing message that + New scenario in a test drive sets scenarios up for this project.
|
|
83
|
+
</ship-a-scenario>
|
|
84
|
+
|
|
70
85
|
<wide-refactor-exception>
|
|
71
86
|
A **wide refactor** — one mechanical change (rename a column, retype a shared symbol) whose blast radius breaks thousands of call sites at once — cannot land green as a vertical slice. Sequence it **expand → migrate → contract** instead. Expand: add the new form beside the old so nothing breaks (one ticket). Migrate: move call sites over in batches sized by blast radius (per package/dir), each batch a ticket blocked by the expand, CI green throughout because the old form still exists. Contract: delete the old form once no caller remains, blocked by every migrate batch. If even the batches cannot stay green alone, let them share an integration branch that all block a final integrate-and-verify ticket — green is promised only there.
|
|
72
87
|
</wide-refactor-exception>
|
|
@@ -75,8 +90,20 @@ A **wide refactor** — one mechanical change (rename a column, retype a shared
|
|
|
75
90
|
When the feature has a `preview.html` and a ticket **builds a screen that appears in it as a screen block**, point that ticket at **one named screen block**, never the file. Its **context** names the path (`docs/features/<slug>/preview.html`) and the block name, and tells the burn agent to find the two markers, `<!-- screen:<name> -->` and `<!-- /screen:<name> -->`, and **read only the lines between them**. Say plainly that the block shows the approved look and behaviour and **is not the implementation**: build it with the project's real components and never copy the mock's markup. The mock's styles live in the page head and are deliberately not read. **Never tell an agent to read the whole file** — the rest repeats docs it already has.
|
|
76
91
|
|
|
77
92
|
A ticket with no screen does not mention the preview.
|
|
93
|
+
|
|
94
|
+
A **designed-screen block** holds no mock. It names a component committed in design mode and its preview page. Point the ticket at that component file and that preview page path, not at markup.
|
|
78
95
|
</preview-screen-block>
|
|
79
96
|
|
|
97
|
+
<connect-designed-screens>
|
|
98
|
+
A ticket that wires a **designed screen** (one committed in design mode, `design: …` commits on the feature branch) names the committed component and its preview page in its `context`, and says plainly that **the look is approved**. Its job is to **connect, never restyle**:
|
|
99
|
+
|
|
100
|
+
- swap the sample data for real data (queries, API calls);
|
|
101
|
+
- add the behaviour the design left out;
|
|
102
|
+
- change markup only where the wiring needs it.
|
|
103
|
+
|
|
104
|
+
It does not rebuild the component or change its look. When wiring forces a visible change (a field the design lacked, say), the burner calls it out in its digest so the review sees it. No new check exists: the project's typecheck keeps preview pages compiling, and nothing else enforces them.
|
|
105
|
+
</connect-designed-screens>
|
|
106
|
+
|
|
80
107
|
## 2. Build each ticket to the schema
|
|
81
108
|
|
|
82
109
|
Emit an array of `TicketInput`. Number them from 1 in the array in **dependency order (blockers first)**; `blockedBy` holds those numbers (they are the seq numbers the store assigns).
|
|
@@ -99,11 +126,11 @@ Each ticket:
|
|
|
99
126
|
- **Non-empty → set it only where a note actually fits the ticket.** The note is the human's own vocabulary for what that model is good at ("UI/UX taste", "mechanical refactors"); assign it when this ticket's work is plainly that, and leave the field unset when nothing in the list speaks to it. Unset is the good default — it falls through to the model the project already burns on.
|
|
100
127
|
- **Never invent an id**, never copy one from these docs or from memory, and never write a model that is not in `annotatedModels` — the store rejects it and the whole batch fails to emit.
|
|
101
128
|
|
|
102
|
-
The human sees the assignment on the ticket card and can change or clear it before
|
|
129
|
+
The human sees the assignment on the ticket card and can change or clear it before Go, so a wrong-but-reasonable choice is cheap; a fabricated id is not.
|
|
103
130
|
|
|
104
131
|
## 3. Self-check, then emit
|
|
105
132
|
|
|
106
|
-
There is no in-session quiz — the human's review is the **
|
|
133
|
+
There is no in-session quiz — the human's review is the **Go** click in the runcastle UI, reading these cards. So make them right before you emit. Check yourself, in this order:
|
|
107
134
|
|
|
108
135
|
1. **Can any two of these be one ticket?** Go pair by pair. Same files, shared type, sibling behaviours, or one whose blocker is the only thing it waits on and together they'd still land in one session → merge them. Do this first, because merging changes the rest.
|
|
109
136
|
2. **Would any one of these overrun?** A criteria list longer than one agent can take red→green — with tests, self-review and commits still to come — costs a whole second container to finish. Split it where its files stop overlapping.
|
|
@@ -111,9 +138,10 @@ There is no in-session quiz — the human's review is the **Burn** click in the
|
|
|
111
138
|
4. Are the blocking edges minimal and genuinely true gates — each one a ticket that reads its blocker's output? State the critical path and the width it leaves against `burnConcurrency`, and reslice if the chain runs longer than roughly total / concurrency. Where two are independent, do they touch different files — or will they collide on landing?
|
|
112
139
|
5. Is every `context` self-sufficient for an agent that cannot ask — covering every file and pattern the ticket touches?
|
|
113
140
|
6. Does the batch close with exactly one `kind: "review"` ticket, blocked by every implementation ticket? It is not optional and there is no feature that skips it.
|
|
141
|
+
7. If `scenarios.setUp`, does every ticket that builds hard-to-reach state ship a scenario for it, and does the review ticket name it? If not set up, have you carded none and kept the one + New scenario line for your close?
|
|
114
142
|
|
|
115
143
|
Then:
|
|
116
|
-
- `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do NOT write ticket files.** It returns `{ stored, ids }` and logs the timeline event itself; do not record one of your own. Emit the **whole batch, fully enriched, in one call** — large payloads are supported, so a batch of long `context` fields is fine. Never emit placeholder contexts ("Context follows via `update_ticket`.") to enrich ticket by ticket afterwards: a
|
|
117
|
-
- `mcp__runcastle__complete_phase({ phase: "tickets" })`. It comes back `ok: true` with `waitingOn: "human burn"` and a `warnings` array — the things the human will otherwise read in the
|
|
144
|
+
- `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do NOT write ticket files.** It returns `{ stored, ids }` and logs the timeline event itself; do not record one of your own. Emit the **whole batch, fully enriched, in one call** — large payloads are supported, so a batch of long `context` fields is fine. Never emit placeholder contexts ("Context follows via `update_ticket`.") to enrich ticket by ticket afterwards: a round dispatched mid-enrichment burns agents on the placeholders.
|
|
145
|
+
- `mcp__runcastle__complete_phase({ phase: "tickets" })`. It comes back `ok: true` with `waitingOn: "human burn"` and a `warnings` array — the things the human will otherwise read in the Go dialog (no review ticket in the batch, earlier-lap defects still un-dispositioned, no `spec.md` on disk). Nothing refuses, so anything in that array is yours to fix now, while you are still the session that can: fix it and call again.
|
|
118
146
|
|
|
119
147
|
Return control to **the session skill that invoked you** — `/runcastle:ideate` — and let it close out the session. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running into the window.
|