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.
- package/bin/runcastle.js +8467 -6620
- package/drizzle/0026_polite_vertigo.sql +2 -0
- package/drizzle/0027_dashing_texas_twister.sql +1 -0
- package/drizzle/0028_tired_deadpool.sql +2 -0
- package/drizzle/0029_damp_stellaris.sql +1 -0
- package/drizzle/meta/0026_snapshot.json +921 -0
- package/drizzle/meta/0027_snapshot.json +928 -0
- package/drizzle/meta/0028_snapshot.json +942 -0
- package/drizzle/meta/0029_snapshot.json +949 -0
- package/drizzle/meta/_journal.json +29 -1
- package/index.js +8424 -6588
- package/package.json +1 -1
- package/sandcastle-template/Containerfile +9 -1
- package/sandcastle-template/Dockerfile +9 -1
- package/skills/burner/implement-ticket.md +32 -47
- package/skills/burner/resolve-conflict.md +3 -3
- package/skills/burner/review-ticket.md +118 -37
- package/skills/packs/README.md +99 -7
- package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
- package/skills/packs/runcastle/skills/code-review/SKILL.md +131 -0
- package/skills/packs/runcastle/skills/converge/SKILL.md +26 -21
- package/skills/packs/runcastle/skills/ideate/SKILL.md +14 -8
- package/skills/packs/runcastle/skills/prepare/SKILL.md +96 -0
- package/skills/packs/runcastle/skills/prepare/references/recipes.md +72 -0
- package/skills/packs/runcastle/skills/project/SKILL.md +54 -55
- package/skills/packs/runcastle/skills/project/references/charter.md +79 -0
- package/skills/packs/runcastle/skills/project/references/health-sweeps.md +44 -0
- package/skills/packs/runcastle/skills/qa/SKILL.md +29 -13
- package/skills/packs/runcastle/skills/revisit/SKILL.md +8 -8
- package/skills/packs/runcastle/skills/spec/SKILL.md +3 -3
- package/skills/packs/runcastle/skills/tickets/SKILL.md +19 -3
- package/skills/packs/runcastle/skills/waypoint/SKILL.md +14 -9
- package/web/assets/index-BRjcVBgj.js +142 -0
- package/web/assets/index-CNSn8X1O.css +1 -0
- package/web/index.html +2 -2
- package/web/assets/index-bof4-dhY.css +0 -1
- 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,
|
|
4
|
-
disable-model-invocation:
|
|
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**.
|
|
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
|
-
|
|
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
|
|
21
|
-
- `
|
|
22
|
-
- `
|
|
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
|
-
|
|
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
|
-
|
|
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 —
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
##
|
|
12
|
+
## Read-only, and enforced
|
|
13
13
|
|
|
14
|
-
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
-
|
|
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
|
|
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
|
-
|
|
44
|
+
## Branching the map
|
|
29
45
|
|
|
30
|
-
|
|
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:
|
|
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` —
|
|
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
|
|
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
|
-
- **
|
|
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.
|
|
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
|
-
|
|
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.** `
|
|
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
|
|
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
|
-
|
|
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`
|
|
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:
|
|
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`.
|
|
15
|
-
|
|
16
|
-
|
|
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 `
|
|
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
|
|
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
|
|