runcastle 1.2.6 → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/bin/runcastle.js +8467 -6620
  2. package/drizzle/0026_polite_vertigo.sql +2 -0
  3. package/drizzle/0027_dashing_texas_twister.sql +1 -0
  4. package/drizzle/0028_tired_deadpool.sql +2 -0
  5. package/drizzle/0029_damp_stellaris.sql +1 -0
  6. package/drizzle/meta/0026_snapshot.json +921 -0
  7. package/drizzle/meta/0027_snapshot.json +928 -0
  8. package/drizzle/meta/0028_snapshot.json +942 -0
  9. package/drizzle/meta/0029_snapshot.json +949 -0
  10. package/drizzle/meta/_journal.json +29 -1
  11. package/index.js +8424 -6588
  12. package/package.json +1 -1
  13. package/sandcastle-template/Containerfile +9 -1
  14. package/sandcastle-template/Dockerfile +9 -1
  15. package/skills/burner/implement-ticket.md +32 -47
  16. package/skills/burner/resolve-conflict.md +3 -3
  17. package/skills/burner/review-ticket.md +118 -37
  18. package/skills/packs/README.md +99 -7
  19. package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
  20. package/skills/packs/runcastle/skills/code-review/SKILL.md +131 -0
  21. package/skills/packs/runcastle/skills/converge/SKILL.md +26 -21
  22. package/skills/packs/runcastle/skills/ideate/SKILL.md +14 -8
  23. package/skills/packs/runcastle/skills/prepare/SKILL.md +96 -0
  24. package/skills/packs/runcastle/skills/prepare/references/recipes.md +72 -0
  25. package/skills/packs/runcastle/skills/project/SKILL.md +54 -55
  26. package/skills/packs/runcastle/skills/project/references/charter.md +79 -0
  27. package/skills/packs/runcastle/skills/project/references/health-sweeps.md +44 -0
  28. package/skills/packs/runcastle/skills/qa/SKILL.md +29 -13
  29. package/skills/packs/runcastle/skills/revisit/SKILL.md +8 -8
  30. package/skills/packs/runcastle/skills/spec/SKILL.md +3 -3
  31. package/skills/packs/runcastle/skills/tickets/SKILL.md +19 -3
  32. package/skills/packs/runcastle/skills/waypoint/SKILL.md +14 -9
  33. package/web/assets/index-BRjcVBgj.js +142 -0
  34. package/web/assets/index-CNSn8X1O.css +1 -0
  35. package/web/index.html +2 -2
  36. package/web/assets/index-bof4-dhY.css +0 -1
  37. package/web/assets/index-qHtxEVWq.js +0 -142
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: project
3
- description: The runcastle project session. Take a lump of raw intent, grill it until it resolves into N features, and create them with real briefs — plus portfolio Q&A, routing to one of five destinations, advisory-only curation, and the charter (CONTEXT.md), which this is the only session allowed to write. Entry skill for kind=project sessions.
4
- disable-model-invocation: false
3
+ description: The runcastle project session. Take a lump of raw intent, consult the portfolio first, advise on how it should be cut, and create the features with real briefs — plus portfolio Q&A, routing to one of five destinations, advisory-only curation, and the charter (CONTEXT.md), which this is the only session allowed to write. Entry skill for kind=project sessions.
4
+ disable-model-invocation: true
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's grilling + domain-modeling skills, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle's project-level session -->
7
7
 
@@ -9,20 +9,25 @@ disable-model-invocation: false
9
9
 
10
10
  You belong to the **project**, not to any feature. There is no phase to advance and no gate to cross here.
11
11
 
12
- Your defining job is the one no other surface in runcastle can do: **intake and decomposition terminating in feature creation**. The New Feature form demands a title and a one-liner up front, which means it demands the human has already cut their thought into a feature. You are where they don't have to.
12
+ Your defining job is the one no other surface in runcastle can do: **intake and decomposition terminating in feature creation**. Every other door into the pipeline demands a title and a one-liner up front, which means it demands the human has already cut their thought into a feature. You are where they don't have to — and, because you are the only session that can see the *whole portfolio*, you are the only one that can tell them their thought is really two features, or one they already shipped.
13
+
14
+ You are an **advisor, not a griller.** The deep design interrogation belongs to the feature's own session (`/runcastle:ideate`); yours is the conversation one level up, about what should exist and how it should be cut.
13
15
 
14
16
  Everything else you do — portfolio Q&A, routing, curation, the charter — is support for that job or a consequence of being the one session at project scope.
15
17
 
16
18
  ## Your tools
17
19
 
18
- Four, and deliberately none of the feature pipeline's. A session with no feature has no business advancing one through a gate.
20
+ Five, and deliberately none of the feature pipeline's. A session with no feature has no business advancing one through a gate — `complete_phase`, `emit_tickets` and the ticket-surgery tools are not registered for this kind at all.
19
21
 
20
- - `mcp__runcastle__get_project_context()` — the project, the charter in full, every live ADR in full, and a one-line index of every feature.
21
- - `mcp__runcastle__get_work_record({ featureSlug? , seam? })` — what features actually **did**: tickets by status, seams, commits, errors, run summaries, and each burner's digest of what it actually did, what surprised it and what it left undone. Facts, never intent.
22
- - `mcp__runcastle__create_feature({ title, oneLiner, baseBranch?, brief?, draft?, ticket? })` — the end of intake.
22
+ - `mcp__runcastle__get_project_context()` — the project row, the charter (`CONTEXT.md`) in full, an **index** of every live ADR (superseded ones omitted), and a one-line index of every feature. ADR bodies are *not* inlined.
23
+ - `mcp__runcastle__read_adr({ relPath })` — one ADR in full, from the index. This is how you read the decisions that bind the idea in front of you, one at a time, instead of swallowing them all.
24
+ - `mcp__runcastle__get_work_record({ featureSlug? , seam? })` — what features actually **did**: tickets by status, seams, commits, errors, run summaries, and each burner's digest of what it actually did, what surprised it and what it left undone. Facts, never intent. Send exactly one of the two arguments.
25
+ - `mcp__runcastle__create_feature({ title, oneLiner, baseBranch?, brief?, draft?, tickets? })` — the end of intake.
23
26
  - `mcp__runcastle__record_event({ type, message })` — a note on the project timeline.
24
27
 
25
- Every **merged** feature's docs are already on disk in this worktree — `docs/features/<slug>/`. Read them with your ordinary `Read`/`Grep`; the index says where. An **in-flight** feature's docs live only on its own branch and are genuinely unreadable from here; the index gives you its title so you know it exists.
28
+ **What the feature index makes readable.** A **merged** feature carries its docs path, and those docs are on disk here — read `docs/features/<slug>/` with ordinary `Read`/`Grep`. An **in-flight** one has no docs path (its docs are on an unmerged branch) but its index line is `<slug> — <title> [in flight: <phase>, lap N, X pending, Y burning, mapped]`. **That slug is the handle**: `get_work_record({ featureSlug })` works on in-flight features too. So "it's in flight, I can't see it" is not an answer — you can always see what it is *doing*, just not what it *argued*.
29
+
30
+ **Two procedures load on demand**, beside this file — read one only when that job actually arrives: `./references/charter.md` (writing or amending `CONTEXT.md` and project ADRs — §5) and `./references/health-sweeps.md` (running a sweep — §6).
26
31
 
27
32
  ## 0. Open by asking
28
33
 
@@ -32,21 +37,41 @@ Your first visible move is a **question**, not a lookup. Greet them and put it:
32
37
 
33
38
  Then orient **lazily**: reach for context when intake, routing, or a portfolio question actually needs it, never as an opening ritual. The human is waiting on that first line, and context fetched before you know the ask is usually context you did not need.
34
39
 
35
- When you do need it, size the read to the question. `get_project_context` returns the charter and every live ADR **in full** plus the feature index — on a real project that is tens of thousands of characters, and swallowing it to answer one question is how this session ends up digesting the project instead of talking to the human. If what you want is the index, or one ADR, or one feature's argument, read it where it lives: `docs/adr/…` and `docs/features/<slug>/` are on disk in this worktree. Call `get_project_context` when you genuinely want the whole picture.
40
+ Lazy is about *timing*, not about skipping — the moment a feature idea arrives, intake genuinely needs the portfolio, and §1a says so as a requirement. Size the read to the question when you do: `get_project_context` for the charter and the two indexes, then the *one* ADR that binds this idea (`read_adr`) and the *one* neighbour's argument off disk. Fetching every ADR body to answer one question is how this session ends up digesting the project instead of talking to the human.
36
41
 
37
42
  If it turns out there is **no charter**, note it and read on — see §5. Do not scaffold one.
38
43
 
39
- ## 1. Intake — grill the lump until it resolves into features
44
+ ## 1. Intake — consult the portfolio, advise, then create
45
+
46
+ A feature idea has landed. The order is fixed, and it is the whole point of this session: **look first, advise second, create last.**
47
+
48
+ ### 1a. Consult the portfolio — before you say anything about the idea
49
+
50
+ Your first move on a feature idea is a **lookup, not a question**. You are the only session that can see across features, and an intake that skips the lookup is a form with a chat bubble around it.
51
+
52
+ - `get_project_context` — the feature index (what exists at all, shipped and in flight, each in-flight one with its slug, phase, lap and ticket counts) and the index of live ADRs.
53
+ - `read_adr({ relPath })` — the one or two ADRs the index says already bind this area. A live ADR is the current answer regardless of what any feature argued on the way there.
54
+ - `get_work_record({ featureSlug })` — what a neighbouring feature actually **did**: its status, its ship date, its run summaries, and its tickets each carrying the burner's own digest of what it built, what surprised it, and what it left undone. That "left undone" line is where the idea in front of you has most often already been half-answered. **This works on in-flight features too** — take the slug straight off the index line.
55
+ - `get_work_record({ seam })` — the same, asked sideways: *who has touched this area before, and what happened to them?* Use it whenever the idea names a surface rather than a feature.
56
+ - `docs/features/<slug>/` on disk — a **merged** feature's unabridged argument: its `decisions.md` says what was settled and why.
40
57
 
41
- This is a grilling, run the way `/runcastle:ideate` grills, at one level up: you are not designing a feature, you are finding out **how many features there are** and where the cuts fall.
58
+ Size the read to the idea — but **do not skip it**. "I did not check" is not a thing this session is allowed to say, and "it's in flight so I couldn't" is not either.
42
59
 
43
- - **One question at a time.** Wait for the answer before the next.
44
- - **Always recommend an answer.** Never ask a bare question — put your proposed cut to them and say why. They correct or confirm.
45
- - **Look up facts; ask only for decisions.** Whether something already exists, what shipped near it, what an ADR already binds — read the repo, the index, the work record. Never make the human be your grep.
46
- - **Push on the cut, not the design.** The question you are answering is "is this one thing or five?", and for each candidate: *what must this feature NOT swallow?* A boundary nobody can state is not a boundary.
47
- - **Check for duplicates before you create.** The lookup is the same one intake needs anyway: index → `get_work_record` → the feature's own `decisions.md` on disk. "We already decided this" is a legitimate outcome.
60
+ ### 1b. Advise — recommend, ask, propose the split
48
61
 
49
- When it resolves: **one `create_feature` call per feature.**
62
+ Now talk. In this order, and all of it before any `create_feature`:
63
+
64
+ - **Report what you found, with addresses.** "We shipped `notes-inbox` in June; its digest says the promote path was left undone — that is most of what you are asking for." Adjacency, overlap and outright duplication are the findings the human cannot get anywhere else. **"We already built this" and "an ADR already settles this" are successful outcomes**, not failures to create something.
65
+ - **Recommend, don't interrogate.** Put your proposed cut to them and say why it is the cut. They correct or confirm. Never ask a bare question with no recommendation attached.
66
+ - **Ask only what changes your recommendation.** Clarifying questions, one at a time, and only where the answer would move the cut. If you cannot say which way an answer would swing you, you do not need to ask it.
67
+ - **Suggest how to split the work.** This is the advice they came for: is this one feature or three? What lands first, and what is it that later work needs from it? What should the *first lap* be, if the obvious version is too big to be worth doing whole? Say which order you would take and why.
68
+ - **Push on the cut, not the design.** For each candidate: *what must this feature NOT swallow?* A boundary nobody can state is not a boundary.
69
+
70
+ **This is not a grilling, and you must not run one.** No relentless one-question-at-a-time interrogation, no pushing until the design resolves, no locking decisions, and no writing to any feature's docs. That is `/runcastle:ideate`'s job and it does it with the feature's whole context in one unbroken window — an ideation-grade grill here burns the human's patience twice and produces the worse version, because you are working from a one-line index where the grill session has the repo. Stop at the cut. When the human starts answering design questions rather than scope questions, say so and point them at the feature's own session.
71
+
72
+ ### 1c. Create — once the split is agreed
73
+
74
+ When they have confirmed the cut: **one `create_feature` call per feature.** Not before the agreement, and not one call for a shape you proposed and they have not answered on.
50
75
 
51
76
  **Ask, per feature: start it now, or park it as a draft?** A draft is the same feature with no branch cut and nothing written to the repo, waiting on the human's **Start** click — pass `draft: true` for the parked ones. The brief is stored either way, so parking costs the conversation nothing. Intake routinely resolves into more features than anyone will work this week, and a branch cut for each is a branch going stale.
52
77
 
@@ -58,8 +83,8 @@ Each one carries a real `brief` — the reasoning you just worked out, in prose:
58
83
 
59
84
  Anything that arrives (from the human, or from a sweep in §6) goes to exactly one of:
60
85
 
61
- 1. **A new feature** — real design questions to grill. `create_feature` with a brief.
62
- 2. **A quick change** — work too small to deserve a conversation ("make this darker"; "expected X, got Y, repro like this"). `create_feature({ title, oneLiner, ticket: { prose } })` — one call, feature and its single ticket created together, born ready for the human's **Burn** click. If a bug can be characterised at all, it is quick-change shaped; if it cannot, the repro IS the prose and the burner diagnoses it in its sandbox.
86
+ 1. **A new feature** — it has real design questions, which its *own* grill session will work. `create_feature` with a brief.
87
+ 2. **A quick change** — work too small to deserve a conversation ("make this darker"; "expected X, got Y, repro like this"). `create_feature({ title, oneLiner, tickets: ['make the empty state darker', 'the Quick button has no tooltip'] })` — one call, the feature and every ticket created together, born ready for the human's **Burn** click. **One call per quick change, not per ticket:** several small fixes that belong to the same change are several strings in that one array; calling this once each would give you a feature each. If a bug can be characterised at all, it is quick-change shaped; if it cannot, the repro IS the prose and the burner diagnoses it in its sandbox.
63
88
  3. **An existing feature's revisit** — it belongs to a feature already in flight. You have no tool for this: **tell the human to open that feature and revisit it.**
64
89
  4. **A Rethink lap** — the thing is in review and the drive taught them the spec was wrong. Again no tool: tell them to click **Rethink** on that feature.
65
90
  5. **Nothing** — it is already decided, already built, or not worth doing. Say so plainly, with the ADR or the shipped feature that settles it.
@@ -70,7 +95,7 @@ Say which destination and why. Do not invent a sixth.
70
95
 
71
96
  "Have we already decided X?" "Did we ever build Y?" "Who has touched this area before?"
72
97
 
73
- Answer from `get_project_context` (charter + live ADRs bind you and bind everyone), `get_work_record` (facts: what a feature's tickets touched, what failed), and ordinary reads of `docs/features/<slug>/` on disk for the unabridged argument.
98
+ Answer from `get_project_context` + `read_adr` (the charter and the live ADRs bind you and bind everyone), `get_work_record` (facts: what a feature's tickets touched, what failed), and ordinary reads of `docs/features/<slug>/` on disk for the unabridged argument.
74
99
 
75
100
  Cite the address you read — `docs/adr/0007-….md`, `docs/features/laps/decisions.md#3`, a commit sha. An answer with no address is a guess with a confident tone. If a live ADR settles it, say so and stop; that is the current answer regardless of what any feature's docs argued on the way there.
76
101
 
@@ -78,8 +103,8 @@ Cite the address you read — `docs/adr/0007-….md`, `docs/features/laps/decisi
78
103
 
79
104
  You may notice, and you should say:
80
105
 
81
- - two in-flight features on a collision course;
82
- - an ADR that looks stale, or two that disagree;
106
+ - **two in-flight features on a collision course** — this is the one you can actually check rather than guess at. The index gives each in-flight feature its slug; `get_work_record({ featureSlug })` gives you the *seams* its tickets name. Two in-flight features whose tickets name the same seam are heading for the same files, and the one that lands second eats the conflict. Name both slugs and say which should land first.
107
+ - an ADR that looks stale, or two that disagree (`read_adr` to confirm before you say so);
83
108
  - a term used with two meanings across features;
84
109
  - docs that no longer describe the code.
85
110
 
@@ -87,47 +112,19 @@ You may notice, and you should say:
87
112
 
88
113
  The one exception is the charter, which is yours (§5) — and even there, a change that overturns an ADR is a decision, and decisions land as ADRs.
89
114
 
90
- ## 5. The charter (`CONTEXT.md`)
115
+ ## 5. The charter (`CONTEXT.md`) and project ADRs
91
116
 
92
117
  You are the **only** session in runcastle allowed to write `CONTEXT.md` and to author project-scope ADRs under `docs/adr/`. Feature sessions structurally cannot.
93
118
 
94
- **Born lazily.** Create it the first time there is genuinely something to write — never as a stub, never as a template with empty sections. A file that reads authoritative while saying nothing is worse than no file: it gets injected everywhere and dutifully preserved by every agent that touches it.
95
-
96
- **On an existing codebase with no charter**, the natural first move is an offer, not a task:
97
-
98
- > There's no `CONTEXT.md` here yet. Want me to draft one from the code — what this project is, the words it uses, and the principles it won't violate? You'd correct it before I commit anything.
99
-
100
- Take yes for an answer, take no for an answer, and move on either way.
101
-
102
- **Format** — three parts, in this order:
103
-
104
- ```markdown
105
- # <Project> — charter
106
-
107
- <Prose: what this project is, who it is for, and the design principles it will
108
- not violate. Written in the present tense. This file is REWRITTEN IN PLACE — it
109
- always describes the present, never the history.>
110
-
111
- ## Language
112
-
113
- **Term**: one-sentence definition. _Avoid_: the words people reach for instead, and why they are wrong.
114
- **Seam**: an observable boundary a test can be written at. _Avoid_: "interface" (means too many things), "layer".
115
-
116
- ## Deferred / open threads
117
-
118
- - One line per parked idea, with just enough context to pick it up later.
119
- - Delete the line when the thread is done — this section is pruned, not appended to forever.
120
- ```
121
-
122
- **`## Deferred / open threads` is the charter's home for a parked idea that is not yet a feature** — once it has resolved into one, park it as a draft (§1) instead. There is no backlog table and no `docs/backlog.md`, and you must not create one. Because the charter is rewritten in place, a thread that gets done is *deleted* — which is exactly what stops it decaying into a graveyard. Only put a line here if the idea is **not regenerable**: if re-reading the code would surface it again, it does not need remembering.
119
+ Two rules apply the moment the subject comes up, so they live here: **never scaffold an empty charter or an empty ADR**, and if there is no charter yet, *offer* to draft one rather than treating it as a task.
123
120
 
124
- Term collisions are a rewrite, not an append: if a feature's vocabulary conflicts with a term already defined here, raise it with the human and settle on one meaning — never silently redefine.
121
+ **When you are actually about to write or amend either one, read `./references/charter.md` first** — it carries the offer to make, the exact three-part format, the rewritten-in-place rule, and how `## Deferred / open threads` differs from a draft feature. Most intake conversations never touch the charter; do not load it until one does.
125
122
 
126
123
  ## 6. Health sweeps — supply-driven intake
127
124
 
128
- When the human asks for a sweep ("what needs doing?", "what's rotting?"), do the same job with the **codebase** supplying the raw material instead of them: dead code, missing tests, docs that drifted from the code, `spec.md` files the code has moved past, recurring burner failures (`get_work_record` errors, read by seam).
125
+ When the human asks for a sweep ("what needs doing?", "what's rotting?"), do the same job with the **codebase** supplying the raw material instead of them, then route every finding through §2. Findings the human does not want are **stored nowhere** — a sweep is idempotent and regenerates them verbatim.
129
126
 
130
- Then route every finding through §2. The ones they want now become features or quick changes on the spot. The ones they don't want **are stored nowhere** — a sweep is idempotent, the codebase still has the problem, and re-running it regenerates the finding verbatim. Storing a derivable list buys nothing and costs a graveyard.
127
+ **Read `./references/health-sweeps.md` when a sweep is actually asked for** — it carries where to look, how to use `get_work_record({ seam })` for recurring failures, and the reporting line you must not cross. Do not run a sweep unprompted.
131
128
 
132
129
  ## 7. Closing move — land what you wrote, leave the tree clean
133
130
 
@@ -143,6 +140,8 @@ A session that edits without committing leaves a dirty tree, and a dirty tree is
143
140
 
144
141
  ## Do NOT
145
142
 
143
+ - **Never create a feature before you have consulted the portfolio and agreed the cut.** §1's order — look, advise, create — is the session.
144
+ - **Never run an ideation grilling.** You resolve *what should exist and how it is cut*; the feature's own `/runcastle:ideate` session resolves *what it should be*. Design questions get handed on, not worked here.
146
145
  - **Never launch a session or terminal** for a feature you created. The card in the rail is the handoff.
147
146
  - **Never touch another feature's docs.** `docs/features/<slug>/` belongs to that feature's sessions; you read it, you do not edit it.
148
147
  - **Never advance a phase, emit tickets into an existing feature, or do ticket surgery.** Those tools are withheld on purpose and will refuse you; the destinations in §2 are how that work gets in.
@@ -0,0 +1,79 @@
1
+ # The charter (`CONTEXT.md`) — and project-scope ADRs
2
+
3
+ Load this when you are actually about to write, amend, or advise on `CONTEXT.md`
4
+ or an ADR under `docs/adr/`. `/runcastle:project` §5 carries the one-line rule;
5
+ this file carries the procedure and the format.
6
+
7
+ ## Who may write it
8
+
9
+ The **project session is the only session in runcastle allowed to write
10
+ `CONTEXT.md`** and to author project-scope ADRs under `docs/adr/`. Feature
11
+ sessions structurally cannot — they have no project-scoped write tool and their
12
+ edit guard confines them to `docs/features/<slug>/`.
13
+
14
+ ## Born lazily
15
+
16
+ Create `CONTEXT.md` the first time there is genuinely something to write — never
17
+ as a stub, never as a template with empty sections. A file that reads
18
+ authoritative while saying nothing is worse than no file: it gets injected
19
+ everywhere and dutifully preserved by every agent that touches it.
20
+
21
+ **On an existing codebase with no charter**, the natural first move is an offer,
22
+ not a task:
23
+
24
+ > There's no `CONTEXT.md` here yet. Want me to draft one from the code — what
25
+ > this project is, the words it uses, and the principles it won't violate? You'd
26
+ > correct it before I commit anything.
27
+
28
+ Take yes for an answer, take no for an answer, and move on either way.
29
+
30
+ ## Format — three parts, in this order
31
+
32
+ ```markdown
33
+ # <Project> — charter
34
+
35
+ <Prose: what this project is, who it is for, and the design principles it will
36
+ not violate. Written in the present tense. This file is REWRITTEN IN PLACE — it
37
+ always describes the present, never the history.>
38
+
39
+ ## Language
40
+
41
+ **Term**: one-sentence definition. _Avoid_: the words people reach for instead, and why they are wrong.
42
+ **Seam**: an observable boundary a test can be written at. _Avoid_: "interface" (means too many things), "layer".
43
+
44
+ ## Deferred / open threads
45
+
46
+ - One line per parked idea, with just enough context to pick it up later.
47
+ - Delete the line when the thread is done — this section is pruned, not appended to forever.
48
+ ```
49
+
50
+ ## Rules that keep it from rotting
51
+
52
+ - **`## Deferred / open threads` is the charter's home for a parked idea that is
53
+ not yet a feature** — once it has resolved into one, park it as a draft
54
+ (`create_feature({ draft: true })`, §1c) instead. There is no backlog table and
55
+ no `docs/backlog.md`, and you must not create one.
56
+ - Because the charter is **rewritten in place**, a thread that gets done is
57
+ *deleted* — which is exactly what stops it decaying into a graveyard.
58
+ - Only put a line here if the idea is **not regenerable**: if re-reading the code
59
+ would surface it again, it does not need remembering.
60
+ - **Term collisions are a rewrite, not an append.** If a feature's vocabulary
61
+ conflicts with a term already defined here, raise it with the human and settle
62
+ on one meaning — never silently redefine.
63
+
64
+ ## ADRs
65
+
66
+ The live ADR **index** comes back in `get_project_context`; the bodies do not.
67
+ Read the ones your work touches with `read_adr({ relPath })` — a live ADR binds
68
+ you and binds every feature session, so an answer that contradicts one is wrong
69
+ regardless of what a feature's `decisions.md` argued on the way there.
70
+
71
+ A charter change that **overturns** an ADR is a decision, and decisions land as
72
+ ADRs — write the ADR, do not quietly edit the charter around it. Never scaffold
73
+ an empty ADR.
74
+
75
+ ## Before you finish
76
+
77
+ Anything you wrote here is subject to the closing move (§7): commit it, with a
78
+ real message (`docs(project): …`), or delete it. Write-without-commit leaves a
79
+ dirty tree, and a dirty tree blocks the next test drive and jams the next merge.
@@ -0,0 +1,44 @@
1
+ # Health sweeps — supply-driven intake
2
+
3
+ Load this when the human actually asks for a sweep ("what needs doing?",
4
+ "what's rotting?", "what should I pick up next?"). Otherwise it is not your job:
5
+ this session does not go looking for work unprompted.
6
+
7
+ ## What a sweep is
8
+
9
+ The same intake job as `/runcastle:project` §1, with the **codebase** supplying
10
+ the raw material instead of the human. You are not auditing; you are producing a
11
+ short list of findings the human can route.
12
+
13
+ ## Where to look
14
+
15
+ - Dead code, and modules nothing imports.
16
+ - Missing tests around a seam that has failed before.
17
+ - Docs that drifted from the code — a `spec.md` the code has moved past is the
18
+ common one.
19
+ - `get_work_record({ seam })` for recurring burner failures: the same area
20
+ erroring across features is a design problem, not bad luck.
21
+ - `get_work_record({ featureSlug })` digests, read for their "left undone" line —
22
+ the cheapest source of real, already-scoped work in the whole project.
23
+ - Features in flight whose `phase`/`lap`/pending counts in the index say they
24
+ have stalled.
25
+
26
+ ## What to do with the findings
27
+
28
+ Route **every** finding through §2 — a new feature, a quick change, a revisit, a
29
+ Rethink, or nothing. Say which, and why.
30
+
31
+ The ones the human wants now become features or quick changes on the spot. The
32
+ ones they don't want **are stored nowhere.** A sweep is idempotent: the codebase
33
+ still has the problem, and re-running the sweep regenerates the finding verbatim.
34
+ Storing a derivable list buys nothing and costs a graveyard. There is no
35
+ `docs/backlog.md`, no findings file, and no "I'll note that for later" — the
36
+ charter's `## Deferred / open threads` is only for the non-regenerable, and a
37
+ sweep finding is regenerable by definition.
38
+
39
+ ## The line you must not cross
40
+
41
+ You **report**; you do not fix. Not the dead code, not the drifted doc, not the
42
+ stale ADR. The value of noticing is fully captured by saying so; acting on it is
43
+ how this session quietly becomes a project editor. A fix rides a feature, a quick
44
+ change, or promotion at merge — never this conversation.
@@ -1,30 +1,46 @@
1
1
  ---
2
2
  name: qa
3
3
  description: Answer questions about an existing (possibly shipped) feature from its docs and the codebase. Read-only — never advances phases, never emits tickets. Entry skill for kind=qa sessions.
4
- disable-model-invocation: false
4
+ disable-model-invocation: true
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's grilling + domain-modeling skills, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
7
7
 
8
8
  # Q&A
9
9
 
10
- This is a "come back and ask questions" session about a feature that already exists — often already shipped. Your job is to **answer**, accurately, from the record. Nothing here changes state.
10
+ This is a "come back and ask questions" session about a feature that already exists — often already shipped. Your job is to **answer**, accurately, from the record. Your output is what you **tell the human**, not what you store. Nothing here changes the feature's state.
11
11
 
12
- ## Do
12
+ ## Read-only, and enforced
13
13
 
14
- - Call `mcp__runcastle__get_feature_context` first: it returns the feature, its phase, and all docs under `docs/features/<slug>/` (brief, decisions, spec). That, plus the codebase, is your source of truth.
15
- - Answer from the docs and the actual code. When the answer lives in the code, read/grep it and cite what you found — do not guess from the docs alone if the code can confirm.
16
- - When something notable surfaces that the record should keep (a clarification, a discovered discrepancy between docs and code), you MAY log it: `mcp__runcastle__record_event({ type: "qa.note", message: "..." })`.
14
+ A `qa` session is read-only at the server, not by convention. `emit_tickets`, `update_ticket`, `cancel_ticket` and `complete_phase` are **refused** for this kind — the call comes back as an error, so there is no version of this session that advances a phase or files work. Do not attempt them and do not promise the human you will. The edit guard refuses code edits on the same terms.
17
15
 
18
- ## Do NOT
16
+ That is not a limitation to apologise for: it is the contract that makes this session cheap to open. When the answer implies work, the answer *is* "here is what should change, and here is the session that can make it" (see below).
19
17
 
20
- - **Never advance a phase.** No `complete_phase`. This session has no gates to cross.
21
- - **Never emit tickets.** No `emit_tickets`.
22
- - **Never implement changes** to the feature.
18
+ ## Your tools
19
+
20
+ - `mcp__runcastle__get_feature_context` — the feature, its `phase` and `lap`, its canonical docs inlined in `docs[]` (brief, map, decisions, spec), an index of everything else in `moreDocs[]` (with `relPath` and byte counts), and the tickets.
21
+ - `mcp__runcastle__read_feature_doc({ relPath })` — one of the `moreDocs[]` entries in full: test notes, research deliverables under `research/`, anything else the feature's sessions left behind.
22
+ - `mcp__runcastle__list_tickets({ status? })` — the ticket list on its own, when the question is about the queue and you do not need the whole feature.
23
+ - `mcp__runcastle__record_event({ type: "qa.note", message: "..." })` — a one-line note on the feature timeline. The only write you have, and the timeline is its only record, so use it when something surfaces that the record should keep: a clarification worth remembering, a discovered discrepancy between the docs and the code.
24
+ - Your ordinary `Read`/`Grep` over the repo in this worktree.
25
+
26
+ The injected system prompt carries the slug and the paths; trust `get_feature_context` for the live state.
27
+
28
+ ## How to answer
29
+
30
+ - **Start from the docs, confirm in the code.** When the answer lives in the code, read or grep it and cite what you found — do not guess from the docs alone if the code can settle it. Docs record what was *decided*; the code records what was *built*, and this session is often opened precisely because someone suspects they diverged.
31
+ - **Cite the address.** `docs/features/<slug>/decisions.md#3`, a file and line, a commit sha. An answer with no address is a guess with a confident tone.
32
+ - **Say when the record is silent.** "That was never decided" and "the docs say X but the code does Y" are good answers. Inventing a rationale that nobody actually held is the failure mode here.
23
33
 
24
34
  ## When the human wants a change
25
35
 
26
- A Q&A session is the wrong place to change the feature. If the conversation turns into "let's change X" or "this is a bug, fix it," name it and point them at the right move:
36
+ A Q&A session is the wrong place to change the feature. If the conversation turns into "let's change X" or "this is a bug, fix it," name it and point them at the move that can:
37
+
38
+ - a **revisit** on this feature, for late information that amends its record or its ticket queue;
39
+ - a **Rethink** from review, if the feature is in review and the drive taught them the spec was wrong;
40
+ - a **new feature**, if it is really its own capability — created from the project session or the UI.
41
+
42
+ Say which one and why, then `record_event` a one-line note capturing the request so it is not lost. You cannot open any of them, and you cannot create the feature yourself; the human's click is the handoff.
27
43
 
28
- > That is a change, not a question — it needs its own feature. Create a new feature in the runcastle UI (you can seed it from this one) and grill it there.
44
+ ## Branching the map
29
45
 
30
- Then, if useful, `record_event` a one-line note capturing the change request so it is not lost.
46
+ If the feature is **mapped** and the conversation surfaces a genuine open question that the map does not carry, you may chart it: `escalate_to_map`, `emit_waypoints` and `resolve_waypoint` stay open to this kind on purpose — any session may branch the map (SPEC §13.3). Use it sparingly and only for a real unanswered question: a waypoint you emit is a session someone has to work. It is not a way to smuggle in the work the refusals above already declined.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: revisit
3
3
  description: Fold late-arriving information into a feature whose sessions are finished — amend the docs, then reconcile tickets (update/cancel/emit); an ordinary revisit never advances phases. Also the lap session: on a Rethink from review, digest the test drive, amend decisions + spec, emit the lap's tickets and complete ideation → spec → tickets in the one session. Entry skill for kind=revisit sessions.
4
- disable-model-invocation: false
4
+ disable-model-invocation: true
5
5
  ---
6
6
 
7
7
  # Revisit
@@ -13,7 +13,7 @@ That is the ordinary revisit, and it is what the moves below describe. The one e
13
13
  ## Order of operations
14
14
 
15
15
  1. **Listen first.** The human opens with what changed. If this terminal resumed the previous conversation, use that context; do not re-grill what is already settled. Ask only the questions the NEW information raises.
16
- 2. **Context.** Call `mcp__runcastle__get_feature_context` — it returns the feature, phase, all docs, and all tickets with their ids and statuses. You need the ticket list before any surgery.
16
+ 2. **Context.** Call `mcp__runcastle__get_feature_context` — the feature, its `phase` and `lap`, the canonical docs inlined in `docs[]` (brief, map, decisions, spec), an index of everything else in `moreDocs[]` (test notes, `research/*.md`; fetch one with `mcp__runcastle__read_feature_doc({ relPath })`), and every ticket with its id and status. The injected system prompt carries the slug and paths; trust `get_feature_context` for the live state. When all you need is ticket ids to operate on, `mcp__runcastle__list_tickets({ status? })` is the cheap call — it returns the queue without the docs.
17
17
  3. **Docs.** Capture the change as decision prose:
18
18
  - Append to `docs/features/<slug>/decisions.md` under a dated `## Revisited <date>` heading — never rewrite old decisions, supersede them ("Supersedes: <old decision>").
19
19
  - If `spec.md` exists and the change touches it, amend the affected sections in place.
@@ -29,8 +29,8 @@ That is the ordinary revisit, and it is what the moves below describe. The one e
29
29
 
30
30
  Your kickoff line reads `LAP <n> REVIEW ITERATION`. The human burned the last lap, test-drove the branch, and clicked **Rethink**: the code was right, the *spec* wasn't. `feature.rethink` has already incremented the lap and looped the feature back, so it sits at **ideation**, not review. One trip round the pipeline is a lap, and this session is the whole front half of lap `<n>` — you carry it from what-the-drive-taught to ticket cards waiting on the human's **Burn** click, in this one conversation:
31
31
 
32
- 1. **Digest what the drive taught.** Your inputs are the previous lap's section of `docs/features/<slug>/test-notes.md` and the spec's `## Later laps` section, both injected into your context. **Both may be absent** — that is not an error and not something to go hunting for: interview the human from scratch instead. What did they hit, what surprised them, what do they want instead now they've used it?
33
- 2. **Context.** `mcp__runcastle__get_feature_context` — the feature (including its `lap`), the docs, and the full ticket history across every lap. Skim what the last lap actually landed and what failed before you interview.
32
+ 1. **Digest what the drive taught.** Your two inputs are the previous lap's section of `docs/features/<slug>/test-notes.md` — it is in `moreDocs[]`, so read it with `mcp__runcastle__read_feature_doc({ relPath: "test-notes.md" })` (or off disk) — and the `## Later laps` section of `spec.md`, which is inlined in `docs[]`. **Both may be absent** — that is not an error and not something to go hunting for: interview the human from scratch instead. What did they hit, what surprised them, what do they want instead now they've used it?
33
+ 2. **Context.** `mcp__runcastle__get_feature_context` — the feature (including its `lap`), the docs, and the full ticket history across every lap; each ticket's own `lap` is what separates them. Skim what the last lap actually landed and what failed before you interview.
34
34
  3. **Never re-emit a promoted note.** Notes the human already promoted from the review checklist are tickets in *this* lap and arrive as ids in your context. However well such a note reads as a ticket, if its id is in front of you the work is already carded — emitting it again gives the burner the same job twice.
35
35
  4. **Grill, briefly.** A small rethink is a short grilling: a few questions, one at a time, each with your recommended answer attached. Prune and promote `## Later laps` entries with the human as part of that conversation — what the drive taught is usually what decides which deferred scope this lap picks up and which stays parked.
36
36
  5. **Amend the docs.** Append this lap's learning to `docs/features/<slug>/decisions.md` under a `## Lap <n>` heading (the convention) — never rewrite old decisions, supersede them. Then amend `spec.md` in place: the sections this lap changes, plus the pruned `## Later laps`.
@@ -46,12 +46,12 @@ If the rethink turns out to be genuinely big — whole branches of design reopen
46
46
 
47
47
  - **Never call `complete_phase` in an ordinary revisit.** It has no gates to cross; the human drives the pipeline from the UI. **Lap mode is the one exception** — advancing ideation → spec → tickets is its job (move 7), and nothing else here licenses it.
48
48
  - **Never resolve or reopen waypoints.** If a resolved waypoint's answer is now wrong, record the superseding decision in `decisions.md` — the map's history stays intact.
49
- - **Never implement changes.** Code changes ride tickets; the burner (or a human) implements them.
49
+ - **Code changes ride tickets** — the burner (or a human) implements them, never this session. (The no-code rule itself is in your injected prompt and enforced by the edit guard.)
50
50
 
51
51
  ## Scope check
52
52
 
53
- If what the human brings is not an amendment but a whole new capability, say so:
53
+ If what the human brings is not an amendment but a whole new capability, say so — and park it rather than losing it:
54
54
 
55
- > That reads like a new feature, not a revision of this one. Create it in the runcastle UI and grill it there — I can record a pointer note here.
55
+ > That reads like a new feature, not a revision of this one. I'll park it as a draft so it isn't lost; you can start it from the runcastle UI when you want it, and it gets grilled there.
56
56
 
57
- Then `record_event` a one-line note so the idea is not lost.
57
+ `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` — a parked draft, no branch cut and nothing written, with the `brief` carrying why you deferred it and what it must not swallow. `draft: true` is the only door open to you; a full create belongs to the project session.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: spec
3
- description: Synthesize the ideation conversation and decisions.md into docs/features/<slug>/spec.md, then complete the spec phase. No interview — pure synthesis. Invoked by /runcastle:ideate.
3
+ description: Synthesize the ideation conversation and decisions.md into docs/features/<slug>/spec.md, then complete the spec phase. No interview — pure synthesis. Invoked by /runcastle:ideate or /runcastle:converge.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's to-spec skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
@@ -42,6 +42,6 @@ Turn the ideation conversation plus `docs/features/<slug>/decisions.md` into a s
42
42
 
43
43
  Do **not** hardcode specific file paths or code snippets — they go stale. Exception: if the ideation produced a snippet that pins a decision more precisely than prose can (a state machine, reducer, schema, type shape), inline the decision-rich parts and note it came from that discussion.
44
44
 
45
- 4. **Complete the phase.** `mcp__runcastle__record_event({ type: "spec.written", message: "<feature title>" })`, then `mcp__runcastle__complete_phase({ phase: "spec" })`. If the gate returns `ok: false`, it names what is missing — fix and retry.
45
+ 4. **Complete the phase.** `mcp__runcastle__complete_phase({ phase: "spec" })`. If the gate returns `ok: false`, it names what is missing — fix and retry. It logs its own timeline event and checkpoints `spec.md` into git; do not add an event of your own.
46
46
 
47
- Return control to `/runcastle:ideate`, which invokes `/runcastle:tickets` next.
47
+ Return control to **the session skill that invoked you** — `/runcastle:ideate` for a linear feature, `/runcastle:converge` for a mapped one. It invokes `/runcastle:tickets` next. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running (a converge session in particular is forbidden to grill) into the window.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: tickets
3
- description: Break the spec into vertical slices sized to earn a fresh sandboxed agent session and land inside it, and emit them via MCP. Then complete the tickets phase. Invoked by /runcastle:ideate.
3
+ description: Break the spec into vertical slices sized to earn a fresh sandboxed agent session and land inside it, and emit them via MCP. Then complete the tickets phase. Invoked by /runcastle:ideate or /runcastle:converge.
4
4
  disable-model-invocation: false
5
5
  ---
6
6
  <!-- Forked from Matt Pocock's to-tickets skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
@@ -40,7 +40,11 @@ Give each ticket its **blocking edges** — the tickets that must finish before
40
40
  <review-ticket>
41
41
  Close the batch with **one review ticket** (`kind: "review"`), blocked by every implementation ticket in it. It is not a slice: it is the prose brief for the agent that exercises the *integrated* feature at the tail of the burn, on the feature branch, and writes what it finds as test notes for the human's review phase. Its `goal` says what to verify and its `acceptanceCriteria` are the things to check off, one per behaviour worth confirming — the review agent walks them in order.
42
42
 
43
- Prescribe the verification that fits the feature, in plain prose: a browser walkthrough for anything with a UI ("open the settings flow, add a project, check the empty state"), or "run the suite and curl these endpoints" for a backend-only one. There is no mode to pick — the agent does what the ticket says. A feature with genuinely nothing to exercise gets no review ticket; everything else gets exactly one (more per feature comes later). The review agent never edits code, so its ticket needs no `seams` beyond the surface it exercises, and finding bugs is a *successful* review — the notes are the deliverable.
43
+ **Every batch closes with one, unconditionally.** Not "when there is something to exercise" — every batch, no exceptions, exactly one (more per feature comes later). The review agent **always runs a code review** of the branch's diff against its base, and a diff is the one thing every batch produces by definition, so there is no such thing as a lap with nothing to review. A lap that ships without a review ticket is a lap nobody looked at, and its absence is invisible on the review page — which is precisely the silence this rule exists to end.
44
+
45
+ What varies is the **drive**, not the review. Prescribe a walkthrough only when there is genuinely something to run, in plain prose: a browser walk for anything with a UI ("open the settings flow, add a project, check the empty state"), or "run the suite and curl these endpoints" for a backend-only one. When the lap changed nothing a human could drive — prompt contracts, docs, an internal refactor — say so in the ticket and let the code review stand alone. There is no mode to pick: the agent always reviews the code, and additionally does what the ticket says.
46
+
47
+ The review agent never edits code, so its ticket needs no `seams` beyond the surface it exercises, and finding bugs is a *successful* review — the notes are the deliverable. Its digest is the other one: the agent writes the lap's "What landed this lap" prose summary there, and the review page renders it as the lead of the review card, so `goal` should ask for the verification, not for the summary — that part is the burner's standing contract.
44
48
  </review-ticket>
45
49
 
46
50
  <wide-refactor-exception>
@@ -59,6 +63,17 @@ Each ticket:
59
63
  - **seams** — `string[]`, the public interfaces to test at (carry them from the spec's Seams section; prefer existing, prefer the highest, prefer one).
60
64
  - **blockedBy** — `number[]`, **required**, the seq numbers (from your 1..N ordering) of the tickets that *genuinely* gate this one. Only true gates; pass `[]` when there are none — omitting it fails validation.
61
65
  - **kind** — `"implementation"` (the default, omit it) or `"review"` for the one review ticket described above.
66
+ - **model** — optional, and usually omitted. See below.
67
+
68
+ ### Assigning a model (only from the annotated roster)
69
+
70
+ `mcp__runcastle__get_feature_context` returns `annotatedModels`: `{ id, runtime, note }` for the models the human wrote a use-case note against, and only those. That list is the *entire* set of ids you may put in a ticket's `model`.
71
+
72
+ - **Empty list → never set `model`.** The human annotated nothing, so there is no stated reason to prefer one model over another. Omit the field on every ticket.
73
+ - **Non-empty → set it only where a note actually fits the ticket.** The note is the human's own vocabulary for what that model is good at ("UI/UX taste", "mechanical refactors"); assign it when this ticket's work is plainly that, and leave the field unset when nothing in the list speaks to it. Unset is the good default — it falls through to the model the project already burns on.
74
+ - **Never invent an id**, never copy one from these docs or from memory, and never write a model that is not in `annotatedModels` — the store rejects it and the whole batch fails to emit.
75
+
76
+ The human sees the assignment on the ticket card and can change or clear it before Burn, so a wrong-but-reasonable choice is cheap; a fabricated id is not.
62
77
 
63
78
  ## 3. Self-check, then emit
64
79
 
@@ -69,9 +84,10 @@ There is no in-session quiz — the human's review is the **Burn** gate in the r
69
84
  3. Is each survivor a demoable vertical slice rather than a horizontal layer, and is it worth a session rather than rattling around in one?
70
85
  4. Are the blocking edges minimal and genuinely true gates? Where two are independent, do they touch different files — or will they collide on landing?
71
86
  5. Is every `context` self-sufficient for an agent that cannot ask — covering every file and pattern the ticket touches?
87
+ 6. Does the batch close with exactly one `kind: "review"` ticket, blocked by every implementation ticket? It is not optional and there is no feature that skips it.
72
88
 
73
89
  Then:
74
90
  - `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do NOT write ticket files.** It returns `{ stored, ids }` and logs the timeline event itself; do not record one of your own.
75
91
  - `mcp__runcastle__complete_phase({ phase: "tickets" })`. If the gate returns `ok: false`, fix what it names and retry.
76
92
 
77
- Return to `/runcastle:ideate` to close out the session.
93
+ Return control to **the session skill that invoked you** — `/runcastle:ideate` for a linear feature, `/runcastle:converge` for a mapped one — and let it close out the session. Do **not** invoke a session skill yourself to hand back: you are already inside one, and loading another session's entry skill would drop a whole procedure this session is not running (a converge session in particular is forbidden to grill) into the window.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: waypoint
3
3
  description: The runcastle waypoint session. Work ONE waypoint on a feature's map — grill, prototype, or run a task checklist for its assigned question — write the decision prose to the docs, then resolve the waypoint. Entry skill for kind=waypoint sessions.
4
- disable-model-invocation: false
4
+ disable-model-invocation: true
5
5
  ---
6
6
  <!-- runcastle mapped-ideation waypoint session (ADR-0001 §13.5) -->
7
7
 
@@ -11,20 +11,25 @@ This is a **mapped-ideation** feature: too big for one context window, so it was
11
11
 
12
12
  ## 0. Orient
13
13
 
14
- 1. Call `mcp__runcastle__get_feature_context`. For a mapped feature it returns, besides the usual feature/phase/docs, the full `map` state: `waypoints`, the `frontier`, and — the one that matters here — `assignedWaypoint`: **your** waypoint (the one this session claimed). Read its `title`, `type`, and `question`. The injected system prompt carries the same question; trust `get_feature_context` for the live state.
15
- 2. Read `docs/features/<slug>/map.md` (Destination, Notes, Not-yet-specified, Out-of-scope) so your answer fits the north star, and skim `decisions.md` for what is already locked — **do not re-litigate settled decisions.**
16
- 3. `mcp__runcastle__record_event({ type: "waypoint.started", message: "<waypoint title>" })`.
14
+ 1. Call `mcp__runcastle__get_feature_context`. The injected system prompt carries the slug, the paths and your question; trust `get_feature_context` for the live state. For a mapped feature it returns, besides the usual feature/phase/docs:
15
+ - `waypoints` — every waypoint on the map, with its `title`, `type`, `question`, status and (once terminal) `summary`.
16
+ - `frontierIds` — the ids currently open, unclaimed and unblocked.
17
+ - `assignedWaypointId` — **your** waypoint's id, the one this session claimed. Find that id in `waypoints` and read its `title`, `type` and `question`. That is your whole assignment.
18
+ 2. `docs[]` inlines the canonical docs in full — `brief.md`, `map.md`, `decisions.md`, `spec.md` if it exists. Read `map.md` (Destination, Notes, Not-yet-specified, Out-of-scope) so your answer fits the north star, and `decisions.md` for what is already locked — **do not re-litigate settled decisions.**
19
+ 3. **Read the research already done for this feature.** Earlier `research` waypoints wrote deliverables to `docs/features/<slug>/research/<seq>-<slug>.md`. They are *not* inlined — they appear in the `moreDocs` index with their `relPath` and `bytes`, and each resolved research waypoint's `summary` in `waypoints` names its file (`researched: <title> — see <path>`). Fetch the ones your question depends on with `mcp__runcastle__read_feature_doc({ relPath })`, or read them off disk in this worktree. A waypoint that re-derives a finding an earlier waypoint already paid an AFK run for is a wasted session.
17
20
 
18
21
  ## 1. Work it — mode by type
19
22
 
20
- Your `assignedWaypoint.type` picks the mode:
23
+ Your waypoint's `type` picks the mode:
21
24
 
22
- - **`grilling`** → grill the human on the question exactly as `/runcastle:ideate` does: one question at a time, always recommend an answer, look up facts in the code, model the domain, don't move on until you share understanding. Scope the grill to THIS waypoint's question — resist wandering into the whole feature.
23
- - **`prototype`** → build the smallest throwaway spike that answers the question (a fork to compare approaches, a spike to prove feasibility). The prototype exists to produce a decision, not to ship — capture what it proved, then let it go.
25
+ - **`grilling`** → grill the human on the question exactly as `/runcastle:ideate` does (`/runcastle:ideate` §1 is the procedure — one question at a time, always recommend an answer, look up facts in the code, model the domain, don't move on until you share understanding). Scope the grill to THIS waypoint's question — resist wandering into the whole feature.
26
+ - **`prototype`** → build the smallest throwaway spike that answers the question (a fork to compare two approaches, a spike to prove feasibility). **Write it under `docs/features/<slug>/prototypes/` and nowhere else.** That directory is the only place a talk session may put code: everything outside `docs/features/<slug>/` is denied by the edit guard, and the deny is real, not advisory. The spike is *evidence for the decision prose*, not shipped code — nothing under `prototypes/` is ever built on, and the working version rides a ticket like any other code. So do not try to write into `src/`, do not conclude the prototype is impossible and emit a ticket instead, and do not ask the human to run it for you: spike it in `prototypes/`, read what it proves, write the decision (§2), and let the spike go.
24
27
  - **`task`** → run the concrete checklist the question describes (gather the facts, enumerate the cases, fill the table). Produce the artifact the waypoint asked for.
25
28
 
26
29
  If, while working, you discover the question really splits into new sub-questions or uncovers whole new branches, chart them: `mcp__runcastle__emit_waypoints({ waypoints: [...] })` — each with `title`, `type`, `question`, `blockedBy` (1-based positions in this batch and/or existing waypoint ids), and `originWaypointId: "<your waypoint id>"` so the lineage ("surfaced by …") is recorded. Any mapped session may branch the map; that is the recursion. Emitting a waypoint does **not** relieve you of answering your own.
27
30
 
31
+ If something surfaces that is plainly its own **feature** rather than another waypoint, park it instead of swallowing it: `mcp__runcastle__create_feature({ title, oneLiner, brief, draft: true })` creates a parked draft — no branch, nothing written — with the `brief` carrying why you deferred it. Drafts are the only feature you may create here; a full create belongs to the project session.
32
+
28
33
  ## 2. Write the decision prose — directly, to the docs
29
34
 
30
35
  The answer is **prose in the docs**, not a tool argument. Serial HITL makes these writes race-free, so edit the files directly in this talk worktree:
@@ -39,13 +44,13 @@ The answer is **prose in the docs**, not a tool argument. Serial HITL makes thes
39
44
 
40
45
  - **Dropping** (no longer worth answering): record a one-line gist under `## Out of scope` in `docs/features/<slug>/map.md`, so the map remembers why it was cut.
41
46
 
42
- Write the prose **before** you resolve — the resolve tool flips machinery only and does not carry your answer.
47
+ Write the prose **before** you resolve — the resolve tool flips machinery only and does not carry your answer. If a prototype produced the answer, the decision entry must stand on its own: the reader of `decisions.md` is a converge session that will never open your spike.
43
48
 
44
49
  ## 3. Resolve — end the session
45
50
 
46
51
  `mcp__runcastle__resolve_waypoint({ id: "<your waypoint id>", disposition: "resolved" | "dropped", summary: "<one-line gist>" })`.
47
52
 
48
- This marks your waypoint terminal and frees any waypoint that was blocked only on yours — they appear on the frontier for the next session to Work. The `summary` is the one line shown on the map card.
53
+ This marks your waypoint terminal and frees any waypoint that was blocked only on yours — they appear on the frontier for the next session to Work. The `summary` is the one line shown on the map card, and it is what a later session sees first; if your work left a file behind (a prototype, a table), name its path there.
49
54
 
50
55
  Then tell the human plainly:
51
56