runcastle 1.6.2 → 1.6.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +4 -4
  2. package/bin/runcastle.js +13202 -61413
  3. package/drizzle/0057_jazzy_lila_cheney.sql +1 -0
  4. package/drizzle/0058_test_note_open_only.sql +9 -0
  5. package/drizzle/0059_milky_black_knight.sql +17 -0
  6. package/drizzle/0060_drop_prepare_sessions.sql +6 -0
  7. package/drizzle/0061_events_type_index.sql +4 -0
  8. package/drizzle/0062_workers.sql +30 -0
  9. package/drizzle/0063_worker_dev_server.sql +1 -0
  10. package/drizzle/0064_worker_tokens.sql +1 -0
  11. package/drizzle/0065_review_workers.sql +28 -0
  12. package/drizzle/0066_drive_workers.sql +39 -0
  13. package/drizzle/meta/0057_snapshot.json +1351 -0
  14. package/drizzle/meta/0059_snapshot.json +1461 -0
  15. package/drizzle/meta/0062_snapshot.json +1654 -0
  16. package/drizzle/meta/0063_snapshot.json +1662 -0
  17. package/drizzle/meta/0064_snapshot.json +1669 -0
  18. package/drizzle/meta/0065_snapshot.json +1690 -0
  19. package/drizzle/meta/0066_snapshot.json +1704 -0
  20. package/drizzle/meta/_journal.json +70 -0
  21. package/index.js +12480 -60694
  22. package/package.json +2 -3
  23. package/skills/packs/README.md +11 -15
  24. package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
  25. package/skills/packs/runcastle/skills/code-review/SKILL.md +3 -3
  26. package/skills/packs/runcastle/skills/feature/SKILL.md +37 -18
  27. package/skills/packs/runcastle/skills/feature/references/design.md +102 -127
  28. package/skills/packs/runcastle/skills/feature/references/grilling.md +9 -3
  29. package/skills/packs/runcastle/skills/feature/references/planning-alone.md +69 -38
  30. package/skills/packs/runcastle/skills/feature/references/preview-starter.html +22 -20
  31. package/skills/packs/runcastle/skills/feature/references/preview.md +45 -27
  32. package/skills/packs/runcastle/skills/feature/references/spec.md +1 -1
  33. package/skills/packs/runcastle/skills/feature/references/workers.md +144 -0
  34. package/skills/packs/runcastle/skills/project/SKILL.md +64 -201
  35. package/skills/packs/runcastle/skills/project/references/charter.md +3 -3
  36. package/skills/packs/runcastle/skills/project/references/health-sweeps.md +3 -3
  37. package/skills/packs/runcastle/skills/project/references/intake.md +139 -0
  38. package/skills/packs/runcastle/skills/{prepare/references/recipes.md → project/references/preparation-recipes.md} +2 -2
  39. package/skills/packs/runcastle/skills/project/references/preparation.md +176 -0
  40. package/skills/packs/runcastle/skills/project/references/routing.md +39 -0
  41. package/skills/packs/runcastle/skills/project/references/triage.md +3 -3
  42. package/skills/packs/runcastle/skills/qa/SKILL.md +5 -7
  43. package/skills/review/review-drive.md +33 -0
  44. package/skills/review/review-gates.md +22 -0
  45. package/skills/review/review.md +61 -0
  46. package/skills/review/verify.md +31 -0
  47. package/web/assets/index-C0TYj0Ka.css +1 -0
  48. package/web/assets/index-C6JVtDiw.js +193 -0
  49. package/web/index.html +3 -3
  50. package/web/manifest.webmanifest +2 -2
  51. package/web/sw.js +4 -4
  52. package/skills/burner/implement-ticket.md +0 -118
  53. package/skills/burner/resolve-conflict.md +0 -78
  54. package/skills/burner/review-ticket.md +0 -268
  55. package/skills/burner/verify-fixes.md +0 -59
  56. package/skills/packs/runcastle/skills/feature/references/rounds.md +0 -97
  57. package/skills/packs/runcastle/skills/feature/references/tickets.md +0 -231
  58. package/skills/packs/runcastle/skills/prepare/SKILL.md +0 -148
  59. package/web/assets/index-CHxfwKvB.js +0 -193
  60. package/web/assets/index-CVCnjOHN.css +0 -1
@@ -7,12 +7,20 @@ instead (`./grilling.md`); nothing here binds that conversation.
7
7
  You still look facts up, model the domain and park scope creep as
8
8
  `./grilling.md` says. What changes is who answers.
9
9
 
10
+ **Survey the code through a subagent.** This chat goes on to build the feature,
11
+ so every file you read here is carried into every later call. Send the codebase
12
+ survey to one Explore-type subagent, its model picked from the human's
13
+ use-case notes (SKILL.md, "Build it the cheapest good way"), with your
14
+ questions, and keep the short map it returns. Read a
15
+ file yourself only to settle one fact the map left open.
16
+
10
17
  **This run plans; it does not build.** Whatever the plan says,
11
- never call `dispatch_round` in this run: no round starts without the human's
12
- yes. You write the feature's docs (`decisions.md`, `spec.md`, `preview.html`)
13
- and, when the work is worth handing to workers, its tickets. The one exception
18
+ never call `start_worker` or `start_review` in this run: nothing starts
19
+ without the human's yes. You write the feature's docs (`decisions.md`,
20
+ `spec.md`, `preview.html`), and the preview names the workers you would start.
21
+ The one exception
14
22
  is designing its screens (**Designing alone**, below): for that, and only that,
15
- you may install dependencies and write components, preview pages and sample
23
+ you may install dependencies and write components, design pages and sample
16
24
  data. Write no other code.
17
25
 
18
26
  - **One turn.** Do the whole plan in this one turn. Never end the turn to ask a
@@ -44,16 +52,28 @@ data. Write no other code.
44
52
  first.
45
53
  - **A timed-out tool is not a refused one.** A timeout is no answer, and the
46
54
  call may have landed anyway. Retry it once, checking first that it did not
47
- land: before repeating `emit_tickets`, `update_ticket` or `cancel_ticket`,
48
- read `list_tickets` and send only what is missing. If the retry times out
55
+ land. If the retry times out
49
56
  too, carry on and say so in the closing message.
50
57
  - **Check first.** Pick at most five decisions for the human to check first:
51
58
  guesses about their intent the brief did not settle, decisions that force
52
59
  several others, and decisions the challenger disagreed with. They open the
53
- preview's decisions part (`./preview.md`).
54
- - **Design, but never show.** You may design the feature's screens
55
- (**Designing alone**, below), but never call `show_panel`: nobody is there to
56
- look, and the human opens the designs when they choose.
60
+ Plan's decisions part (`./preview.md`). Record them in `decisions.md` as one
61
+ line under its intro, `Check first: 8, 9, 17` (the numbers, at most five),
62
+ kept in step with the Plan's Check first group; the Plan card counts it. Drop
63
+ a number once the human confirms that decision.
64
+ - **For the human.** Every decision that changes what the human gets — what
65
+ they or their users see, how the app or runcastle behaves for them, a setting
66
+ of their own — gets a `**For the human.**` line under its `**Assumed.**`
67
+ line, whether or not it is one of the five to check first. Each one is put to
68
+ the human before your plan gets its yes. A decision only about how the feature
69
+ is built gets no mark. Never override a setting the human set themselves
70
+ without marking it. A decision the brief records under
71
+ `## Decided with the human` is already theirs: copy it into `decisions.md`
72
+ without `**Assumed.**`.
73
+ - **Show with cards, never the panel.** You may design the feature's screens
74
+ (**Designing alone**, below) and call `show_design` and `show_plan`: they post
75
+ cards and open nothing, so the human finds them when they are back. Never call
76
+ `show_panel`: nobody is there to look.
57
77
 
58
78
  ## Designing alone
59
79
 
@@ -62,61 +82,72 @@ visual change (a colour, a label, one control) is described in the plan
62
82
  instead: it would not repay the install. A feature with no screen designs
63
83
  nothing.
64
84
 
65
- - **When.** Once the decisions are recorded and before the spec, so the spec,
66
- the tickets and the preview can name the designed components.
85
+ - **When.** Once the decisions are recorded and before the spec, so the spec
86
+ and the preview can name the designed components.
67
87
  - **How.** As `./design.md` says for an attended chat: the project's own
68
- install, the project's own frontend dev server, one preview page per state,
88
+ install, the project's own frontend dev server, one design page per screen
89
+ with its states on a switcher (never a design per state or variant),
69
90
  sample data, navigation off, real components, the project's design docs.
70
91
  Design against the components on the feature's base branch; never read
71
92
  another feature's worktree. If a screen needs a shared component changed, change
72
93
  it here and say so in the closing message.
73
94
  - **Tweaks for taste.** Where a look is genuinely open (two layouts, a density,
74
- an accent), leave a Tweaks menu on that preview page instead of guessing
95
+ an accent), leave a Tweaks menu on that design page instead of guessing
75
96
  (`./design.md`, Tweaks). **At most two
76
97
  open choices per screen**: past that, pick, and list your picks in the
77
98
  closing message. Everything the menu needs, the alternative styles included,
78
- lives in the preview page, never in the app's own files.
99
+ lives in the design page, never in the app's own files.
79
100
  - **Look at your own screens.** Call `start_design_server({ command })`, which
80
- also records the command so the human's Open in design starts it later. Then
81
- check every preview page. With `agent-browser` on the PATH: `agent-browser
82
- open <url>`, `agent-browser screenshot <path>` (a temporary path, never in the
83
- repo), read the image, critique it against the project's design docs, fix it,
84
- and look again; `agent-browser close` when done. Without it, check that each
101
+ also records the command so the human's Design tab can start it later. Then
102
+ check every state of every design page (each `?state=` address). With
103
+ `agent-browser` on the PATH, hand the loop to a subagent on the model the
104
+ human's notes give UI work:
105
+ it runs `agent-browser open <url>`, `agent-browser screenshot <path>` (a
106
+ temporary path, never in the repo), reads the image, critiques it against the
107
+ project's design docs, fixes it and looks again, closes with
108
+ `agent-browser close`, and returns its verdict and the final picture's path
109
+ for `show_design`. Do not read the images yourself. Without it, check that each
85
110
  page compiles and serves, and say in the closing message that you never saw
86
111
  them. The design server stops by itself when this run ends.
112
+ - **Record every design.** Once a page is right, call `show_design({ path, title,
113
+ width? , picture? })` for it with a human title and the width you laid it out
114
+ at, passing the final screenshot you took of its most telling state as
115
+ `picture`, once per page, never once per state. It posts a design card
116
+ and records the design for the Design tab; it opens nothing in this run.
87
117
  - **A screen you cannot serve** (no frontend dev server you can work out, a
88
118
  page the server builds, native mobile) is a hand mock in `preview.html`, as
89
119
  any plan draws one. Say so in the closing message. Do not stop or ask.
90
- - **Leave the designs uncommitted.** Never commit a component, preview page,
120
+ - **Leave the designs uncommitted.** Never commit a component, design page,
91
121
  sample data or `.runcastle/design.json` you designed: only the human's
92
122
  approval commits them. Commit your docs by path, so nothing designed rides
93
123
  along.
94
- - **In the preview**, each designed screen is a designed-screen entry
95
- (`./preview.md`, Designed screens) naming its component file and preview page.
96
- A ticket that wires one follows `./tickets.md` on designed screens.
124
+ - **In the Plan**, each designed screen is a designed-screen entry
125
+ (`./preview.md`, Designed screens) naming its title, the states its switcher
126
+ holds, its component file and design page, with no button.
127
+ A worker that wires one follows `./workers.md` on designed screens.
97
128
 
98
129
  ## Straight through, then ask
99
130
 
100
131
  1. Write `spec.md` (`./spec.md`) once the decisions are recorded.
101
- 2. Write the tickets only if the work is worth handing to workers (SKILL.md,
102
- "Build it the cheapest good way"; then `./tickets.md`). Otherwise the plan
103
- says you will build it yourself, and that is the usual answer for a small
104
- feature.
132
+ 2. Plan the workers by the offering rule (SKILL.md, "Build it the cheapest good
133
+ way"): each with its label, one-line purpose and kind, in the preview. Start
134
+ none. With none, the plan says you will build it yourself, and that is the
135
+ usual answer for a small feature.
105
136
  3. Draw the preview and run its self-check (`./preview.md`).
106
137
  4. Commit the docs.
107
- 5. **End with one closing chat message** that covers the plan only: that it was
138
+ 5. Call `show_plan()` once, before the closing message.
139
+ 6. **End with one closing chat message** that covers the plan only: that it was
108
140
  made without the human, how many decisions it holds and which are to check
109
- first (by number and title), whether it is tickets or work you will do
141
+ first (by number and title), which workers it offers and what you will do
110
142
  yourself, and anything that stopped you doing the job (a refused or failed
111
143
  tool the plan needed, and what you decided without it). Leave out the state
112
144
  of tools and connectors the plan did not need. **If you designed**, also
113
- name each designed screen, the open Tweaks choices, any shared component you
145
+ name each designed screen and the states its switcher holds, the open Tweaks choices, any shared component you
114
146
  changed, and whether you saw the screens in screenshots or only checked that
115
- they compile, and say that the yes commits the designs as they stand. Close the message by asking
116
- the human for their yes to start round 1 (or to start the work, if there are
117
- no tickets).
118
- 6. Call `mcp__runcastle__wait_for_you({ why: "start round 1?" })`, or
119
- `mcp__runcastle__wait_for_you({ why: "designs to look at · start round 1?" })`
120
- if you designed. With no tickets, say "start the work?" in place of "start
121
- round 1?". Then end the turn. The human answers in this chat when they
147
+ they compile, and say that only designs the human approves are committed. Close the message by asking
148
+ the human for their yes to start the work.
149
+ 7. Call `mcp__runcastle__wait_for_you({ why: "start the work?", kind: "plan" })`.
150
+ (Waiting designs raise "Design to approve" by themselves; no design-kind wait
151
+ is needed.) Write nothing after step 6's question, so the turn ends on
152
+ it, and then end the turn. The human answers in this chat when they
122
153
  are back; their yes is what starts the work, in a later turn.
@@ -3,6 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8">
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <!-- FILL:title -->
6
7
  <title>[Feature title] preview</title>
7
8
  <style>
8
9
  /* ---- runcastle theme: dark is the default, light is a full peer.
@@ -10,10 +11,10 @@
10
11
  Token names and values are the app's own (apps/web/src/theme.css): keep them in step. ---- */
11
12
  :root{
12
13
  color-scheme:dark;
13
- --color-canvas:#0a0c11;--color-surface:#10131a;--color-surface-raised:#161a24;--color-surface-hover:color-mix(in oklab, var(--color-text) 3.5%, transparent);--color-surface-selected:color-mix(in oklab, var(--color-text) 6%, transparent);--color-fill-card:color-mix(in oklab, var(--color-text) 2.5%, transparent);--color-fill-well:color-mix(in oklab, var(--color-text) 6%, transparent);--color-surface-inset:#0c0e14;
14
- --color-border:#222837;--color-border-subtle:rgb(200 215 255 / 0.06);--color-border-strong:#333a4d;
15
- --color-text:#eceef4;--color-text-secondary:#a2a8b9;--color-text-tertiary:#8a91a5;--color-text-disabled:#4b5163;--color-icon:#8990a4;
16
- --color-primary:#eceef4;--color-on-primary:#0a0c11;
14
+ --color-canvas:#04060b;--color-surface:#080c13;--color-surface-raised:#0e131e;--color-surface-hover:color-mix(in oklab, var(--color-text) 3%, transparent);--color-surface-selected:color-mix(in oklab, var(--color-text) 6%, transparent);--color-fill-card:var(--color-surface-hover);--color-fill-well:var(--color-surface-selected);--color-surface-inset:var(--color-canvas);
15
+ --color-border:#181f2d;--color-border-subtle:rgb(200 215 255 / 0.06);--color-border-strong:#293243;
16
+ --color-text:#eceef4;--color-text-secondary:#a9b1c1;--color-text-tertiary:#798294;--color-text-disabled:#424a5b;--color-icon:var(--color-text-tertiary);
17
+ --color-primary:#eceef4;--color-on-primary:var(--color-canvas);
17
18
  --color-accent:#4f8ef7;--color-accent-text:#7aaafa;--color-accent-subtle:#16213a;
18
19
  --color-success:#46b67a;--color-warning:#e2a53b;--color-danger:#f06363;--color-danger-subtle:#2a1515;
19
20
  --color-scrim:rgb(0 0 0 / 0.6);
@@ -23,9 +24,9 @@
23
24
  }
24
25
  :root[data-theme="light"]{
25
26
  color-scheme:light;
26
- --color-canvas:#f4f6f9;--color-surface:#ffffff;--color-surface-raised:#ffffff;--color-surface-hover:color-mix(in oklab, var(--color-text) 4%, transparent);--color-surface-selected:color-mix(in oklab, var(--color-text) 7%, transparent);--color-fill-card:color-mix(in oklab, var(--color-text) 3%, transparent);--color-fill-well:color-mix(in oklab, var(--color-text) 7%, transparent);--color-surface-inset:#f4f6f9;
27
+ --color-canvas:#f4f6f9;--color-surface:#ffffff;--color-surface-raised:#ffffff;--color-surface-hover:color-mix(in oklab, var(--color-text) 3.5%, transparent);--color-surface-selected:color-mix(in oklab, var(--color-text) 7%, transparent);
27
28
  --color-border:#dfe3ec;--color-border-subtle:rgb(22 30 50 / 0.07);--color-border-strong:#cdd2de;
28
- --color-text:#161a24;--color-text-secondary:#596075;--color-text-tertiary:#676e82;--color-text-disabled:#a4aab9;--color-icon:#7a8195;
29
+ --color-text:#161a24;--color-text-secondary:#454f62;--color-text-tertiary:#677083;--color-text-disabled:#a4aab9;
29
30
  --color-primary:#161a24;--color-on-primary:#ffffff;
30
31
  --color-accent:#3a7ef0;--color-accent-text:#2361d4;--color-accent-subtle:#ecf2fe;
31
32
  --color-success:#23955a;--color-warning:#c06e00;--color-danger:#df5050;--color-danger-subtle:#fdeeee;
@@ -34,9 +35,9 @@
34
35
  @media (prefers-color-scheme:light){
35
36
  :root:not([data-theme]){
36
37
  color-scheme:light;
37
- --color-canvas:#f4f6f9;--color-surface:#ffffff;--color-surface-raised:#ffffff;--color-surface-hover:color-mix(in oklab, var(--color-text) 4%, transparent);--color-surface-selected:color-mix(in oklab, var(--color-text) 7%, transparent);--color-fill-card:color-mix(in oklab, var(--color-text) 3%, transparent);--color-fill-well:color-mix(in oklab, var(--color-text) 7%, transparent);--color-surface-inset:#f4f6f9;
38
+ --color-canvas:#f4f6f9;--color-surface:#ffffff;--color-surface-raised:#ffffff;--color-surface-hover:color-mix(in oklab, var(--color-text) 3.5%, transparent);--color-surface-selected:color-mix(in oklab, var(--color-text) 7%, transparent);
38
39
  --color-border:#dfe3ec;--color-border-subtle:rgb(22 30 50 / 0.07);--color-border-strong:#cdd2de;
39
- --color-text:#161a24;--color-text-secondary:#596075;--color-text-tertiary:#676e82;--color-text-disabled:#a4aab9;--color-icon:#7a8195;
40
+ --color-text:#161a24;--color-text-secondary:#454f62;--color-text-tertiary:#677083;--color-text-disabled:#a4aab9;
40
41
  --color-primary:#161a24;--color-on-primary:#ffffff;
41
42
  --color-accent:#3a7ef0;--color-accent-text:#2361d4;--color-accent-subtle:#ecf2fe;
42
43
  --color-success:#23955a;--color-warning:#c06e00;--color-danger:#df5050;--color-danger-subtle:#fdeeee;
@@ -84,11 +85,9 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
84
85
  /* the framed panel a mocked screen sits in; inside it, use the project's own styling */
85
86
  .screen{border:1px solid var(--color-border);border-radius:var(--radius-lg);overflow:hidden;background:var(--color-surface);position:relative}
86
87
 
87
- /* a screen designed in design mode: a name, one line, and a button that opens it in the app's design panel */
88
+ /* a screen designed in design mode: a title, one line, the states its switcher holds, and the component file and design page it names (no button) */
88
89
  .designed{display:grid;gap:6px;justify-items:start;padding:12px 14px;border:1px solid var(--color-border);border-left:2px solid var(--color-accent);border-radius:var(--radius-md);background:var(--color-surface-raised)}
89
90
  .designed p{color:var(--color-text-secondary)}
90
- .designed button[data-open-design]{height:28px;padding:0 12px;border-radius:var(--radius-md);background:var(--color-accent-subtle);color:var(--color-accent-text);font-weight:500}
91
- .designed button[data-open-design]:hover{background:var(--color-accent);color:#fff}
92
91
 
93
92
  /* decided / left out / under the hood */
94
93
  .dlist{border-top:1px solid var(--color-border-subtle);margin-top:12px}
@@ -134,6 +133,7 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
134
133
 
135
134
  <!-- 1 · what this is -->
136
135
  <header id="what">
136
+ <!-- FILL:what -->
137
137
  <h1>[Feature title]</h1>
138
138
  <p class="lede">[One or two sentences: what this feature is, in the human's words.]</p>
139
139
  <div class="meta"><span><span data-dcount></span> decisions</span><span class="mono">docs/features/[slug]/preview.html</span></div>
@@ -145,6 +145,7 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
145
145
  <!-- 2 · the show -->
146
146
  <section id="show">
147
147
  <h2>The show</h2>
148
+ <!-- FILL:show -->
148
149
  <!-- THE SHOW IS FREE-FORM. Replace this comment with whatever explains how the
149
150
  feature will work: screens, clickable prototypes, skeletons, flows, before
150
151
  and after, scenarios. There is no template. Pin a decision's number where
@@ -154,10 +155,12 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
154
155
 
155
156
  Example screen block (copy it, rename it in kebab case, uncomment the
156
157
  panel; delete it if the show has no screen). A screen designed in design
157
- mode gets a designed-screen entry instead of a mock; see preview.md:
158
- <div class="designed"><h3>Name</h3><p>What is real, what is sample.</p>
159
- <button data-open-design="/preview/path">Open in design</button></div>
158
+ mode gets a designed-screen entry instead of a mock, with no button; see preview.md:
159
+ <div class="designed"><h3>Title</h3><p>What is real, what is sample.</p>
160
+ <p>States: empty · loading · full · error.</p>
161
+ <p class="mono">src/components/File.tsx · design page /preview/path</p></div>
160
162
  -->
163
+ <!-- FILL:screen -->
161
164
  <!-- screen:example-name -->
162
165
  <!--
163
166
  <div class="screen">
@@ -178,9 +181,11 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
178
181
  <section id="left-out">
179
182
  <h2>What is left out</h2>
180
183
  <div class="out2">
184
+ <!-- FILL:later -->
181
185
  <div><h3>Later</h3><ul>
182
186
  <li><i class="dot"></i>[Deferred to a backlog feature]</li>
183
187
  </ul></div>
188
+ <!-- FILL:not-this -->
184
189
  <div><h3>Not this feature</h3><ul>
185
190
  <li><i class="dot"></i>[Out of scope]</li>
186
191
  </ul></div>
@@ -192,6 +197,7 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
192
197
  <details class="hood" id="hood">
193
198
  <summary><svg class="i"><use href="#i-chev"/></svg><h2>Under the hood</h2></summary>
194
199
  <div class="sub">
200
+ <!-- FILL:hood -->
195
201
  <!-- [The technical shape, for a reader who wants it. Draw it where you can.] -->
196
202
  </div>
197
203
  </details>
@@ -204,11 +210,13 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
204
210
  var $ = function (s, r) { return (r || document).querySelector(s) }
205
211
  var $$ = function (s, r) { return Array.prototype.slice.call((r || document).querySelectorAll(s)) }
206
212
 
213
+ // <!-- FILL:D -->
207
214
  // every decision in decisions.md, by number and title, in order: [title, reason, mark?]
208
215
  // mark is 'assumed' for a decision made without the human; a grilled feature has none
209
216
  var D = [
210
217
  ['[Decision 1 title]', '[Why, in a sentence]']
211
218
  ]
219
+ // <!-- FILL:C -->
212
220
  // check first, a self-planned feature only (at most five): [decisionNumber, alternative, whyUnsure, tag?]
213
221
  // leave it empty for a grilled feature, and no group is drawn
214
222
  var C = []
@@ -256,12 +264,6 @@ button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:poin
256
264
  // navigation is unreliable inside the app's sandboxed frame
257
265
  function reveal(el) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }) }
258
266
  document.addEventListener('click', function (e) {
259
- // Open in design: the app listens for exactly this message and opens the design panel
260
- var od = e.target.closest('[data-open-design]')
261
- if (od) {
262
- parent.postMessage({ type: 'runcastle:open-design', path: od.dataset.openDesign }, '*')
263
- return
264
- }
265
267
  var pin = e.target.closest('a.pin[data-d]')
266
268
  if (pin) {
267
269
  e.preventDefault()
@@ -1,27 +1,28 @@
1
- # The plan preview — the page the human looks at
1
+ # The Plan — the page the human looks at
2
2
 
3
3
  Load this when the decisions are settled and you are about to show the human
4
- the plan, or when a decision edit means the preview has to be redrawn.
4
+ the plan, or when a decision edit means the Plan has to be redrawn.
5
5
  Otherwise it costs this conversation nothing.
6
6
 
7
- The preview is the one page the human reads the feature by. They confirm the
7
+ The Plan (`preview.html`) is the one page the human reads the feature by. They confirm the
8
8
  plan by looking at it, and when tired it is the only thing they read before
9
- they say yes to the first round. Draw it for that reader.
9
+ they say yes to the work. Draw it for that reader.
10
10
 
11
11
  ## Show it, then get the yes
12
12
 
13
13
  1. **Draw it** from `./preview-starter.html`, following the rules below.
14
14
  2. **Check it against `decisions.md`** (the self-check at the end). Fix the
15
15
  preview before anyone sees it.
16
- 3. **Show it**: `show_panel({ view: 'plan' })` opens it beside the chat. Tell
17
- the human what you propose — tickets and rounds, or work you will do
18
- yourself — and ask for their explicit yes. Then wait for the answer.
16
+ 3. **Show it**: `show_plan()` posts the Plan card in the chat and opens the
17
+ Plan tab beside it. Tell the human what you propose — the workers, the work you
18
+ will do yourself, the Review worker at the end — and ask for their explicit yes. Then wait for the answer.
19
19
  4. **A correction is a decision edit plus a redraw.** Edit the decision in
20
- `decisions.md` and redraw the preview in the same move, then ask them to
21
- look again.
20
+ `decisions.md` and redraw the preview in the same move, call `show_plan()`
21
+ again, then ask them to look again.
22
22
 
23
- With no human present, steps 1 and 2 are the whole of this; `./planning-alone.md`
24
- says how that run asks for the yes.
23
+ With no human present, steps 1 and 2 are the whole of this, plus one
24
+ `show_plan()` before the closing message; `./planning-alone.md` says how that
25
+ run asks for the yes.
25
26
 
26
27
  **Never skip the preview on your own judgement** — not because the feature is
27
28
  small, not because the grill went quickly. A small feature gets a small
@@ -37,9 +38,22 @@ built.
37
38
  - **Exactly one file, `preview.html`, at the root of the feature's docs dir**:
38
39
  `docs/features/<slug>/preview.html`. Several screens or flows are sections of
39
40
  that one page, never more files.
40
- - **Start from `./preview-starter.html`.** Copy it to that path and fill it in.
41
- It carries runcastle's theme tokens for both themes and the styles and markup
42
- for the fixed parts; do not restyle them.
41
+ - **Start from `./preview-starter.html`.** Copy it to that path with a shell
42
+ copy (`cp`), find its slots with `grep -n 'FILL:'` on the copy, and fill each
43
+ by editing; **never read the starter whole**. It carries runcastle's theme
44
+ tokens for both themes and the styles and markup for the fixed parts; do not
45
+ restyle them.
46
+ - **The slots**, each a `<!-- FILL:<name> -->` line just above what to replace:
47
+ - `title`: the feature title in `<title>`.
48
+ - `what`: the `<h1>` title, the one- or two-sentence lede, `[slug]` in the meta line.
49
+ - `show`: replace the free-form comment below it with the show.
50
+ - `screen`: the example screen block; copy and rename it per screen, or delete it.
51
+ - `D`: one entry per decision, in order: `['Title', 'Why, in a sentence']`, or
52
+ `['Title', 'Why', 'assumed']` for a decision made without the human.
53
+ - `C`: Check first, a self-planned feature only, at most five:
54
+ `[decisionNumber, 'Alternative', 'Why unsure', 'tag'?]`; leave `[]` for a grilled feature.
55
+ - `later` / `not-this`: one `<li><i class="dot"></i>…</li>` per item left out.
56
+ - `hood`: the technical shape, inside the collapsed **Under the hood**.
43
57
  - **Throwaway explorations** may go under `docs/features/<slug>/prototypes/`.
44
58
  They are never shown; the app shows only `preview.html`.
45
59
 
@@ -50,6 +64,9 @@ built.
50
64
  no template**: screens, clickable prototypes, skeletons, flows, before and
51
65
  after, scenarios. Choose the form that explains *this* feature — work with no
52
66
  screen gets flows, use cases, before and after, or a clickable skeleton.
67
+ **It ends with who builds what**: each worker you offer by its label, one-line
68
+ purpose and kind ("with you" or "on its own"), what you build yourself, and
69
+ the Review worker at the end. The human's yes starts those workers.
53
70
  3. **What was decided.** Every decision in `decisions.md` by its number and
54
71
  title, with the reason available on demand (the starter's `D` list builds
55
72
  this). Where a decision is visible in the show, pin its number on that spot:
@@ -89,8 +106,8 @@ Each screen in the show is a **screen block**: the lines between two markers,
89
106
  <!-- /screen:<name> -->
90
107
  ```
91
108
 
92
- with `<name>` in kebab case and unique in the file. A ticket that builds that
93
- screen names the block, and its worker reads only those lines — so put
109
+ with `<name>` in kebab case and unique in the file. A worker that builds that
110
+ screen is pointed at the block and reads only those lines — so put
94
111
  everything the screen needs to be understood between the markers, and nothing
95
112
  else. Delete the starter's `example-name` block if the show has no screen.
96
113
 
@@ -99,15 +116,15 @@ else. Delete the starter's `example-name` block if the show has no screen.
99
116
  A screen designed in design mode (`./design.md`) is **not mocked
100
117
  again**. Its place in the show is a **designed-screen entry**:
101
118
 
102
- - its name;
119
+ - its name, the same title you gave `show_design`;
103
120
  - one line saying what is real and what is sample data;
104
- - an **Open in design** button: `<button data-open-design="/preview/path">Open in design</button>`,
105
- where the value is the preview page's path on the design server. The starter
106
- wires it; the app answers by opening the design panel at that path, starting
107
- the server first if needed.
121
+ - the states its switcher holds, so the plan says what the human will find.
122
+
123
+ The entry has **no button**: the human reaches a design from its card in the chat
124
+ and from the Design tab, which lists every design you showed with `show_design`.
108
125
 
109
126
  Wrap the entry in a screen block, but put no markup in it. The block names the
110
- **committed component file and the preview page path**, so the ticket that
127
+ **committed component file and the design page path**, so the worker that
111
128
  connects the screen has something to point at. Use the starter's `.designed`
112
129
  entry:
113
130
 
@@ -116,8 +133,8 @@ entry:
116
133
  <div class="designed">
117
134
  <h3>Invoice list</h3>
118
135
  <p>Layout and states are real components; the rows are sample data.</p>
119
- <p class="mono">src/components/InvoiceList.tsx · preview /preview/invoice-list</p>
120
- <button data-open-design="/preview/invoice-list">Open in design</button>
136
+ <p>States: empty · loading · full · error.</p>
137
+ <p class="mono">src/components/InvoiceList.tsx · design page /preview/invoice-list</p>
121
138
  </div>
122
139
  <!-- /screen:invoice-list -->
123
140
  ```
@@ -133,8 +150,7 @@ or inline SVG. This is enforced, not a preference: the app renders the preview
133
150
  in a sandboxed frame that runs scripts and refuses every fetch, external image
134
151
  and font, form post and popup. Anything hosted elsewhere simply does not load.
135
152
  In-page links scroll by script (the starter does this), because hash navigation
136
- is unreliable inside that frame. The Open in design button posts a message to
137
- the app, which is not a request, so it stays within this rule.
153
+ is unreliable inside that frame.
138
154
 
139
155
  ## Sizing
140
156
 
@@ -152,6 +168,8 @@ minute for a small feature, a few minutes for a large one.**
152
168
  For a grilled feature, `C` is empty and no decision is marked.
153
169
  - Every pin points at the decision the spot actually shows.
154
170
  - The five parts are in order; **Under the hood** is closed.
171
+ - The show ends with who builds what: every worker by label, purpose and kind,
172
+ and the Review worker unless the human declined it.
155
173
  - Every screen designed in design mode is a designed-screen entry that names its
156
- committed component and preview page, not a redrawn mock.
174
+ states, its committed component and design page, not a redrawn mock.
157
175
  - The file is still fully inline.
@@ -20,7 +20,7 @@ settled.
20
20
  Prefer *existing* seams to new ones; use the *highest* seam possible; the
21
21
  fewer across the codebase, the better — the ideal is one. If new seams are
22
22
  needed, propose them at the highest point you can. They flow straight into
23
- the tickets and the tests, so get them right here.
23
+ the workers' assignments and the tests, so get them right here.
24
24
 
25
25
  3. **Write `docs/features/<slug>/spec.md`** with exactly these sections:
26
26
 
@@ -0,0 +1,144 @@
1
+ # Workers
2
+
3
+ Load this when you are about to start, message, land or send back a worker, or
4
+ you have just been woken by one. Which work gets a worker is SKILL.md's rule
5
+ ("Build it the cheapest good way").
6
+
7
+ A worker is an agent in its own sandbox, on its own copy of the feature branch.
8
+ The human can step into any worker and talk to it. It cannot see this chat. It
9
+ can read the feature's docs, its designs and your latest turns. Its only way to
10
+ change the feature is its handback. `get_feature_context` lists the feature's
11
+ workers and their statuses.
12
+
13
+ ## Start one
14
+
15
+ Start workers on the human's yes to the plan, or to an offer in chat. Nothing
16
+ else starts them, and they need no approval each.
17
+
18
+ `start_worker({ label, purpose, kind, goal, doneWhen, model? })`:
19
+
20
+ - **label** — the short name the human sees in the sidebar ("Thread list").
21
+ - **purpose** — one line on what it is for.
22
+ - **kind** — `"with-you"` for taste work, `"on-its-own"` for build work.
23
+ - **goal** and **doneWhen** — the assignment, below.
24
+ - **model** — only an id from `get_feature_context`'s `annotatedModels`, where
25
+ the human's note plainly fits the job. Otherwise omit it. Never invent an id.
26
+
27
+ A "with you" worker makes a first pass, then waits for the human. It hands back
28
+ only when the human says they are happy or presses **Hand back**. An "on its
29
+ own" worker hands back when its done-when is met, and stops only for a real
30
+ question.
31
+
32
+ Where one piece must wait for another, start the second after you land the
33
+ first. Pieces that run side by side should touch different files: two on the
34
+ same files conflict when you land them. Workers share the machine's limit of
35
+ agents working at once; past it they queue and start by themselves.
36
+
37
+ Without Docker or Podman, `start_worker` says so. Tell the human in plain words
38
+ and point them at the setup check (doctor).
39
+
40
+ ## Write the assignment
41
+
42
+ The worker cannot ask you where things live, so the goal and done-when are
43
+ concrete:
44
+
45
+ - **Name what it needs**: the files to touch, the patterns to copy, the shapes,
46
+ the gotchas, the decisions and spec sections that bind it.
47
+ - **State the check.** A worker with no stated check reports success on its own
48
+ terms. Say what is built and what proves it: a test worth writing, a command
49
+ that passes, the screen that shows it. Name the tests that cover the change,
50
+ never the whole suite: you run the project's checks after landing.
51
+ - **UI work names its design references**: the project's design docs and this
52
+ feature's designs. Ask it to plan the screen, build it, then critique it
53
+ against those docs with screenshots.
54
+ - **A screen in `preview.html`**: point it at one named screen block, never the
55
+ file. It reads only the lines between `<!-- screen:<name> -->` and
56
+ `<!-- /screen:<name> -->`. The block shows the approved look, not the
57
+ implementation: it builds with the project's real components.
58
+ - **A designed screen** (committed in design mode): name the component and its
59
+ design page, and say the look is approved. Its job is to connect, never
60
+ restyle: real data in place of the sample, the behaviour the design left out,
61
+ markup changed only where the wiring needs it. The states on the design
62
+ page's switcher are the states it must reach.
63
+ - **Hard-to-reach state**: when `scenarios.setUp` is true, the done-when ships
64
+ the scenario (see **Ship a scenario**, below).
65
+ - **One area each.** A worker's cost grows with the square of its context, so a
66
+ job across several packages is two workers, one after the other.
67
+
68
+ ## Ship a scenario
69
+
70
+ **Ship a scenario when the state is hard to reach.** A scenario is a project
71
+ command under `.runcastle/scenarios/` that seeds a state and prints the URL to
72
+ land on, so a test drive starts where the feature is instead of on a blank app.
73
+ Read `get_feature_context`'s `scenarios` field — `{ setUp, runnerConfigured,
74
+ scenarios, fixtures }` — before you decide anything.
75
+
76
+ **When `setUp` is true**, ask of each piece of work: will the person driving
77
+ this have to build state by hand to see it — an account, a cart with an expired
78
+ coupon?
79
+
80
+ - If yes, the work that builds that state ships `.runcastle/scenarios/<group>/<name>.<ext>`, which seeds the state (reusing `_fixtures`) and prints `ENTRY_URL=<where>`: a line in that worker's done-when, or yours to write. Name the fixtures it should reuse. When the change is an API call, it also ships a sibling `.http` file at the same path with the request, and the scenario prints `TOKEN=` when the call is authenticated.
81
+ - Use that scenario as the `scenario` you declare ready with; the Review worker replays it rather than improvising the setup.
82
+ - Copy changes and settings toggles get none. A scenario earns its place only where reaching the state is the chore.
83
+ - A worker writes and typechecks the scenario but cannot run it — a sandbox has no drive.
84
+
85
+ **When `setUp` is false**, card nothing about scenarios. Tell the human once
86
+ that + New scenario in a test drive sets scenarios up for this project.
87
+
88
+ ## Talk to it
89
+
90
+ `message_worker({ workerId, text })` reaches any worker at any time. One sent
91
+ mid-turn waits for the turn's end. The human may be talking to it too.
92
+
93
+ Never ask a worker for its transcript or its work in detail. This chat gains
94
+ only handbacks, and that is the point.
95
+
96
+ ## A handback
97
+
98
+ You are woken once, with a handback card: the worker's summary, which carries
99
+ any direction the human gave it, plus its commits and changed files. Trust the
100
+ commits over the summary. Then do one of three things:
101
+
102
+ - **Land it**: `land_worker({ workerId })` lands its branch on the feature
103
+ branch through the landing queue, like your own commits. Resolve any conflict.
104
+ Then run the project's checks yourself, and fix small slips yourself rather
105
+ than sending it back.
106
+ - **Send it back**: `send_back_worker({ workerId, note })`, saying concretely
107
+ what is wrong. It returns to working.
108
+ - **Drop it**: `drop_worker({ workerId })` removes its sandbox and branch.
109
+
110
+ Where the human's direction to a worker changes a decision, amend
111
+ `decisions.md` and `spec.md` when you land it.
112
+
113
+ A worker the human started with the "+" has no assignment. It hands back the
114
+ same way, and you land, send back or drop it the same way.
115
+
116
+ A worker that hands back after you declared ready: land it, then declare ready
117
+ again.
118
+
119
+ ## The review
120
+
121
+ The review at the end is a worker labelled **Review**. Start it when the work
122
+ is done, every build worker landed or dropped: `start_review({})`. It writes no
123
+ code and lands nothing. It runs the project's checks itself, reports each
124
+ finding as it goes, and hands back.
125
+
126
+ - **Its mode is set at start.** Left unset, it drives the app when this
127
+ machine can: on the human's machine, in their checkout, while it holds
128
+ the one drive slot. It waits as queued while the human drives. Otherwise, or
129
+ with `mode: "gates"` (a change with nothing to drive), it reads the diff in
130
+ its own sandbox.
131
+ - **Its handback is the Review Package** in your chat: its account,
132
+ screenshots, walkthrough, calls and findings, with the findings listed for
133
+ you. Close each finding with `resolve_finding` (`addressed` or `dismissed`,
134
+ with a note). Fix the defects you judge real, yourself or with workers.
135
+ - **Then verify.** Once you fixed some, start a fresh one:
136
+ `start_review({ verify: { findingIds, commits } })`, with only those findings
137
+ and the commits that fixed them. It checks each fix holds and nothing else.
138
+ - `send_back_worker` asks a review for more; `drop_worker` closes it. Never
139
+ `land_worker` it.
140
+
141
+ ## A failed worker
142
+
143
+ When a worker's sandbox dies, its setup fails or its agent crashes, you get one
144
+ short message saying what failed. Retry it, drop it, or do the job yourself.