runcastle 1.6.3 → 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.
- package/README.md +4 -4
- package/bin/runcastle.js +13200 -61414
- package/drizzle/0057_jazzy_lila_cheney.sql +1 -0
- package/drizzle/0058_test_note_open_only.sql +9 -0
- package/drizzle/0059_milky_black_knight.sql +17 -0
- package/drizzle/0060_drop_prepare_sessions.sql +6 -0
- package/drizzle/0061_events_type_index.sql +4 -0
- package/drizzle/0062_workers.sql +30 -0
- package/drizzle/0063_worker_dev_server.sql +1 -0
- package/drizzle/0064_worker_tokens.sql +1 -0
- package/drizzle/0065_review_workers.sql +28 -0
- package/drizzle/0066_drive_workers.sql +39 -0
- package/drizzle/meta/0057_snapshot.json +1351 -0
- package/drizzle/meta/0059_snapshot.json +1461 -0
- package/drizzle/meta/0062_snapshot.json +1654 -0
- package/drizzle/meta/0063_snapshot.json +1662 -0
- package/drizzle/meta/0064_snapshot.json +1669 -0
- package/drizzle/meta/0065_snapshot.json +1690 -0
- package/drizzle/meta/0066_snapshot.json +1704 -0
- package/drizzle/meta/_journal.json +70 -0
- package/index.js +12480 -60694
- package/package.json +2 -3
- package/skills/packs/README.md +11 -15
- package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
- package/skills/packs/runcastle/skills/code-review/SKILL.md +3 -3
- package/skills/packs/runcastle/skills/feature/SKILL.md +37 -18
- package/skills/packs/runcastle/skills/feature/references/design.md +102 -127
- package/skills/packs/runcastle/skills/feature/references/grilling.md +9 -3
- package/skills/packs/runcastle/skills/feature/references/planning-alone.md +69 -38
- package/skills/packs/runcastle/skills/feature/references/preview-starter.html +22 -20
- package/skills/packs/runcastle/skills/feature/references/preview.md +45 -27
- package/skills/packs/runcastle/skills/feature/references/spec.md +1 -1
- package/skills/packs/runcastle/skills/feature/references/workers.md +144 -0
- package/skills/packs/runcastle/skills/project/SKILL.md +64 -201
- package/skills/packs/runcastle/skills/project/references/charter.md +3 -3
- package/skills/packs/runcastle/skills/project/references/health-sweeps.md +3 -3
- package/skills/packs/runcastle/skills/project/references/intake.md +139 -0
- package/skills/packs/runcastle/skills/{prepare/references/recipes.md → project/references/preparation-recipes.md} +2 -2
- package/skills/packs/runcastle/skills/project/references/preparation.md +176 -0
- package/skills/packs/runcastle/skills/project/references/routing.md +39 -0
- package/skills/packs/runcastle/skills/project/references/triage.md +3 -3
- package/skills/packs/runcastle/skills/qa/SKILL.md +5 -7
- package/skills/review/review-drive.md +33 -0
- package/skills/review/review-gates.md +22 -0
- package/skills/review/review.md +61 -0
- package/skills/review/verify.md +31 -0
- package/web/assets/index-C0TYj0Ka.css +1 -0
- package/web/assets/index-C6JVtDiw.js +193 -0
- package/web/index.html +3 -3
- package/web/manifest.webmanifest +2 -2
- package/web/sw.js +4 -4
- package/skills/burner/implement-ticket.md +0 -118
- package/skills/burner/resolve-conflict.md +0 -78
- package/skills/burner/review-ticket.md +0 -268
- package/skills/burner/verify-fixes.md +0 -59
- package/skills/packs/runcastle/skills/feature/references/rounds.md +0 -97
- package/skills/packs/runcastle/skills/feature/references/tickets.md +0 -231
- package/skills/packs/runcastle/skills/prepare/SKILL.md +0 -148
- package/web/assets/index-CHxfwKvB.js +0 -193
- 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 `
|
|
12
|
-
yes. You write the feature's docs (`decisions.md`,
|
|
13
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
81
|
-
check every
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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,
|
|
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
|
|
95
|
-
(`./preview.md`, Designed screens) naming its
|
|
96
|
-
|
|
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.
|
|
102
|
-
"
|
|
103
|
-
says you will build it yourself, and that is the
|
|
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.
|
|
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),
|
|
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
|
|
116
|
-
the human for their yes to start
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
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:#
|
|
14
|
-
--color-border:#
|
|
15
|
-
--color-text:#eceef4;--color-text-secondary:#
|
|
16
|
-
--color-primary:#eceef4;--color-on-primary
|
|
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)
|
|
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:#
|
|
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)
|
|
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:#
|
|
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
|
|
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>
|
|
159
|
-
<
|
|
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
|
|
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
|
|
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
|
|
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**: `
|
|
17
|
-
the human what you propose —
|
|
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,
|
|
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
|
|
24
|
-
|
|
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
|
|
41
|
-
|
|
42
|
-
|
|
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
|
|
93
|
-
screen
|
|
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
|
-
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
|
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
|
|
120
|
-
<
|
|
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.
|
|
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
|
|
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
|
|
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.
|