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
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
# Running rounds
|
|
2
|
-
|
|
3
|
-
Load this when you are about to dispatch a round, or you have just been woken by
|
|
4
|
-
one coming back.
|
|
5
|
-
|
|
6
|
-
A round is how workers build: each gets one ticket, a clean sandbox and the full
|
|
7
|
-
brief, and works on its own branch that lands on the feature branch. You
|
|
8
|
-
dispatch it, you are woken when it comes back, you check what it did, and you
|
|
9
|
-
plan the next move.
|
|
10
|
-
|
|
11
|
-
Start the first round only after the human's explicit yes in chat, and ask again
|
|
12
|
-
before any round the agreed plan did not settle (SKILL.md, "The human's yes").
|
|
13
|
-
If designs wait uncommitted in the worktree, the human's yes commits them first;
|
|
14
|
-
an orchestrator relay never does (`./design.md`, section 8).
|
|
15
|
-
|
|
16
|
-
## What you run on
|
|
17
|
-
|
|
18
|
-
`get_feature_context` carries:
|
|
19
|
-
|
|
20
|
-
- `rounds` — every round so far, numbered from 1: the tickets it held, how it ended, its gates.
|
|
21
|
-
- `latestReady` — your newest ready declaration, if any.
|
|
22
|
-
- `driveActive` — the human is test-driving this feature right now.
|
|
23
|
-
- `stopped` — the human pressed Stop and has not replied since.
|
|
24
|
-
- `waitingReason` — why the feature is waiting on the human, if it is.
|
|
25
|
-
- `burnConcurrency` — how many workers the project runs at once.
|
|
26
|
-
|
|
27
|
-
## Plan a round
|
|
28
|
-
|
|
29
|
-
1. **Write the tickets** with `emit_tickets`, or reshape pending ones with
|
|
30
|
-
`update_ticket` / `cancel_ticket`, following `./tickets.md`. Then **dispatch**
|
|
31
|
-
with `dispatch_round({ seqs })`, naming exactly the pending tickets this round
|
|
32
|
-
covers. Tickets you do not name wait for a later round. A ticket may stay
|
|
33
|
-
blocked by tickets that landed or were cancelled in an earlier round: those
|
|
34
|
-
count as satisfied. A blocker that is still pending or failed must be named
|
|
35
|
-
in the same round.
|
|
36
|
-
2. **Every worker task names its deliverable and how it is checked.** A worker
|
|
37
|
-
with no stated check reports success on its own terms. Say what is built and
|
|
38
|
-
what proves it: a test where one is worth writing, the command that passes, the screen
|
|
39
|
-
that shows it.
|
|
40
|
-
3. **Keep the round to `burnConcurrency` workers or fewer.** A soft cap: workers
|
|
41
|
-
past it only queue, and a wider round has more to reconcile when it returns.
|
|
42
|
-
4. **Contract first when the work spans layers.** When workers on different
|
|
43
|
-
layers must agree on a shape — types, an API, a schema — make one ticket land
|
|
44
|
-
that contract in code and make the others `blockedBy` it in the same round.
|
|
45
|
-
**Contracts live in code, never in a doc**: workers trust the code.
|
|
46
|
-
5. **UI work names the design references.** A ticket that touches UI tells its
|
|
47
|
-
worker which design docs to follow — the project's own design system docs and
|
|
48
|
-
any design references in this feature's docs — and asks it to plan the
|
|
49
|
-
screen, build it, then critique it against those docs with screenshots.
|
|
50
|
-
|
|
51
|
-
Workers do not talk to each other or to you. They report through their digests,
|
|
52
|
-
their ticket status and their commits. A worker that finds the contract wrong
|
|
53
|
-
stops and says so in its digest; reconciling it is your job, between rounds.
|
|
54
|
-
|
|
55
|
-
## When a round comes back
|
|
56
|
-
|
|
57
|
-
You are woken with the round's outcome, each ticket's status, the verify-gate
|
|
58
|
-
result, any review findings and where the digests are.
|
|
59
|
-
|
|
60
|
-
1. **Trust the record over the prose.** Ticket status, the gates and the commits
|
|
61
|
-
are what happened; a digest is a worker's account of it. Read each digest
|
|
62
|
-
with `get_ticket({ seq })`, and where the two disagree, believe the record.
|
|
63
|
-
2. **Reconcile.** Did a worker report the contract wrong, or change something
|
|
64
|
-
another relied on? Did a decision turn out wrong? Fix the contract in code,
|
|
65
|
-
amend `decisions.md` and `spec.md` when what was decided changed, and update
|
|
66
|
-
the pending tickets. A contract reported wrong is a reason to re-plan.
|
|
67
|
-
3. **Handle review findings.** A finding stays open until you resolve it: fix it
|
|
68
|
-
in a round or yourself, or dismiss it and say why. A review reports useless
|
|
69
|
-
tests as low-severity observations, never defects: when the round you are
|
|
70
|
-
planning already mints a fix ticket, fold them into it (one acceptance
|
|
71
|
-
criterion: delete or rewrite the named tests), so they go at no extra sandbox
|
|
72
|
-
cost. Never mint a ticket only to delete useless tests.
|
|
73
|
-
4. **Decide the next move**:
|
|
74
|
-
- small leftovers → fix them yourself;
|
|
75
|
-
- more work, or a failed ticket to retry → plan the next round;
|
|
76
|
-
- a question only the human can answer → ask it in chat and call `wait_for_you({ why })`;
|
|
77
|
-
- the feature does what the plan says → run the checks and declare ready (SKILL.md, "Ready for the human").
|
|
78
|
-
|
|
79
|
-
A failed round is just what came back. Re-plan, retry or fix it yourself.
|
|
80
|
-
`declare_ready` warns when the last round had failures; pass that on.
|
|
81
|
-
|
|
82
|
-
## While and after the human drives
|
|
83
|
-
|
|
84
|
-
Never dispatch while the feature is being driven (`driveActive`). Your own
|
|
85
|
-
commits wait until the drive stops. When the human reports back, follow
|
|
86
|
-
SKILL.md, "After a drive"; when you are satisfied again, declare ready with a
|
|
87
|
-
new scenario.
|
|
88
|
-
|
|
89
|
-
## What the server refuses
|
|
90
|
-
|
|
91
|
-
`dispatch_round` is refused, with the reason, for a draft or shipped feature,
|
|
92
|
-
while a round is already running, while the human is driving this feature, and
|
|
93
|
-
after the human pressed Stop until they have replied. It is also refused when a
|
|
94
|
-
ticket you name is blocked by a ticket outside the round that is still pending
|
|
95
|
-
or failed (or does not exist): the refusal lists each such edge, and the fix is
|
|
96
|
-
to name that blocker in the round too. `stop_round` stops the running round;
|
|
97
|
-
work that already landed stays.
|
|
@@ -1,231 +0,0 @@
|
|
|
1
|
-
<!-- Forked from Matt Pocock's to-tickets skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
|
|
2
|
-
|
|
3
|
-
# Writing tickets
|
|
4
|
-
|
|
5
|
-
Load this when work is worth handing to sandboxed workers and you are about to
|
|
6
|
-
write, reshape or cancel tickets. Whether it is worth it is your call (SKILL.md,
|
|
7
|
-
"Build it the cheapest good way"): parallel pieces, work too big for your own
|
|
8
|
-
window, or long unattended work. Anything else you build yourself.
|
|
9
|
-
|
|
10
|
-
Break the work into **tickets** — vertical slices, each cutting end-to-end
|
|
11
|
-
through every layer it needs, each big enough to be worth a whole agent session
|
|
12
|
-
and small enough to finish inside one — and emit them with
|
|
13
|
-
`mcp__runcastle__emit_tickets`. Synthesize from `spec.md` and the conversation.
|
|
14
|
-
The feature's existing tickets come as summary rows in `get_feature_context`;
|
|
15
|
-
`mcp__runcastle__get_ticket({ seq })` holds one ticket's context, acceptance
|
|
16
|
-
criteria and digest.
|
|
17
|
-
|
|
18
|
-
## The one thing that makes a runcastle ticket different
|
|
19
|
-
|
|
20
|
-
Each ticket is executed by a **single fresh agent, alone, in a sandbox, with
|
|
21
|
-
~100k tokens and NO way to ask a follow-up question.** It sees only what you put
|
|
22
|
-
in the ticket plus the code already in the repo. So — *inverting* the usual
|
|
23
|
-
"avoid file paths, they go stale" rule — the ticket's `context` field **must be
|
|
24
|
-
concrete and self-sufficient**: name the files to touch, the existing patterns
|
|
25
|
-
to copy, the type/schema shapes, the gotchas, the commands to run and verify
|
|
26
|
-
with. A ticket that assumes the agent can ask "where does X live?" is a broken
|
|
27
|
-
ticket. Over-specify context; under-specify nothing.
|
|
28
|
-
|
|
29
|
-
## The second thing: a ticket costs ~10 minutes before it does any work — and pays it twice if it overruns
|
|
30
|
-
|
|
31
|
-
That fresh agent is fresh *every time*. Each ticket gets its own container: cold
|
|
32
|
-
build, a full dependency install (measured 70–507s, ~2.5 min average), then an
|
|
33
|
-
agent that re-reads the spec, the docs digest, and every file your `context`
|
|
34
|
-
names — reading the repo was 16% of all agent time across 42 measured attempts.
|
|
35
|
-
**This overhead is paid per ticket and is the same whether the ticket is twenty
|
|
36
|
-
lines or a day's work.** Splitting one coherent piece of work into three tickets
|
|
37
|
-
triples it, and adds two seams where a new agent has to reconstruct what the
|
|
38
|
-
previous one had just finished learning.
|
|
39
|
-
|
|
40
|
-
But that meter runs both ways. A ticket too big to finish in one pass gets no
|
|
41
|
-
cheap second wind: an iteration is not a retry (ADR-0008) — the container is
|
|
42
|
-
rebuilt, the install runs again, and a brand-new agent re-reads everything from
|
|
43
|
-
the last commit, having lost every conclusion its predecessor reached.
|
|
44
|
-
|
|
45
|
-
So size a ticket to **earn its container and still land in one pass**:
|
|
46
|
-
substantial, with room left at the end for the tests, the self-review and the
|
|
47
|
-
commits. Fine-grained slicing belongs *inside* a ticket, as the
|
|
48
|
-
`acceptanceCriteria` list: the worker works through the criteria and
|
|
49
|
-
commits each finished slice, so one ticket with six criteria orients once; six
|
|
50
|
-
one-criterion tickets orient six times.
|
|
51
|
-
|
|
52
|
-
Expect a **handful of substantial tickets** for a feature — roughly 4–8, each
|
|
53
|
-
worth a session. A dozen thin ones means you sliced by layer or by file, not by
|
|
54
|
-
behaviour. Two or three giant ones means you will pay back the containers you
|
|
55
|
-
saved, with interest, when they overrun.
|
|
56
|
-
|
|
57
|
-
## 1. Draft vertical slices
|
|
58
|
-
|
|
59
|
-
<vertical-slice-rules>
|
|
60
|
-
- Each slice cuts a narrow but COMPLETE path through every layer it needs (schema, API, UI, tests) — vertical, NOT a horizontal slice of one layer.
|
|
61
|
-
- A completed slice is demoable or verifiable on its own.
|
|
62
|
-
- Each slice is worth one fresh ~100k-token agent session working alone and **lands inside it** — leave room for the orientation at the front and the tests, self-review and commits at the back.
|
|
63
|
-
- **Merge when a merge test holds.** Two drafts are one ticket if they touch the same files, share a type or module, or the second one's first act would be re-reading what the first just wrote. Same for the obvious siblings of one behaviour: create/edit/delete of a resource, an endpoint and the UI that calls it, code and the tests that cover it.
|
|
64
|
-
- **Split only for a real reason:** (a) a genuine gate — the later work cannot be written until the earlier work's interface exists; (b) the combined work would not land in one session; (c) a prefactor, which is its own slice done first ("make the change easy, then make the easy change"); (d) parallel width, under the rule below; (e) the wide-refactor sequence below.
|
|
65
|
-
- **Not reasons to split:** one ticket per file, layer, endpoint or component; keeping each ticket easy; keeping diffs small; separating tests from the code they test.
|
|
66
|
-
- **Parallelism pays when the halves are file-disjoint.** Tickets with no blocking edge burn concurrently, each on its own temp branch, landing one at a time (ADR-0002) — so two substantial slices touching *different* files buy real wall-clock. Two touching the *same* files race to land, and the loser fails on a merge conflict: that width costs a re-burn. Overlapping files → merge.
|
|
67
|
-
</vertical-slice-rules>
|
|
68
|
-
|
|
69
|
-
Give each ticket its **blocking edges** — the tickets that must finish before it
|
|
70
|
-
can start. The field is required: a ticket with no blockers gets an empty array.
|
|
71
|
-
|
|
72
|
-
<review-ticket>
|
|
73
|
-
**The review ticket.** Every feature ends with one, unless the human does not
|
|
74
|
-
want it (SKILL.md, "Every feature ends with a review round"). Add **one** ticket
|
|
75
|
-
with `kind: "review"`, blocked by every implementation ticket whose work it
|
|
76
|
-
checks, or by nothing when you built the work yourself. It is not a slice: it is the brief for the agent that exercises the
|
|
77
|
-
*integrated* work on the feature branch and reports what it finds as findings.
|
|
78
|
-
Its `goal` says what to verify and its `acceptanceCriteria` are the things to
|
|
79
|
-
check off, one per behaviour worth confirming — the review agent walks them in
|
|
80
|
-
order. `emit_tickets` refuses a ticket *titled* like a review but carrying
|
|
81
|
-
another kind: set the kind (or retitle it) and re-emit.
|
|
82
|
-
|
|
83
|
-
The review agent works in **exactly one of two modes**, never both — a browser
|
|
84
|
-
**Drive** of the app against your acceptance criteria, or **Gates**: the
|
|
85
|
-
project's verify commands plus a two-axis read of the diff. Reviews that
|
|
86
|
-
attempted both ran long or died having delivered neither.
|
|
87
|
-
|
|
88
|
-
Make the choice obvious. Prescribe a walkthrough only when there is genuinely a
|
|
89
|
-
surface to walk, in plain prose ("open the settings flow, add a project, check
|
|
90
|
-
the empty state"), and write the `acceptanceCriteria` as the steps of that walk.
|
|
91
|
-
When the work changed nothing a human could drive — prompt contracts, docs, an
|
|
92
|
-
internal refactor, a backend-only change — say so in the ticket, and write
|
|
93
|
-
criteria the gates-and-diff read can check off.
|
|
94
|
-
|
|
95
|
-
**Say what to walk, never which mode.** A review ticket never directs the agent
|
|
96
|
-
to Gates — no "Mode: Gates", no "skip the drive" — and the mode is the review
|
|
97
|
-
agent's call from the surface you describe. Least of all give as the reason that
|
|
98
|
-
the change "isn't deployed or installed yet": the drive boots **the branch** —
|
|
99
|
-
the project's drive setup points it at the checkout's own code, migrations and
|
|
100
|
-
assets precisely so a branch is visible in its own drive. A review ticket once
|
|
101
|
-
prescribed Gates on exactly that reasoning, and a UI feature reached the human
|
|
102
|
-
without ever being driven. If the state to check does not exist yet in a fresh
|
|
103
|
-
drive, the `acceptanceCriteria` say how to produce it inside the drive, as far as
|
|
104
|
-
the project's drive instructions allow, and then what to check.
|
|
105
|
-
|
|
106
|
-
The review agent never edits code, so its ticket needs no `seams` beyond the
|
|
107
|
-
surface it exercises, and finding bugs is a *successful* review. Its digest
|
|
108
|
-
carries a prose summary of what landed, which the Review view leads with, so
|
|
109
|
-
`goal` asks for the verification, not for the summary.
|
|
110
|
-
</review-ticket>
|
|
111
|
-
|
|
112
|
-
<parallelism-budget>
|
|
113
|
-
**Budget the parallelism explicitly, before you emit.** A round runs up to
|
|
114
|
-
`burnConcurrency` tickets at once (from `get_feature_context`), but a ticket
|
|
115
|
-
cannot start until every ticket in its `blockedBy` has landed, so a round's
|
|
116
|
-
wall-clock is set by its **critical path**: the longest chain of blocking edges.
|
|
117
|
-
A chain of N tickets is N sequential sandboxes however wide the pool is.
|
|
118
|
-
Measured at `burnConcurrency: 3`, three consecutive batches whose tickets each
|
|
119
|
-
chained end to end came in at **1.09x, 1.16x and 1.00x** — the width was there
|
|
120
|
-
and never spent.
|
|
121
|
-
|
|
122
|
-
So, after slicing, **say it out loud in the session**:
|
|
123
|
-
|
|
124
|
-
- the **critical path** — the longest chain of implementation tickets, named in order, and its length;
|
|
125
|
-
- **how many tickets can run at once**, against this project's `burnConcurrency`;
|
|
126
|
-
- whether that clears the bar: T implementation tickets at concurrency C want a chain no longer than roughly **T / C**.
|
|
127
|
-
|
|
128
|
-
**When it does not clear the bar, reslice.** **A blocking edge is true only when
|
|
129
|
-
the later ticket reads the earlier one's output**: a file it writes, a type or
|
|
130
|
-
interface it defines, a schema it migrates, a stub it fills. "Related", "same
|
|
131
|
-
area", "logical reading order" are not blocking edges — drop them. Where the
|
|
132
|
-
edge is real, merge the two into one ticket, or pull the shared foundation
|
|
133
|
-
forward into the blocker so everything else goes wide behind it.
|
|
134
|
-
|
|
135
|
-
A review ticket is the tail by definition: measure the critical path over the
|
|
136
|
-
implementation tickets and add it. Width only counts if it lands: parallel
|
|
137
|
-
tickets touching the same files collide on landing.
|
|
138
|
-
</parallelism-budget>
|
|
139
|
-
|
|
140
|
-
<ship-a-scenario>
|
|
141
|
-
**Ship a scenario when the state is hard to reach.** A scenario is a project
|
|
142
|
-
command under `.runcastle/scenarios/` that seeds a state and prints the URL to
|
|
143
|
-
land on, so a test drive starts where the feature is instead of on a blank app.
|
|
144
|
-
Read `get_feature_context`'s `scenarios` field — `{ setUp, runnerConfigured,
|
|
145
|
-
scenarios, fixtures }` — before you decide anything.
|
|
146
|
-
|
|
147
|
-
**When `setUp` is true**, ask of each ticket: will the person driving this have
|
|
148
|
-
to build state by hand to see it — an account, a cart with an expired coupon?
|
|
149
|
-
|
|
150
|
-
- 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 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.
|
|
151
|
-
- Name that scenario in the review ticket's `context`, if there is one, so the reviewer replays it rather than improvising the setup; and use it as the `scenario` you declare ready with.
|
|
152
|
-
- Copy changes and settings toggles get none. A scenario earns its place only where reaching the state is the chore.
|
|
153
|
-
- The worker writes and typechecks the scenario but cannot run it — a sandbox has no drive.
|
|
154
|
-
|
|
155
|
-
**When `setUp` is false**, card nothing about scenarios. Tell the human once
|
|
156
|
-
that + New scenario in a test drive sets scenarios up for this project.
|
|
157
|
-
</ship-a-scenario>
|
|
158
|
-
|
|
159
|
-
<wide-refactor-exception>
|
|
160
|
-
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**. 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 a ticket blocked by the expand, 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 all block a final integrate-and-verify ticket — green is promised only there.
|
|
161
|
-
</wide-refactor-exception>
|
|
162
|
-
|
|
163
|
-
<preview-screen-block>
|
|
164
|
-
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 worker 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. **Never tell a worker to read the whole file.**
|
|
165
|
-
|
|
166
|
-
A ticket with no screen does not mention the preview.
|
|
167
|
-
|
|
168
|
-
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.
|
|
169
|
-
</preview-screen-block>
|
|
170
|
-
|
|
171
|
-
<connect-designed-screens>
|
|
172
|
-
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**:
|
|
173
|
-
|
|
174
|
-
- swap the sample data for real data (queries, API calls);
|
|
175
|
-
- add the behaviour the design left out;
|
|
176
|
-
- change markup only where the wiring needs it.
|
|
177
|
-
|
|
178
|
-
When wiring forces a visible change (a field the design lacked, say), the worker calls it out in its digest.
|
|
179
|
-
</connect-designed-screens>
|
|
180
|
-
|
|
181
|
-
## 2. Build each ticket to the schema
|
|
182
|
-
|
|
183
|
-
Emit an array of `TicketInput`. Number them from 1 in the array in **dependency
|
|
184
|
-
order (blockers first)**; `blockedBy` holds those numbers.
|
|
185
|
-
|
|
186
|
-
Each ticket:
|
|
187
|
-
- **title** — `{group}: {task}`, in the domain vocabulary: `Checkout: apply the coupon at payment`.
|
|
188
|
-
- **goal** — the end-to-end behaviour this ticket makes work, from the user's/caller's perspective. Not a layer list.
|
|
189
|
-
- **context** — everything the sandboxed agent needs and cannot ask for: file paths, patterns to follow, type/schema shapes, gotchas, how to run and verify. Be concrete.
|
|
190
|
-
- **acceptanceCriteria** — `string[]`, each independently *verifiable* (a behaviour you can observe, a command that passes). Four to eight on a substantial ticket is right; a list running well past that will not land in one pass. Criteria are observable behaviours; ask for a test as a criterion only for a bug repro or a contract worth protecting, never for its own sake. A ticket that fixes red round gates names the failing command and test files in its `context` and has one criterion that re-runs them narrowed (never the full suite — the host runs that once per round). A review reports useless tests as low-severity observations, never defects: when the round already mints a fix ticket, fold those observations into it as one criterion (delete or rewrite the named tests), and never mint a ticket only to delete useless tests.
|
|
191
|
-
- **seams** — `string[]`, the public interfaces to test at (from the spec's Seams; prefer existing, prefer the highest, prefer one).
|
|
192
|
-
- **blockedBy** — `number[]`, **required**, only true gates; `[]` when there are none.
|
|
193
|
-
- **kind** — `"implementation"` (the default, omit it) or `"review"`.
|
|
194
|
-
- **model** — optional, and usually omitted. See below.
|
|
195
|
-
|
|
196
|
-
### Assigning a model (only from the annotated roster)
|
|
197
|
-
|
|
198
|
-
`get_feature_context` returns `annotatedModels`: `{ id, runtime, note }` for the
|
|
199
|
-
models the human wrote a use-case note against. That list is the *entire* set
|
|
200
|
-
of ids you may use.
|
|
201
|
-
|
|
202
|
-
- **Empty list → never set `model`.**
|
|
203
|
-
- **Non-empty → set it only where a note plainly fits the ticket.** Unset is the good default.
|
|
204
|
-
- **Never invent an id**, and never write one not in `annotatedModels` — the store rejects it and the whole batch fails to emit.
|
|
205
|
-
|
|
206
|
-
## 3. Self-check, then emit
|
|
207
|
-
|
|
208
|
-
Check yourself, in this order:
|
|
209
|
-
|
|
210
|
-
1. **Can any two of these be one ticket?** Take them pair by pair; merging changes the rest.
|
|
211
|
-
2. **Would any one overrun?** Split it where its files stop overlapping.
|
|
212
|
-
3. Is each survivor a demoable vertical slice, worth a session?
|
|
213
|
-
4. Are the blocking edges minimal and true? State the critical path and the width against `burnConcurrency`, and reslice if the chain runs longer than roughly total / concurrency. Do independent tickets touch different files?
|
|
214
|
-
5. Is every `context` self-sufficient for an agent that cannot ask?
|
|
215
|
-
6. If you chose a review, is there exactly one `kind: "review"` ticket, blocked by the work it checks?
|
|
216
|
-
7. If `scenarios.setUp`, does every ticket that builds hard-to-reach state ship a scenario for it?
|
|
217
|
-
|
|
218
|
-
Then `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do
|
|
219
|
-
NOT write ticket files.** It returns `{ stored, ids }`, logs the timeline event
|
|
220
|
-
itself, and opens the **Work** view beside the chat. Emit the **whole batch,
|
|
221
|
-
fully enriched, in one call**; never emit placeholder contexts to enrich
|
|
222
|
-
afterwards — a round dispatched mid-enrichment burns workers on placeholders.
|
|
223
|
-
|
|
224
|
-
## Reshaping tickets later
|
|
225
|
-
|
|
226
|
-
When something changes after tickets exist, walk the ticket list against it:
|
|
227
|
-
|
|
228
|
-
- Stale but still needed → `update_ticket({ id, ...changed fields })` (pending or failed only).
|
|
229
|
-
- No longer needed → `cancel_ticket({ id, reason })` (pending or failed only).
|
|
230
|
-
- New work → `emit_tickets` again; tickets accumulate on the feature.
|
|
231
|
-
- Landed work now wrong → a NEW ticket that corrects it. Never touch landed or running tickets.
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: prepare
|
|
3
|
-
description: Establish a project's host settings with evidence — the dev command, the drive setup/stop scripts, the dev-database reset — by running them on the developer's own machine rather than guessing them from config. Covers the drive contract, the scenario contract it feeds, discovering a project's shape, and the closing dry-run drive. Entry skill for kind=prepare sessions.
|
|
4
|
-
disable-model-invocation: false
|
|
5
|
-
---
|
|
6
|
-
|
|
7
|
-
# Prepare — establish this project's host settings
|
|
8
|
-
|
|
9
|
-
A preparation session runs in the developer's **real checkout on their own machine**, not in a sandbox. That is the whole capability it exists to use: every setting here can be *run* instead of guessed at, and a value with an account of itself is worth more than a value that merely looks plausible.
|
|
10
|
-
|
|
11
|
-
Your system prompt carries the per-session facts — the repo path, which keys are still open, what has already been established and how stale it is. This skill is the method.
|
|
12
|
-
|
|
13
|
-
## The order of the conversation
|
|
14
|
-
|
|
15
|
-
1. Open by naming what is still open and what you need from the human for each. Do not survey the repo first; the prompt already told you the gap.
|
|
16
|
-
2. Work the keys **one at a time**: propose, ask, run it if they agree, record it.
|
|
17
|
-
3. If the open keys touch the drive loop, author the machinery (below) before you record the two hook commands — the settings are invocation lines for scripts that must exist.
|
|
18
|
-
4. If the repo builds with a toolchain the sandbox image does not carry, author its Dockerfile (below) — the sandbox keys are worth nothing in an image that cannot run them.
|
|
19
|
-
5. Close by proposing a dry-run drive (below). Values that have never been driven look exactly as trustworthy as values that run perfectly.
|
|
20
|
-
|
|
21
|
-
**Ask before you act.** Anything that starts or stops a service, creates or migrates a database, installs software, or writes outside the repo needs the human to agree first: say what you are about to run and why, then wait. Their own stack is running next to yours.
|
|
22
|
-
|
|
23
|
-
## The eight keys
|
|
24
|
-
|
|
25
|
-
Four describe the drive loop and can only be settled here:
|
|
26
|
-
|
|
27
|
-
- **`devCommand`** — spawned in a drive-owned terminal pane for the length of a test drive. The first localhost URL it prints becomes the "Open app" link.
|
|
28
|
-
- **`driveSetupCommand`** / **`driveStopCommand`** — run on the host, in the project repo, before the dev pane starts and after the drive stops. They are the **invocation lines** of scripts you write and commit, not the machinery itself.
|
|
29
|
-
- **`dbResetCommand`** — **not** part of the drive loop. Its only consumer is the migration-drift banner after a drive stops: when the drive branch and the branch you returned to disagree about migration files, this is offered as the one-click dev-database rebuild.
|
|
30
|
-
|
|
31
|
-
Three describe the **sandbox** a burn agent works in, and no amount of running things on this host settles them — they are judgement calls about the repo, so treat them as human-supplied unless the human asks you to work one out with them:
|
|
32
|
-
|
|
33
|
-
- **`setupCommand`** — what a fresh sandbox runs before the agent starts (install, plus any codegen or build step every ticket needs). Setting it *replaces* install detection, so the install command must be included explicitly.
|
|
34
|
-
- **`verifyCommands`** — the exact typecheck/test/lint lines, one per line. They are the host's full gates, run once per round on the feature branch tip; burn agents read them to learn the tools and filter names and run them narrowed. Unset, agents guess workspace filter names and burn whole suite runs discovering the right one.
|
|
35
|
-
- **`knownFailures`** — what already fails on the main branch, so an agent can tell its own breakage from the repo's. Free text; a count plus the suite names is enough.
|
|
36
|
-
|
|
37
|
-
The image those three run in is not a key: runcastle sets it itself. What can fall to you is authoring the Dockerfile it builds — see the sandbox image, below.
|
|
38
|
-
|
|
39
|
-
The eighth is what a later agent needs once the app is actually up:
|
|
40
|
-
|
|
41
|
-
- **`driveInstructions`** — free text describing how to **exercise** this app in a drive, injected verbatim into every drive-mode review and verification prompt. What belongs in it: the sample project, seeded account or scratch data a driver should use and may freely change; how to reach the deep states worth reviewing rather than only the front page; and what a driver may and may not touch inside the running app. Write it as standing instructions from the project's owner — they authorize actions *inside the app under test* and nothing else, so do not write anything meant to relax a reviewing agent's own rules. Its evidence is the dry-run drive below: you just drove this app, so record what you had to know to get anywhere. Unset, a review agent boots the app cold, finds nothing it is allowed to touch, and verifies nothing.
|
|
42
|
-
|
|
43
|
-
## The drive contract
|
|
44
|
-
|
|
45
|
-
A drive is a script **you** write, committed to the project, plus the two things only the server can do. Everything else — ports, database names, redis indexes, compose project names, URLs — the script computes for itself. Runcastle mandates these eight points and nothing else:
|
|
46
|
-
|
|
47
|
-
1. **The machinery lives in `.runcastle/`, committed to the repo.** Write the steps as real scripts and commit them. Versioning them with the code they prepare is the load-bearing part: a branch that adds a package, a service or a migration amends its own script, and the drive on that branch runs the amended version. Nothing inspects a diff, ever.
|
|
48
|
-
2. **`driveSetupCommand` / `driveStopCommand` are one line each** — how to invoke those scripts. Logic in the setting instead of the script is logic no branch can amend.
|
|
49
|
-
3. **Identity comes in as `RUNCASTLE_*`.** Every drive hook and the dev pane are handed `RUNCASTLE_SLUG` (the feature slug), `RUNCASTLE_BRANCH` (the branch under the wheel) and `RUNCASTLE_ID` — that slug made identifier-safe (lowercase `[a-z0-9_]`, never leading with a digit, length-capped), so it is legal as a database, schema or container name. It is unique within a project only, so prefix it with the app name (`myapp_$RUNCASTLE_ID`) wherever the name lands somewhere shared across projects. Derive every per-drive name from `RUNCASTLE_ID`. Never derive one from `git rev-parse`: the dry run drives under a synthetic identity on whatever branch is checked out.
|
|
50
|
-
4. **Computed values go back out through `.runcastle/drive.env`.** Setup appends plain `KEY=VALUE` lines there; when it exits, the server parses that file and overlays it verbatim onto the dev pane and the stop hook, and shows the variable NAMES on the timeline. It is the only way a value your script computed reaches the dev server — a variable exported inside the script dies with the script. Truncate the file at the top of setup so a rerun does not accumulate stale lines.
|
|
51
|
-
5. **`.runcastle/drive.env` MUST be gitignored.** Add the entry yourself. The server deletes the file when a drive ends, but a scratch file holding a connection string must never be one `git add -A` away from a commit.
|
|
52
|
-
6. **Every step is unconditionally idempotent.** Install, migrate, seed, compose up — run them every time, never behind a "has anything changed?" check. A no-op on a clean tree is cheap; a skipped install on a branch that added a package is a dead drive. This is how the loop absorbs whatever a feature branch changed with no delta detection anywhere.
|
|
53
|
-
7. **Exit 0 means the services are actually up.** The waits belong INSIDE the script — `docker compose up --wait`, a `pg_isready` loop, curl-until-healthy — because the dev pane starts the instant setup returns. The server waits for the app itself; it will not wait for your database.
|
|
54
|
-
8. **Everything the app needs to reach its data goes out through `.runcastle/drive.env`** — above all the database URL, plus any cache or queue URL the app reads. Scenarios (below) and the dev server both get exactly that overlay and nothing else. A setup that hands the database to the dev server some other way — writing the app's own `.env.local`, say — leaves every scenario seeding the wrong database, or none, while the app looks fine.
|
|
55
|
-
|
|
56
|
-
Stop undoes what setup made, **for this identity only**: drop the database it created, take its compose project down with its volumes, free its ports. The human's own stack is never yours.
|
|
57
|
-
|
|
58
|
-
Writing those files is the one exception to a preparation session not editing the repo: you may write `.runcastle/` and `.gitignore` in this checkout and nothing else — a hook enforces it. Show the human the script before you commit it; it is their repo and their PR.
|
|
59
|
-
|
|
60
|
-
## Scenarios
|
|
61
|
-
|
|
62
|
-
A drive can land the human in a seeded state instead of a blank app. That is the **scenario contract**, a peer of the drive contract above:
|
|
63
|
-
|
|
64
|
-
- A scenario is a file at `.runcastle/scenarios/<group>/<name>.<ext>`, committed. The folder is the group (`checkout/` → Checkout), the file name the display name (`expired-coupon.ts` → Expired coupon). Files and folders starting with `_` (e.g. `_fixtures/`) are shared code, never listed.
|
|
65
|
-
- The project's **scenario runner** is one command with a `{file}` placeholder (`bun {file}`, `bin/rails runner {file}`). Runcastle runs it in the drive's checkout with the `RUNCASTLE_*` identity, the `drive.env` overlay (point 8 is why that overlay must carry the database), and a fresh `RUNCASTLE_SCENARIO_RUN` per run that keeps entities unique.
|
|
66
|
-
- It prints `ENTRY_URL=<url>` (the last wins; relative resolves against the drive URL), and optionally `TOKEN=<value>`, a credential for API calls.
|
|
67
|
-
- A sibling `.http` file at the same path (`feed/blocked-only.ts` + `feed/blocked-only.http`) is that scenario's API call, sent from the drive's API view with **Try it**.
|
|
68
|
-
|
|
69
|
-
**You do not set scenarios up.** Record no runner, write no drive-only login route, add no fixtures. The first + New scenario chat in a drive does all of that, where it is proven by landing the human; a route written blind here is not. Your part is point 8: a `drive.env` that carries the data.
|
|
70
|
-
|
|
71
|
-
## Discover the shape before you author anything
|
|
72
|
-
|
|
73
|
-
Projects differ in every dimension this touches, and a script fitted to a project you imagined is worse than no script at all. Your system prompt reports what the server could probe for itself — the platform, whether there is a compose file, whether `.runcastle/` already exists, which lockfile is present. Take those as read and find out the rest, by reading the repo and running things here:
|
|
74
|
-
|
|
75
|
-
- **Workspace layout** — one package or a monorepo with workspaces, and which package the app actually is. Install, migrate and seed each run from somewhere specific.
|
|
76
|
-
- **The services the app needs to boot** — database, redis, queues, object storage, a second process. Read the config and the env example, then confirm with the human.
|
|
77
|
-
- **Whether the human actually uses docker for this project.** A compose file in the repo is not consent to run it.
|
|
78
|
-
- **Hosted or local data stores** — a local postgres you may freely `createdb` on is a very different recipe from a hosted one where you may only branch or add a schema.
|
|
79
|
-
- **How the app loads its environment** — see the audit below.
|
|
80
|
-
|
|
81
|
-
Then write the smallest script that brings THAT shape up. For worked shapes to adapt — postgres-per-drive, docker compose, redis, hosted databases, deterministic ports — read `references/recipes.md`. They are illustrations, not a library: take the idea and fit it to what you found, in the language this host actually runs.
|
|
82
|
-
|
|
83
|
-
## Audit how the app loads its environment
|
|
84
|
-
|
|
85
|
-
The overlay is process environment, which beats a `.env` file in dotenv, Prisma and Next by default. One pattern defeats it: a loader told to clobber what is already exported — `dotenv.config({ override: true })` and its equivalents — ignores everything the script computed and quietly keeps the app on the shared database. That is a drive that looks perfect while testing the wrong data. No machinery can detect it; you are the detector.
|
|
86
|
-
|
|
87
|
-
Grep the app's entry points and config for env loading and decide, for each, which side wins. Where the process environment loses: get it fixed — you may not edit app code from this session, so propose the change (usually dropping `override`) and let the human make it — or, if it must stay, record the finding with `record_event` naming the file and what it breaks, so the first confusing drive is not a mystery.
|
|
88
|
-
|
|
89
|
-
## The sandbox image
|
|
90
|
-
|
|
91
|
-
Burn agents do not work on this host; they work in a container. The image runcastle builds carries a JavaScript toolchain and the agent CLIs — Node, Bun, git, curl — and nothing beyond that, so a repo that builds with a JDK, a Python, a Go compiler or a Rust toolchain cannot run its own `setupCommand` there: the burn dies on `mvn: not found`. The fix is a Dockerfile committed at **`.runcastle/sandbox/Dockerfile`**. Runcastle detects it, builds it as this project's image, and every burn runs in that image from then on.
|
|
92
|
-
|
|
93
|
-
Deciding whether this project needs one is part of this session — you are in the real checkout, which is where the evidence is. Read the manifests at the root and in each workspace:
|
|
94
|
-
|
|
95
|
-
- `pom.xml`, `mvnw` — a JDK and Maven
|
|
96
|
-
- `build.gradle`, `build.gradle.kts`, `gradlew` — a JDK and Gradle
|
|
97
|
-
- `pyproject.toml`, `requirements.txt` — Python and its installer (pip, or uv where the project uses it)
|
|
98
|
-
- `go.mod` — Go
|
|
99
|
-
- `Cargo.toml` — Rust
|
|
100
|
-
- `Gemfile` — Ruby
|
|
101
|
-
|
|
102
|
-
A mixed repo gets the union: a service in Go beside a Java client needs both. Anything else the project shells out to in its setup or verify commands and the stock image lacks belongs here too — a database client, a protobuf compiler — on the same evidence, that you saw it in the repo.
|
|
103
|
-
|
|
104
|
-
**A JavaScript-only repo gets no Dockerfile.** The stock image already covers it, and an unnecessary project image is one more thing to build, rebuild and keep fresh in exchange for nothing. Do not write one to be helpful.
|
|
105
|
-
|
|
106
|
-
When the project does need one, what you write:
|
|
107
|
-
|
|
108
|
-
- **It starts `FROM sandcastle:runcastle`** and layers onto it. That base is what keeps the agent CLIs, git and Node present; a Dockerfile from any other base is not a runcastle sandbox, and burns in it fail on the agent binary before they ever reach the toolchain.
|
|
109
|
-
- **Read the stock Dockerfile before you write a single install line**, rather than assuming its distro or package manager. It is the build context runcastle scaffolds at `~/.runcastle/sandbox-build/Dockerfile` once the image has been built on this machine, and it also ships inside the installed runcastle package as `sandcastle-template/Dockerfile`. Follow its idioms — the same package manager, the same flags, the same cleanup at the end of the layer. If you cannot find it, ask the human instead of guessing what you are layering onto.
|
|
110
|
-
- **The toolchain only, never the project's dependencies.** Install the JDK and Maven; do not run `mvn install` or `go mod download` at build time. Dependencies are what `setupCommand` installs per burn, from the branch's own manifests — anything baked into the image is stale the first time a branch adds a package.
|
|
111
|
-
- **No package caches in layers.** Do not prime `~/.m2`, `~/.gradle`, `~/.cache/pip` or the Go module cache in a `RUN` step. Download caches are mounted into the sandbox from the host, which is how they stay current across burns; a cache baked into a layer is weight the image carries and wrong the moment a lockfile moves.
|
|
112
|
-
- **Keep it minimal** — one `RUN` block where the package manager allows it, versions pinned only where the project pins them, nothing added "while we're here". Every line is a line someone maintains.
|
|
113
|
-
|
|
114
|
-
It is committed project machinery, exactly like the drive scripts, for the same reason: a branch that changes the toolchain amends its own Dockerfile, and burns on that branch get the amended image. Writing it falls under the same exception — `.runcastle/` and `.gitignore`, nothing else — and the same courtesy: show the human the file before you commit it.
|
|
115
|
-
|
|
116
|
-
If `.runcastle/sandbox/Dockerfile` is already there, read it and **edit conservatively**. Someone chose those lines, and a customization you do not recognize is still a decision. Say what you probed and what you think is missing — "I see `pyproject.toml` but no Python in the image" — and let the human decide. An existing Dockerfile that covers what you found needs no edit at all.
|
|
117
|
-
|
|
118
|
-
**You never build the image.** That is a watched terminal action the human takes: the Enable AFK burns card offers **Build image**, and on a successful build runcastle points this project at the image itself — there is no image setting for you to work out or record. So end on it. Tell them the Dockerfile is written (or already right), that the card now offers Build image, and that burns use the project image after that click; until then they keep running in whatever image they run in today.
|
|
119
|
-
|
|
120
|
-
## Recording what you establish
|
|
121
|
-
|
|
122
|
-
One `record_finding` call per key, and `evidence` is not optional in spirit: record what you ran and what it printed, or what the human told you. A value with no account of itself is a guess with a source field.
|
|
123
|
-
|
|
124
|
-
`userSupplied` is the one flag worth being careful with. **True** means the human GAVE you this value or confirmed it verbatim — that marks it as theirs and permanently stops automatic runs from overwriting it. Leave it **false** for anything you worked out yourself, even with them watching; that stays improvable by a later run. Getting this backwards silently retires a field from preparation forever, and the only way back is the human clearing it.
|
|
125
|
-
|
|
126
|
-
Secrets: this is a development environment and the human has agreed to supply real connection strings and credentials here. Store them as given. Do not paste a secret into a timeline note or a commit message — `record_finding` is the only place a value belongs.
|
|
127
|
-
|
|
128
|
-
## Closing move: propose a dry-run drive
|
|
129
|
-
|
|
130
|
-
Once the open drive keys are recorded, end the session by proposing one. Recorded values that have never been driven look exactly as trustworthy as values that run perfectly, and the first person to find out otherwise is someone mid-feature with a broken environment.
|
|
131
|
-
|
|
132
|
-
**Ask first.** This starts services and creates a database on their machine. Name what will run — the setup command, the dev command, the stop command — then wait. If they decline, end the session normally: the keys simply stay unverified, and the drive UI will keep saying so.
|
|
133
|
-
|
|
134
|
-
On a yes, `dry_run_drive({ action })` runs it in two halves and you inspect between them:
|
|
135
|
-
|
|
136
|
-
1. **`start`** — the server runs `driveSetupCommand` with the identity variables, reads back the `.runcastle/drive.env` it wrote (the reply lists the variable NAMES it parsed) and spawns `devCommand` in a real drive pane under that overlay. Identity is the reserved slug `prep-dry-run`, so `RUNCASTLE_ID` is `prep_dry_run` (in every project — the id is unique within a project only) and a script deriving from it makes e.g. `myapp_prep_dry_run`, on the current branch. Nothing is checked out.
|
|
137
|
-
2. **While it is up**, check what the server cannot: the variable names came back as you meant them — and they include the database URL, or whatever the app reaches its data by (point 8). If they do not, the app is getting its data some other way that scenarios cannot see: tell the human, fix the setup script to write it to `drive.env`, and drive again. That holds for an existing setup you did not write, too. Then: the temp database exists and is FRESH, the migrations applied, and the app actually RESPONDS at the sniffed URL — the server waits for it to answer before "Open app" goes live, but only you can say the page is the right one. `status` gives you the pane and the URL while you work.
|
|
138
|
-
3. **`stop`** — the server runs `driveStopCommand` under the same overlay. Then check the cleanup: temp database gone, no orphaned process, container or volume left behind.
|
|
139
|
-
|
|
140
|
-
Anything off at any step, fix it — amend the script, or `record_finding` for a key — and run the WHOLE thing again until a pass is clean. Watch especially for a `myapp_prep_dry_run` left standing after the stop: an idempotent setup will happily reuse it on the next start, so a teardown that never worked reads exactly like a drive that did.
|
|
141
|
-
|
|
142
|
-
The verified stamps are computed server-side from what the machinery observed, and only on a clean full pass. You cannot mark your own homework: your deeper checks decide whether to retry, never what gets stamped.
|
|
143
|
-
|
|
144
|
-
Close with one line telling the human that + New scenario in their next test drive sets up scenarios, so a drive can land them in the state they want to test.
|
|
145
|
-
|
|
146
|
-
## When nothing is open
|
|
147
|
-
|
|
148
|
-
If your system prompt says every key is already set, this session is a **confirmation**, not a preparation. Say which values are recorded and how stale they are, ask the human whether they still hold, and stop. Do not re-derive a settled value, and do not propose a dry run unasked — replacing a measured value with a fresh guess makes preparation worse, not better.
|