runcastle 1.5.3 → 1.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/bin/runcastle.js +1716 -2625
  2. package/drizzle/0052_friendly_black_tarantula.sql +1 -0
  3. package/drizzle/0053_soft_karma.sql +12 -0
  4. package/drizzle/meta/0052_snapshot.json +1364 -0
  5. package/drizzle/meta/0053_snapshot.json +1283 -0
  6. package/drizzle/meta/_journal.json +14 -0
  7. package/index.js +1539 -2448
  8. package/package.json +1 -1
  9. package/skills/packs/README.md +24 -29
  10. package/skills/packs/runcastle/.claude-plugin/plugin.json +1 -1
  11. package/skills/packs/runcastle/skills/feature/SKILL.md +72 -0
  12. package/skills/packs/runcastle/skills/feature/references/design.md +132 -0
  13. package/skills/packs/runcastle/skills/feature/references/grilling.md +99 -0
  14. package/skills/packs/runcastle/skills/feature/references/planning-alone.md +70 -0
  15. package/skills/packs/runcastle/skills/{ideate → feature}/references/preview-starter.html +11 -16
  16. package/skills/packs/runcastle/skills/{ideate → feature}/references/preview.md +33 -8
  17. package/skills/packs/runcastle/skills/feature/references/rounds.md +91 -0
  18. package/skills/packs/runcastle/skills/feature/references/spec.md +54 -0
  19. package/skills/packs/runcastle/skills/feature/references/tickets.md +229 -0
  20. package/skills/packs/runcastle/skills/project/SKILL.md +18 -16
  21. package/skills/packs/runcastle/skills/project/references/health-sweeps.md +5 -5
  22. package/skills/packs/runcastle/skills/project/references/triage.md +20 -23
  23. package/skills/packs/runcastle/skills/qa/SKILL.md +1 -2
  24. package/web/assets/index-CT9k1kz0.css +1 -0
  25. package/web/assets/index-DVR7VbWW.js +186 -0
  26. package/web/index.html +2 -2
  27. package/skills/packs/runcastle/skills/design/SKILL.md +0 -73
  28. package/skills/packs/runcastle/skills/ideate/SKILL.md +0 -131
  29. package/skills/packs/runcastle/skills/lead/SKILL.md +0 -104
  30. package/skills/packs/runcastle/skills/revisit/SKILL.md +0 -55
  31. package/skills/packs/runcastle/skills/spec/SKILL.md +0 -47
  32. package/skills/packs/runcastle/skills/tickets/SKILL.md +0 -147
  33. package/web/assets/index-BYSs5Hdy.js +0 -186
  34. package/web/assets/index-bpL_iO_C.css +0 -1
@@ -1,12 +1,36 @@
1
1
  # The plan preview — the page the human looks at
2
2
 
3
- Load this when the decisions are locked and the preview step (§3 of the ideate
4
- skill) arrives, or when a decision edit at planning means the preview has to be
5
- redrawn. Otherwise it costs this conversation nothing.
3
+ Load this when the decisions are settled and you are about to show the human
4
+ the plan, or when a decision edit means the preview has to be redrawn.
5
+ Otherwise it costs this conversation nothing.
6
6
 
7
7
  The preview is the one page the human reads the feature by. They confirm the
8
8
  plan by looking at it, and when tired it is the only thing they read before
9
- Burn. Draw it for that reader.
9
+ they say yes to the first round. Draw it for that reader.
10
+
11
+ ## Show it, then get the yes
12
+
13
+ 1. **Draw it** from `./preview-starter.html`, following the rules below.
14
+ 2. **Check it against `decisions.md`** (the self-check at the end). Fix the
15
+ preview before anyone sees it.
16
+ 3. **Show it**: `show_panel({ view: 'plan' })` opens it beside the chat. Tell
17
+ the human what you propose — tickets and rounds, or work you will do
18
+ yourself — and ask for their explicit yes. Then wait for the answer.
19
+ 4. **A correction is a decision edit plus a redraw.** Edit the decision in
20
+ `decisions.md` and redraw the preview in the same move, then ask them to
21
+ look again.
22
+
23
+ With no human present, steps 1 and 2 are the whole of this; `./planning-alone.md`
24
+ says how that run asks for the yes.
25
+
26
+ **Never skip the preview on your own judgement** — not because the feature is
27
+ small, not because the grill went quickly. A small feature gets a small
28
+ preview. **If the human says to skip it**, write no file. The yes is still
29
+ needed.
30
+
31
+ **The redraw rule.** Any later edit to `decisions.md` updates `preview.html` in
32
+ the same move, before anything else, while the plan is still being agreed or
33
+ built.
10
34
 
11
35
  ## The file
12
36
 
@@ -30,13 +54,14 @@ Burn. Draw it for that reader.
30
54
  title, with the reason available on demand (the starter's `D` list builds
31
55
  this). Where a decision is visible in the show, pin its number on that spot:
32
56
  `<a class="pin" data-d="3">3</a>`.
33
- **A self-planned feature** (§1a of the ideate skill) opens this part with a
57
+ **A self-planned feature** (`./planning-alone.md`) opens this part with a
34
58
  **Check first** group of at most five decisions, the ones the planner picked.
35
59
  Each entry shows the decision's number and title, its alternative, and why it
36
60
  is uncertain (the starter's `C` list builds this). The remaining decisions
37
61
  follow in the list, each showing that it was assumed (a third element on its
38
62
  `D` entry). **A grilled feature has neither**: no group and no assumed marks.
39
- 4. **What is left out.** Out of scope, and anything deferred to a later lap.
63
+ 4. **What is left out.** Out of scope, and under **Later** anything deferred
64
+ to a backlog feature.
40
65
  5. **Under the hood.** The technical shape for a reader who wants it,
41
66
  **collapsed by default**, drawn where it can be.
42
67
 
@@ -65,13 +90,13 @@ Each screen in the show is a **screen block**: the lines between two markers,
65
90
  ```
66
91
 
67
92
  with `<name>` in kebab case and unique in the file. A ticket that builds that
68
- screen names the block, and its burn agent reads only those lines — so put
93
+ screen names the block, and its worker reads only those lines — so put
69
94
  everything the screen needs to be understood between the markers, and nothing
70
95
  else. Delete the starter's `example-name` block if the show has no screen.
71
96
 
72
97
  ## Designed screens
73
98
 
74
- A screen designed in design mode (`../../design/SKILL.md`) is **not mocked
99
+ A screen designed in design mode (`./design.md`) is **not mocked
75
100
  again**. Its place in the show is a **designed-screen entry**:
76
101
 
77
102
  - its name;
@@ -0,0 +1,91 @@
1
+ # Running rounds
2
+
3
+ Load this when you are about to dispatch a round, or you have just been woken by
4
+ one coming back.
5
+
6
+ A round is how workers build: each gets one ticket, a clean sandbox and the full
7
+ brief, and works on its own branch that lands on the feature branch. You
8
+ dispatch it, you are woken when it comes back, you check what it did, and you
9
+ plan the next move.
10
+
11
+ Start the first round only after the human's explicit yes in chat, and ask again
12
+ before any round the agreed plan did not settle (SKILL.md, "The human's yes").
13
+
14
+ ## What you run on
15
+
16
+ `get_feature_context` carries:
17
+
18
+ - `rounds` — every round so far, numbered from 1: the tickets it held, how it ended, its gates.
19
+ - `latestReady` — your newest ready declaration, if any.
20
+ - `driveActive` — the human is test-driving this feature right now.
21
+ - `stopped` — the human pressed Stop and has not replied since.
22
+ - `waitingReason` — why the feature is waiting on the human, if it is.
23
+ - `burnConcurrency` — how many workers the project runs at once.
24
+
25
+ ## Plan a round
26
+
27
+ 1. **Write the tickets** with `emit_tickets`, or reshape pending ones with
28
+ `update_ticket` / `cancel_ticket`, following `./tickets.md`. Then **dispatch**
29
+ with `dispatch_round({ seqs })`, naming exactly the pending tickets this round
30
+ covers. Tickets you do not name wait for a later round. A ticket may stay
31
+ blocked by tickets that landed or were cancelled in an earlier round: those
32
+ count as satisfied. A blocker that is still pending or failed must be named
33
+ in the same round.
34
+ 2. **Every worker task names its deliverable and how it is checked.** A worker
35
+ with no stated check reports success on its own terms. Say what is built and
36
+ what proves it: the test that goes green, the command that passes, the screen
37
+ that shows it.
38
+ 3. **Keep the round to `burnConcurrency` workers or fewer.** A soft cap: workers
39
+ past it only queue, and a wider round has more to reconcile when it returns.
40
+ 4. **Contract first when the work spans layers.** When workers on different
41
+ layers must agree on a shape — types, an API, a schema — make one ticket land
42
+ that contract in code and make the others `blockedBy` it in the same round.
43
+ **Contracts live in code, never in a doc**: workers trust the code.
44
+ 5. **UI work names the design references.** A ticket that touches UI tells its
45
+ worker which design docs to follow — the project's own design system docs and
46
+ any design references in this feature's docs — and asks it to plan the
47
+ screen, build it, then critique it against those docs with screenshots.
48
+
49
+ Workers do not talk to each other or to you. They report through their digests,
50
+ their ticket status and their commits. A worker that finds the contract wrong
51
+ stops and says so in its digest; reconciling it is your job, between rounds.
52
+
53
+ ## When a round comes back
54
+
55
+ You are woken with the round's outcome, each ticket's status, the verify-gate
56
+ result, any review findings and where the digests are.
57
+
58
+ 1. **Trust the record over the prose.** Ticket status, the gates and the commits
59
+ are what happened; a digest is a worker's account of it. Read each digest
60
+ with `get_ticket({ seq })`, and where the two disagree, believe the record.
61
+ 2. **Reconcile.** Did a worker report the contract wrong, or change something
62
+ another relied on? Did a decision turn out wrong? Fix the contract in code,
63
+ amend `decisions.md` and `spec.md` when what was decided changed, and update
64
+ the pending tickets. A contract reported wrong is a reason to re-plan.
65
+ 3. **Handle review findings.** A finding stays open until you resolve it: fix it
66
+ in a round or yourself, or dismiss it and say why.
67
+ 4. **Decide the next move**:
68
+ - small leftovers → fix them yourself;
69
+ - more work, or a failed ticket to retry → plan the next round;
70
+ - a question only the human can answer → ask it in chat and call `wait_for_you({ why })`;
71
+ - the feature does what the plan says → run the checks and declare ready (SKILL.md, "Ready for the human").
72
+
73
+ A failed round is just what came back. Re-plan, retry or fix it yourself.
74
+ `declare_ready` warns when the last round had failures; pass that on.
75
+
76
+ ## While and after the human drives
77
+
78
+ Never dispatch while the feature is being driven (`driveActive`). Your own
79
+ commits wait until the drive stops. When the human reports back, follow
80
+ SKILL.md, "After a drive"; when you are satisfied again, declare ready with a
81
+ new scenario.
82
+
83
+ ## What the server refuses
84
+
85
+ `dispatch_round` is refused, with the reason, for a draft or shipped feature,
86
+ while a round is already running, while the human is driving this feature, and
87
+ after the human pressed Stop until they have replied. It is also refused when a
88
+ ticket you name is blocked by a ticket outside the round that is still pending
89
+ or failed (or does not exist): the refusal lists each such edge, and the fix is
90
+ to name that blocker in the round too. `stop_round` stops the running round;
91
+ work that already landed stays.
@@ -0,0 +1,54 @@
1
+ # Writing the spec
2
+
3
+ Load this once what the feature does is settled — whether you will build it
4
+ yourself or hand it to workers — or when a later decision changes what
5
+ `spec.md` says.
6
+
7
+ The spec is synthesis, not an interview: write it from the conversation and
8
+ `docs/features/<slug>/decisions.md`. Do not re-ask the human anything that is
9
+ settled.
10
+
11
+ ## Process
12
+
13
+ 1. **Load state.** `mcp__runcastle__get_feature_context` for the decisions and
14
+ any existing `spec.md` (this may be a rewrite). Read any canonical doc listed
15
+ in `notInlined` before writing a word. Explore the codebase for its current
16
+ state if you have not already; use the project's domain vocabulary and
17
+ respect any ADRs in the area you touch.
18
+
19
+ 2. **Sketch the seams** — the public boundaries the feature will be tested at.
20
+ Prefer *existing* seams to new ones; use the *highest* seam possible; the
21
+ fewer across the codebase, the better — the ideal is one. If new seams are
22
+ needed, propose them at the highest point you can. They flow straight into
23
+ the tickets and the tests, so get them right here.
24
+
25
+ 3. **Write `docs/features/<slug>/spec.md`** with exactly these sections:
26
+
27
+ ```markdown
28
+ # <Feature title>
29
+
30
+ ## Problem
31
+ The problem the user faces, from the user's perspective.
32
+
33
+ ## Approach
34
+ The solution from the user's perspective, then the shape of it: modules built/modified, their interfaces, schema/API/contract decisions, key interactions. Prose, not a file-by-file plan.
35
+
36
+ ## Seams
37
+ The interfaces to test at (from step 2) — each named, with what it lets you observe. Flag which are existing vs. new.
38
+
39
+ ## Out of scope
40
+ What this feature explicitly does not cover, including scope parked as backlog features.
41
+
42
+ ## Open questions
43
+ Anything still unresolved or deliberately deferred.
44
+ ```
45
+
46
+ Do **not** hardcode file paths or code snippets — they go stale. Exception:
47
+ when the conversation produced a snippet that pins a decision more precisely
48
+ than prose can (a state machine, a reducer, a schema, a type shape), inline
49
+ the decision-rich parts and say where it came from.
50
+
51
+ 4. **Commit it** with the decisions it was written from, as a docs commit.
52
+
53
+ When a later decision changes what the feature does, amend the affected
54
+ sections in place rather than appending.
@@ -0,0 +1,229 @@
1
+ <!-- Forked from Matt Pocock's to-tickets skill, via https://github.com/mattpocock/skills, 2026-07-14, adapted for runcastle -->
2
+
3
+ # Writing tickets
4
+
5
+ Load this when work is worth handing to sandboxed workers and you are about to
6
+ write, reshape or cancel tickets.
7
+
8
+ Break the work into **tickets** — vertical slices, each cutting end-to-end
9
+ through every layer it needs, each big enough to be worth a whole agent session
10
+ and small enough to finish inside one — and emit them with
11
+ `mcp__runcastle__emit_tickets`. Synthesize from `spec.md` and the conversation.
12
+ The feature's existing tickets come as summary rows in `get_feature_context`;
13
+ `mcp__runcastle__get_ticket({ seq })` holds one ticket's context, acceptance
14
+ criteria and digest.
15
+
16
+ ## The one thing that makes a runcastle ticket different
17
+
18
+ Each ticket is executed by a **single fresh agent, alone, in a sandbox, with
19
+ ~100k tokens and NO way to ask a follow-up question.** It sees only what you put
20
+ in the ticket plus the code already in the repo. So — *inverting* the usual
21
+ "avoid file paths, they go stale" rule — the ticket's `context` field **must be
22
+ concrete and self-sufficient**: name the files to touch, the existing patterns
23
+ to copy, the type/schema shapes, the gotchas, the commands to run and verify
24
+ with. A ticket that assumes the agent can ask "where does X live?" is a broken
25
+ ticket. Over-specify context; under-specify nothing.
26
+
27
+ ## The second thing: a ticket costs ~10 minutes before it does any work — and pays it twice if it overruns
28
+
29
+ That fresh agent is fresh *every time*. Each ticket gets its own container: cold
30
+ build, a full dependency install (measured 70–507s, ~2.5 min average), then an
31
+ agent that re-reads the spec, the docs digest, and every file your `context`
32
+ names — reading the repo was 16% of all agent time across 42 measured attempts.
33
+ **This overhead is paid per ticket and is the same whether the ticket is twenty
34
+ lines or a day's work.** Splitting one coherent piece of work into three tickets
35
+ triples it, and adds two seams where a new agent has to reconstruct what the
36
+ previous one had just finished learning.
37
+
38
+ But that meter runs both ways. A ticket too big to finish in one pass gets no
39
+ cheap second wind: an iteration is not a retry (ADR-0008) — the container is
40
+ rebuilt, the install runs again, and a brand-new agent re-reads everything from
41
+ the last commit, having lost every conclusion its predecessor reached.
42
+
43
+ So size a ticket to **earn its container and still land in one pass**:
44
+ substantial, with room left at the end for the tests, the self-review and the
45
+ commits. Fine-grained slicing belongs *inside* a ticket, as the
46
+ `acceptanceCriteria` list: the worker goes red→green one criterion at a time and
47
+ commits each green slice, so one ticket with six criteria orients once; six
48
+ one-criterion tickets orient six times.
49
+
50
+ Expect a **handful of substantial tickets** for a feature — roughly 4–8, each
51
+ worth a session. A dozen thin ones means you sliced by layer or by file, not by
52
+ behaviour. Two or three giant ones means you will pay back the containers you
53
+ saved, with interest, when they overrun.
54
+
55
+ ## 1. Draft vertical slices
56
+
57
+ <vertical-slice-rules>
58
+ - Each slice cuts a narrow but COMPLETE path through every layer it needs (schema, API, UI, tests) — vertical, NOT a horizontal slice of one layer.
59
+ - A completed slice is demoable or verifiable on its own.
60
+ - Each slice is worth one fresh ~100k-token agent session working alone and **lands inside it** — leave room for the orientation at the front and the tests, self-review and commits at the back.
61
+ - **Merge when a merge test holds.** Two drafts are one ticket if they touch the same files, share a type or module, or the second one's first act would be re-reading what the first just wrote. Same for the obvious siblings of one behaviour: create/edit/delete of a resource, an endpoint and the UI that calls it, code and the tests that cover it.
62
+ - **Split only for a real reason:** (a) a genuine gate — the later work cannot be written until the earlier work's interface exists; (b) the combined work would not land in one session; (c) a prefactor, which is its own slice done first ("make the change easy, then make the easy change"); (d) parallel width, under the rule below; (e) the wide-refactor sequence below.
63
+ - **Not reasons to split:** one ticket per file, layer, endpoint or component; keeping each ticket easy; keeping diffs small; separating tests from the code they test.
64
+ - **Parallelism pays when the halves are file-disjoint.** Tickets with no blocking edge burn concurrently, each on its own temp branch, landing one at a time (ADR-0002) — so two substantial slices touching *different* files buy real wall-clock. Two touching the *same* files race to land, and the loser fails on a merge conflict: that width costs a re-burn. Overlapping files → merge.
65
+ </vertical-slice-rules>
66
+
67
+ Give each ticket its **blocking edges** — the tickets that must finish before it
68
+ can start. The field is required: a ticket with no blockers gets an empty array.
69
+
70
+ <review-ticket>
71
+ **A review ticket, when you choose to run one.** Whether a batch gets a review
72
+ is your call (SKILL.md, "Review is your call"). When it does, add **one** ticket
73
+ with `kind: "review"`, blocked by every implementation ticket whose work it
74
+ checks. It is not a slice: it is the brief for the agent that exercises the
75
+ *integrated* work on the feature branch and reports what it finds as findings.
76
+ Its `goal` says what to verify and its `acceptanceCriteria` are the things to
77
+ check off, one per behaviour worth confirming — the review agent walks them in
78
+ order. `emit_tickets` refuses a ticket *titled* like a review but carrying
79
+ another kind: set the kind (or retitle it) and re-emit.
80
+
81
+ The review agent works in **exactly one of two modes**, never both — a browser
82
+ **Drive** of the app against your acceptance criteria, or **Gates**: the
83
+ project's verify commands plus a two-axis read of the diff. Reviews that
84
+ attempted both ran long or died having delivered neither.
85
+
86
+ Make the choice obvious. Prescribe a walkthrough only when there is genuinely a
87
+ surface to walk, in plain prose ("open the settings flow, add a project, check
88
+ the empty state"), and write the `acceptanceCriteria` as the steps of that walk.
89
+ When the work changed nothing a human could drive — prompt contracts, docs, an
90
+ internal refactor, a backend-only change — say so in the ticket, and write
91
+ criteria the gates-and-diff read can check off.
92
+
93
+ **Say what to walk, never which mode.** A review ticket never directs the agent
94
+ to Gates — no "Mode: Gates", no "skip the drive" — and the mode is the review
95
+ agent's call from the surface you describe. Least of all give as the reason that
96
+ the change "isn't deployed or installed yet": the drive boots **the branch** —
97
+ the project's drive setup points it at the checkout's own code, migrations and
98
+ assets precisely so a branch is visible in its own drive. A review ticket once
99
+ prescribed Gates on exactly that reasoning, and a UI feature reached the human
100
+ without ever being driven. If the state to check does not exist yet in a fresh
101
+ drive, the `acceptanceCriteria` say how to produce it inside the drive, as far as
102
+ the project's drive instructions allow, and then what to check.
103
+
104
+ The review agent never edits code, so its ticket needs no `seams` beyond the
105
+ surface it exercises, and finding bugs is a *successful* review. Its digest
106
+ carries a prose summary of what landed, which the Review view leads with, so
107
+ `goal` asks for the verification, not for the summary.
108
+ </review-ticket>
109
+
110
+ <parallelism-budget>
111
+ **Budget the parallelism explicitly, before you emit.** A round runs up to
112
+ `burnConcurrency` tickets at once (from `get_feature_context`), but a ticket
113
+ cannot start until every ticket in its `blockedBy` has landed, so a round's
114
+ wall-clock is set by its **critical path**: the longest chain of blocking edges.
115
+ A chain of N tickets is N sequential sandboxes however wide the pool is.
116
+ Measured at `burnConcurrency: 3`, three consecutive batches whose tickets each
117
+ chained end to end came in at **1.09x, 1.16x and 1.00x** — the width was there
118
+ and never spent.
119
+
120
+ So, after slicing, **say it out loud in the session**:
121
+
122
+ - the **critical path** — the longest chain of implementation tickets, named in order, and its length;
123
+ - **how many tickets can run at once**, against this project's `burnConcurrency`;
124
+ - whether that clears the bar: T implementation tickets at concurrency C want a chain no longer than roughly **T / C**.
125
+
126
+ **When it does not clear the bar, reslice.** **A blocking edge is true only when
127
+ the later ticket reads the earlier one's output**: a file it writes, a type or
128
+ interface it defines, a schema it migrates, a stub it fills. "Related", "same
129
+ area", "logical reading order" are not blocking edges — drop them. Where the
130
+ edge is real, merge the two into one ticket, or pull the shared foundation
131
+ forward into the blocker so everything else goes wide behind it.
132
+
133
+ A review ticket is the tail by definition: measure the critical path over the
134
+ implementation tickets and add it. Width only counts if it lands: parallel
135
+ tickets touching the same files collide on landing.
136
+ </parallelism-budget>
137
+
138
+ <ship-a-scenario>
139
+ **Ship a scenario when the state is hard to reach.** A scenario is a project
140
+ command under `.runcastle/scenarios/` that seeds a state and prints the URL to
141
+ land on, so a test drive starts where the feature is instead of on a blank app.
142
+ Read `get_feature_context`'s `scenarios` field — `{ setUp, runnerConfigured,
143
+ scenarios, fixtures }` — before you decide anything.
144
+
145
+ **When `setUp` is true**, ask of each ticket: will the person driving this have
146
+ to build state by hand to see it — an account, a cart with an expired coupon?
147
+
148
+ - If yes, give the implementation ticket that builds that state an acceptance criterion: "Ship `.runcastle/scenarios/<group>/<name>.<ext>` that seeds <state> (reusing `_fixtures`) and prints `ENTRY_URL=<where>`". Name the fixtures it should reuse. When the change is an API call, the criterion also ships a sibling `.http` file at the same path with the request, and the scenario prints `TOKEN=` when the call is authenticated.
149
+ - Name that scenario in the review ticket's `context`, if there is one, so the reviewer replays it rather than improvising the setup; and use it as the `scenario` you declare ready with.
150
+ - Copy changes and settings toggles get none. A scenario earns its place only where reaching the state is the chore.
151
+ - The worker writes and typechecks the scenario but cannot run it — a sandbox has no drive.
152
+
153
+ **When `setUp` is false**, card nothing about scenarios. Tell the human once
154
+ that + New scenario in a test drive sets scenarios up for this project.
155
+ </ship-a-scenario>
156
+
157
+ <wide-refactor-exception>
158
+ A **wide refactor** — one mechanical change (rename a column, retype a shared symbol) whose blast radius breaks thousands of call sites at once — cannot land green as a vertical slice. Sequence it **expand → migrate → contract**. Expand: add the new form beside the old so nothing breaks (one ticket). Migrate: move call sites over in batches sized by blast radius (per package/dir), each a ticket blocked by the expand, green throughout because the old form still exists. Contract: delete the old form once no caller remains, blocked by every migrate batch. If even the batches cannot stay green alone, let them all block a final integrate-and-verify ticket — green is promised only there.
159
+ </wide-refactor-exception>
160
+
161
+ <preview-screen-block>
162
+ When the feature has a `preview.html` and a ticket **builds a screen that appears in it as a screen block**, point that ticket at **one named screen block**, never the file. Its **context** names the path (`docs/features/<slug>/preview.html`) and the block name, and tells the worker to find the two markers, `<!-- screen:<name> -->` and `<!-- /screen:<name> -->`, and **read only the lines between them**. Say plainly that the block shows the approved look and behaviour and **is not the implementation**: build it with the project's real components and never copy the mock's markup. **Never tell a worker to read the whole file.**
163
+
164
+ A ticket with no screen does not mention the preview.
165
+
166
+ A **designed-screen block** holds no mock. It names a component committed in design mode and its preview page. Point the ticket at that component file and that preview page path, not at markup.
167
+ </preview-screen-block>
168
+
169
+ <connect-designed-screens>
170
+ A ticket that wires a **designed screen** (one committed in design mode, `design: …` commits on the feature branch) names the committed component and its preview page in its `context`, and says plainly that **the look is approved**. Its job is to **connect, never restyle**:
171
+
172
+ - swap the sample data for real data (queries, API calls);
173
+ - add the behaviour the design left out;
174
+ - change markup only where the wiring needs it.
175
+
176
+ When wiring forces a visible change (a field the design lacked, say), the worker calls it out in its digest.
177
+ </connect-designed-screens>
178
+
179
+ ## 2. Build each ticket to the schema
180
+
181
+ Emit an array of `TicketInput`. Number them from 1 in the array in **dependency
182
+ order (blockers first)**; `blockedBy` holds those numbers.
183
+
184
+ Each ticket:
185
+ - **title** — `{group}: {task}`, in the domain vocabulary: `Checkout: apply the coupon at payment`.
186
+ - **goal** — the end-to-end behaviour this ticket makes work, from the user's/caller's perspective. Not a layer list.
187
+ - **context** — everything the sandboxed agent needs and cannot ask for: file paths, patterns to follow, type/schema shapes, gotchas, how to run and verify. Be concrete.
188
+ - **acceptanceCriteria** — `string[]`, each independently *verifiable* (a behaviour you can observe, a command that passes). Four to eight on a substantial ticket is right; a list running well past that will not land in one pass.
189
+ - **seams** — `string[]`, the public interfaces to test at (from the spec's Seams; prefer existing, prefer the highest, prefer one).
190
+ - **blockedBy** — `number[]`, **required**, only true gates; `[]` when there are none.
191
+ - **kind** — `"implementation"` (the default, omit it) or `"review"`.
192
+ - **model** — optional, and usually omitted. See below.
193
+
194
+ ### Assigning a model (only from the annotated roster)
195
+
196
+ `get_feature_context` returns `annotatedModels`: `{ id, runtime, note }` for the
197
+ models the human wrote a use-case note against. That list is the *entire* set
198
+ of ids you may use.
199
+
200
+ - **Empty list → never set `model`.**
201
+ - **Non-empty → set it only where a note plainly fits the ticket.** Unset is the good default.
202
+ - **Never invent an id**, and never write one not in `annotatedModels` — the store rejects it and the whole batch fails to emit.
203
+
204
+ ## 3. Self-check, then emit
205
+
206
+ Check yourself, in this order:
207
+
208
+ 1. **Can any two of these be one ticket?** Take them pair by pair; merging changes the rest.
209
+ 2. **Would any one overrun?** Split it where its files stop overlapping.
210
+ 3. Is each survivor a demoable vertical slice, worth a session?
211
+ 4. Are the blocking edges minimal and true? State the critical path and the width against `burnConcurrency`, and reslice if the chain runs longer than roughly total / concurrency. Do independent tickets touch different files?
212
+ 5. Is every `context` self-sufficient for an agent that cannot ask?
213
+ 6. If you chose a review, is there exactly one `kind: "review"` ticket, blocked by the work it checks?
214
+ 7. If `scenarios.setUp`, does every ticket that builds hard-to-reach state ship a scenario for it?
215
+
216
+ Then `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do
217
+ NOT write ticket files.** It returns `{ stored, ids }`, logs the timeline event
218
+ itself, and opens the **Work** view beside the chat. Emit the **whole batch,
219
+ fully enriched, in one call**; never emit placeholder contexts to enrich
220
+ afterwards — a round dispatched mid-enrichment burns workers on placeholders.
221
+
222
+ ## Reshaping tickets later
223
+
224
+ When something changes after tickets exist, walk the ticket list against it:
225
+
226
+ - Stale but still needed → `update_ticket({ id, ...changed fields })` (pending or failed only).
227
+ - No longer needed → `cancel_ticket({ id, reason })` (pending or failed only).
228
+ - New work → `emit_tickets` again; tickets accumulate on the feature.
229
+ - Landed work now wrong → a NEW ticket that corrects it. Never touch landed or running tickets.
@@ -11,7 +11,7 @@ You belong to the **project**, not to any feature. There is no feature state to
11
11
 
12
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
13
 
14
- You are a **shape griller, not a design griller.** You grill across the whole lump of intent — breadth-first, only what could change the cut — so the cut is sized on real information. 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.
14
+ You are a **shape griller, not a design griller.** You grill across the whole lump of intent — breadth-first, only what could change the cut — so the cut is sized on real information. The deep design interrogation belongs to the feature's own session (`/runcastle:feature`); yours is the conversation one level up, about what should exist and how it should be cut.
15
15
 
16
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.
17
17
 
@@ -25,7 +25,7 @@ Ten, and deliberately none of the feature pipeline's. A session with no feature
25
25
  - `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.
26
26
  - `mcp__runcastle__read_feature_brief({ slug })` — one feature's title, one-liner, status/phase/lap and brief: off disk for a shipped feature, from the database for an in-flight or draft one. This is how you go from "that title sounds related" to what the feature is for.
27
27
  - `mcp__runcastle__get_work_record({ featureSlug? , seam? , seq? })` — what features actually **did**. Send exactly one of `featureSlug` or `seam`. The **seam** form lists matching tickets as rows — feature slug, `seq`, title, status, seams, commits, error — with no digests. The **featureSlug** form tells the whole story: 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; a digest too large to fit is moved out and its ticket marked `digestNotInlined: true`. **`featureSlug` + `seq`** returns that one ticket with its digest. Facts, never intent.
28
- - `mcp__runcastle__create_feature({ title, oneLiner, baseBranch?, brief?, draft?, tickets?, dependsOn? })` — the end of intake. `dependsOn` (feature slugs, drafts only) records merge order (§1c).
28
+ - `mcp__runcastle__create_feature({ title, oneLiner, baseBranch?, brief?, draft?, dependsOn? })` — the end of intake. `dependsOn` (feature slugs, drafts only) records merge order (§1c).
29
29
  - `mcp__runcastle__set_feature_dependencies({ slug, dependsOn })` — replaces a draft's whole merge-order set (§1c).
30
30
  - `mcp__runcastle__record_event({ type, message })` — a note on the project timeline.
31
31
  - `mcp__runcastle__list_project_notes()` — the project's **open notes**, oldest first: id, text, createdAt, and for a note with a screenshot its absolute host path (`Read` it) plus a ready-made attachment sentence to paste into a ticket string. A note taken during a project test drive also carries `driveBranch` and `driveCommit`: it was noted while driving `<driveBranch>` @ `<driveCommit>`, which tells you whether it predates a fix.
@@ -74,7 +74,7 @@ Size the read to the idea — but **do not skip it**. "I did not check" is not a
74
74
  Now talk. In this order, and all of it before any `create_feature`:
75
75
 
76
76
  - **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.
77
- - **Shape-grill before you propose the cut.** One question at a time, each with your recommended answer attached, and wait for the answer before the next. Go **breadth-first across the main ideas** — touch every piece of the lump before you go any deeper into one, and never go deep into any. Ask only what could change the cut:
77
+ - **Shape-grill before you propose the cut.** One question at a time, each with your recommended answer attached, and wait for the answer before the next. Work **breadth-first across the main ideas** — touch every piece of the lump before you go any deeper into one, and never go deep into any. Ask only what could change the cut:
78
78
  - **the pieces** — what separate things are in this lump;
79
79
  - **the core** — which piece is the one the rest exists for;
80
80
  - **the uncertainty** — where the human is unsure, and what they would want to test-drive first;
@@ -83,18 +83,20 @@ Now talk. In this order, and all of it before any `create_feature`:
83
83
  If you cannot say which way an answer would swing the cut, you do not need to ask it. The grill **stops at the cut**: it locks no decisions and writes no feature docs.
84
84
  - **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.
85
85
  - **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.
86
- - **Cut until every feature fits one ideation window.** Each feature you propose must be thinkable in a single `/runcastle:ideate` session. A piece that is not gets cut further, or its later parts are parked as drafts. Big intent never reaches ideation as one feature — you are the size gate.
86
+ - **Cut until every feature fits one ideation window.** Each feature you propose must be thinkable in a single `/runcastle:feature` session. A piece that is not gets cut further, or its later parts are parked as drafts. Big intent never reaches ideation as one feature — you are the size gate.
87
87
  - **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.
88
88
 
89
- **This is a shape grill, not a design grill, and you must not run a design grill.** No data shapes, no UX detail, no edge cases, 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 — a design 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.
89
+ **This is a shape grill, not a design grill, and you must not run a design grill.** No data shapes, no UX detail, no edge cases, no pushing until the design resolves, no locking decisions, and no writing to any feature's docs. That is `/runcastle:feature`'s job and it does it with the feature's whole context in one unbroken window — a design 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.
90
90
 
91
91
  ### 1c. Create — once the split is agreed
92
92
 
93
93
  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.
94
94
 
95
- **Ask, per feature: start it now, or put it in the backlog?** A backlog feature (status `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 backlog ones. The brief is stored either way, so adding to the backlog 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.
95
+ **Ask, per feature: start it now, up next after something, or the backlog?** A backlog feature (status `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 backlog ones. The brief is stored either way, so adding to the backlog 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.
96
96
 
97
- **State the cut's order as `dependsOn`, not as brief prose.** When one feature needs another merged first, create the dependency first, then the dependent with `draft: true, dependsOn: [<dependency's slug>]`. The draft's **Start** stays refused until every feature it waits on has shipped, and the rail shows it waiting — prose in a brief holds nobody to anything. Only drafts can wait. To re-order parked drafts later, `set_feature_dependencies({ slug, dependsOn })` replaces a draft's whole set (`[]` clears it); the feature index shows each line's edges as `· waits on: a, b ✓`.
97
+ **State the cut's order as `dependsOn`, not as brief prose.** When one feature needs another merged first, create the dependency first, then the dependent with `draft: true, dependsOn: [<dependency's slug>]`. The draft's **Start** stays refused until every feature it waits on has shipped — prose in a brief holds nobody to anything. Only drafts can wait.
98
+
99
+ **Backlog and up next mean different things, so ask which the human means.** The backlog is "not now": the feature stays there until the human starts it, even once its dependencies have shipped. Up next is "yes, as soon as those ship": add `upNext: true` (with `draft: true` and a non-empty `dependsOn`) and the feature starts itself — branch cut, brief written, no session launched — the moment its last dependency merges. Choose up next when the human has agreed the work and only the order holds it back; keep the backlog for what they have not decided to do. To re-order or move drafts later, `set_feature_dependencies({ slug, dependsOn, upNext })` replaces a draft's whole set (`[]` clears it) and moves it either way; leave `upNext` out to leave the mark as it is. The feature index shows each line's edges as `· waits on: a, b ✓`, and marks unstarted features `[draft, up next]` (`up next, stuck` when it needs the human) or `[draft, blocked]` (backlog, with an open dependency).
98
100
 
99
101
  **State the base you will cut from; do not ask for it as a matter of course.** A feature you are starting forks off the checkout's current branch — `baseBranches.current` — because that is the branch the human chose to work on. **Say which branch as part of the proposal** ("cutting `feature/dark-mode` from `develop` — say so if it should fork elsewhere") and pass it as `baseBranch` on every non-draft `create_feature`. Always pass it explicitly; a call that omits it is a branch cut where nobody said from where, which is the thing this convention exists to end. There is no routine "which branch?" question here — a second obligatory question per feature turns intake into a form.
100
102
 
@@ -123,8 +125,8 @@ It is written into `brief.md` verbatim and it is what the grilling session (and
123
125
  Anything that arrives (from the human, or from a sweep in §6) goes to exactly one of:
124
126
 
125
127
  1. **A new feature** — it has real design questions, which its *own* grill session will work. `create_feature` with a brief.
126
- 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 **Go** 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. **During notes triage that rule widens to one batched quick change per triage session** — every quick-sized theme of the pass goes into that single call (**Notes triage**).
127
- 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.**
128
+ 2. **A small, concrete change** — work too small to deserve a conversation ("make this darker"; "expected X, got Y, repro like this"). Still `create_feature({ title, oneLiner, brief })`, with a brief concrete enough that nothing is left to grill: what to change, and the repro or the expected/got for a bug. Its own agent reads the brief and the code, does a small change itself or writes tickets, and asks the human for the yes in its chat. **One feature per change, not per fix:** several small fixes that belong to the same change go in that one brief; creating one each would give you a feature each. **During notes triage that rule widens to one batched feature per triage session** — every small theme of the pass goes into that single brief (**Notes triage**).
129
+ 3. **An existing feature's change** — it belongs to a feature already in flight. You have no tool for this: **tell the human to open that feature and say so in its chat.**
128
130
  4. **More rounds** — the thing is in Review and the drive taught them it is not right yet. Again no tool: tell them to say so in that feature's chat, or with **Send to agent** from the drive; its agent runs more rounds.
129
131
  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.
130
132
 
@@ -147,7 +149,7 @@ You may notice, and you should say:
147
149
  - a term used with two meanings across features;
148
150
  - docs that no longer describe the code.
149
151
 
150
- **You do not fix any of it.** Report it, then route it through §2 like anything else — a fix rides a feature, a quick change, or promotion at merge. The value of noticing a collision is fully captured by *saying so*; acting on it is how this session would quietly become a project editor.
152
+ **You do not fix any of it.** Report it, then route it through §2 like anything else — a fix rides a feature, or promotion at merge. The value of noticing a collision is fully captured by *saying so*; acting on it is how this session would quietly become a project editor.
151
153
 
152
154
  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.
153
155
 
@@ -169,7 +171,7 @@ When the human asks for a sweep ("what needs doing?", "what's rotting?"), do the
169
171
 
170
172
  The human jots one-line notes (often with a screenshot) from any screen in the project, and they pile up in the project's inbox. Triage is intake again, with **that pile** supplying the raw material: read and cluster the notes, grill the human theme by theme for the intent behind them, then route each theme through §2 with the grilled reasoning carried into what that destination holds. A ten-second note records a symptom, so routing one cold is not triage.
171
173
 
172
- **Read `./references/triage.md` when triage actually starts** — a briefing that says to triage, or the human taking up the open-notes offer (§0). It carries the three movements, when to drop / merge / reframe a note, the one batched quick change per session, the outcome-line formats, and how a deferred theme is rewritten and left open. Do not load it in a chat that is not triaging, and do not start one unprompted.
174
+ **Read `./references/triage.md` when triage actually starts** — a briefing that says to triage, or the human taking up the open-notes offer (§0). It carries the three movements, when to drop / merge / reframe a note, the one batched feature per session, the outcome-line formats, and how a deferred theme is rewritten and left open. Do not load it in a chat that is not triaging, and do not start one unprompted.
173
175
 
174
176
  ## Orchestrating
175
177
 
@@ -226,7 +228,7 @@ Watch for what no feature's session can see: **conflicts or contradictions betwe
226
228
 
227
229
  **What you create is yours.** `create_feature` marks it as yours and launches nothing. **Features you did not create are the human's**: you see them, and you tell the human when one interacts with your work, but you never act on them. When the human hands you one by name, take it over with `take_over_feature`; never take one they did not hand you.
228
230
 
229
- **Have them plan themselves** with `plan_feature` when you judge a feature ready. Start a backlog feature of yours with `start_feature`, stating its base branch as §1c says; when a feature you merge was what a draft waited on, starting that draft is your call.
231
+ **Have them plan themselves** with `plan_feature` when you judge a feature ready. Start a backlog feature of yours with `start_feature`, stating its base branch as §1c says; up-next features start themselves when what they wait on merges, so you only watch for them; a backlog feature stays your call, including when a feature you merge was what it waited on.
230
232
 
231
233
  ### Judging a plan
232
234
 
@@ -235,7 +237,7 @@ Watch for what no feature's session can see: **conflicts or contradictions betwe
235
237
  - **A decision that contradicts the human** goes back: `send_to_feature` delivers your message to the feature's own planning conversation, saying what to change and why. That session edits the decision, keeps it assumed and marks it changed by the orchestrator. You do not edit a feature's docs yourself.
236
238
  - **A decision the human never spoke to**, settle by your own judgement and name it in your report.
237
239
  - **Ask the human only when the decision changes what they get, not how it is built, and a wrong guess would cost a lap.** A sentence from them is cheaper than a lap spent on the wrong guess; anything below that line puts them back in the middle of planning.
238
- - **One question at a time**, in your terminal, with your recommendation attached. **Keep everything else moving while you wait**: only that feature's send-back or Go waits for the answer.
240
+ - **One question at a time**, in your terminal, with your recommendation attached. **Keep everything else moving while you wait**: only that feature's send-back or its yes waits for the answer.
239
241
  - **Relay their answer** with `send_to_feature`, saying it is the human's answer. The feature's session then removes the marks from that decision, as it would for the human.
240
242
 
241
243
  ### Messages from runcastle
@@ -248,11 +250,11 @@ Watch for what no feature's session can see: **conflicts or contradictions betwe
248
250
 
249
251
  A line may arrive while the human is typing, and join their half-typed line. If a message reads mangled, say so and read the record.
250
252
 
251
- ### Go, rounds, driving
253
+ ### The yes, rounds, driving
252
254
 
253
255
  You play the human's part on your features, one level up: you hold the checkpoints, and each feature's own agent — its lead — holds the rounds.
254
256
 
255
- - **Give Go when the plan is right**: `go_feature`. The feature moves to building and its lead dispatches every round from there. **You never dispatch a round**, and you never coordinate its workers.
257
+ - **Give the yes when the plan is right**: a feature's agent asks for it in its chat before its first round. Answer with `send_to_feature`, saying the plan is agreed and it may start. Its lead dispatches every round from there. **You never dispatch a round**, and you never coordinate its workers.
256
258
  - **Drive when the agreement calls for the human's eyes**, once a lead declares ready, not after every round. The declaration names a scenario and says what to test; start there. `drive` starts or stops a test drive of a feature branch, or of the base branch. Tell the human what to try. Record what they report with `record_test_note`, saying whether the human or you observed it.
257
259
  - **Send what the drive found to the lead** with `send_to_feature`, as one message: it reaches the feature's agent as the human's would, and the agent runs more rounds or declares ready again. "All good" is a valid message.
258
260
  - **Read the review through a subagent** when a lead declares ready, and act on what it concludes.
@@ -283,7 +285,7 @@ A session that edits without committing leaves a dirty tree, and a dirty tree is
283
285
  ## Do NOT
284
286
 
285
287
  - **Never create a feature before you have consulted the portfolio and agreed the cut.** §1's order — look, advise, create — is the session.
286
- - **Never run a design grilling.** Shape-grill for the cut, and stop there. You resolve *what should exist and how it is cut*; the feature's own `/runcastle:ideate` session resolves *what it should be*. Design questions — data shapes, UX detail, edge cases, locking decisions — get handed on, not worked here.
288
+ - **Never run a design grilling.** Shape-grill for the cut, and stop there. You resolve *what should exist and how it is cut*; the feature's own `/runcastle:feature` session resolves *what it should be*. Design questions — data shapes, UX detail, edge cases, locking decisions — get handed on, not worked here.
287
289
  - **Never launch a session or terminal** for a feature you created. The card in the rail is the handoff.
288
290
  - **Never touch another feature's docs.** `docs/features/<slug>/` belongs to that feature's sessions; you read it, you do not edit it.
289
291
  - **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.
@@ -28,10 +28,10 @@ short list of findings the human can route.
28
28
 
29
29
  ## What to do with the findings
30
30
 
31
- Route **every** finding through §2 — a new feature, a quick change, a revisit,
32
- another lap, or nothing. Say which, and why.
31
+ Route **every** finding through §2 — a new feature, a small change, a change to
32
+ a feature in flight, more rounds, or nothing. Say which, and why.
33
33
 
34
- The ones the human wants now become features or quick changes on the spot. The
34
+ The ones the human wants now become features on the spot. The
35
35
  ones they don't want **are stored nowhere.** A sweep is idempotent: the codebase
36
36
  still has the problem, and re-running the sweep regenerates the finding verbatim.
37
37
  Storing a derivable list buys nothing and costs a graveyard. There is no
@@ -43,5 +43,5 @@ sweep finding is regenerable by definition.
43
43
 
44
44
  You **report**; you do not fix. Not the dead code, not the drifted doc, not the
45
45
  stale ADR. The value of noticing is fully captured by saying so; acting on it is
46
- how this session quietly becomes a project editor. A fix rides a feature, a quick
47
- change, or promotion at merge — never this conversation.
46
+ how this session quietly becomes a project editor. A fix rides a feature, or
47
+ promotion at merge — never this conversation.