runcastle 1.1.1 → 1.2.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "runcastle",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "Burn tickets into shipped features with Claude Code.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,11 +24,17 @@ Four, and deliberately none of the feature pipeline's. A session with no feature
24
24
 
25
25
  Every **merged** feature's docs are already on disk in this worktree — `docs/features/<slug>/`. Read them with your ordinary `Read`/`Grep`; the index says where. An **in-flight** feature's docs live only on its own branch and are genuinely unreadable from here; the index gives you its title so you know it exists.
26
26
 
27
- ## 0. Orient
27
+ ## 0. Open by asking
28
28
 
29
- 1. Call `get_project_context`. That is your charter, your live ADRs, and the shape of the portfolio.
30
- 2. If there is **no charter**, note it and read on — see §5. Do not scaffold one.
31
- 3. Listen to what the human brings before you reach for anything else.
29
+ Your first visible move is a **question**, not a lookup. Greet them and put it:
30
+
31
+ > What are we cutting into features today?
32
+
33
+ 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
+
35
+ When you do need it, size the read to the question. `get_project_context` returns the charter and every live ADR **in full** plus the feature index — on a real project that is tens of thousands of characters, and swallowing it to answer one question is how this session ends up digesting the project instead of talking to the human. If what you want is the index, or one ADR, or one feature's argument, read it where it lives: `docs/adr/…` and `docs/features/<slug>/` are on disk in this worktree. Call `get_project_context` when you genuinely want the whole picture.
36
+
37
+ If it turns out there is **no charter**, note it and read on — see §5. Do not scaffold one.
32
38
 
33
39
  ## 1. Intake — grill the lump until it resolves into features
34
40
 
@@ -1,36 +1,38 @@
1
1
  ---
2
2
  name: tickets
3
- description: Break the spec into the fewest vertical slices that each fill one fresh sandboxed agent session, 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.
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 -->
7
7
 
8
8
  # Tickets
9
9
 
10
- Break the work into **tickets** — vertical slices, each cutting end-to-end through every layer it needs, and each big enough to be worth a whole agent session — and emit them to the runcastle store via `mcp__runcastle__emit_tickets`. You are still in the unbroken ideation window: synthesize from the spec (`docs/features/<slug>/spec.md`). Do not compact or clear.
10
+ Break the work into **tickets** — vertical slices, each cutting end-to-end through every layer it needs, each big enough to be worth a whole agent session and small enough to finish inside one — and emit them to the runcastle store via `mcp__runcastle__emit_tickets`. You are still in the unbroken ideation window: synthesize from the spec (`docs/features/<slug>/spec.md`). Do not compact or clear.
11
11
 
12
12
  ## The one thing that makes a runcastle ticket different
13
13
 
14
14
  Each ticket is executed by a **single fresh agent, alone, in a sandbox, with ~100k tokens and NO way to ask a follow-up question.** It sees only what you put in the ticket plus the code already in the repo. So — *inverting* the usual "avoid file paths, they go stale" rule — the ticket's `context` field **must be concrete and self-sufficient**: name the files to touch, the existing patterns to copy, the type/schema shapes, the gotchas, the commands to run and verify with. A ticket that assumes the agent can ask "where does X live?" is a broken ticket. Over-specify context; under-specify nothing.
15
15
 
16
- ## The second thing: a ticket costs ~10 minutes before it does any work
16
+ ## The second thing: a ticket costs ~10 minutes before it does any work — and pays it twice if it overruns
17
17
 
18
18
  That fresh agent is fresh *every time*. Each ticket gets its own container: cold build, a full dependency install (measured 70–507s, ~2.5 min average), then an agent that re-reads the spec, the docs digest, and every file your `context` names — reading the repo was 16% of all agent time across 42 measured attempts. **This overhead is paid per ticket and is the same whether the ticket is twenty lines or a day's work.** Splitting one coherent piece of work into three tickets triples it, and adds two seams where a new agent has to reconstruct what the previous one had just finished learning.
19
19
 
20
- So size for the **largest slice that still lands green**, not the smallest. Fine-grained slicing still matters — it just belongs *inside* a ticket, as the `acceptanceCriteria` list. The burner works red→green one criterion at a time and commits each green slice, so one ticket with eight criteria produces eight small commits and orients once; eight one-criterion tickets produce the same commits and orient eight times.
20
+ But that meter runs both ways. A ticket too big to finish in one pass gets no cheap second wind: an iteration is not a retry (ADR-0008) — the container is rebuilt, the install runs again, and a brand-new agent re-reads everything from the last commit, having lost every conclusion its predecessor reached. An overstuffed ticket pays the exact overhead it was merged to avoid, on top of the orientation it already paid once, and it pays it *after* the work is half-done rather than before.
21
21
 
22
- Expect a **handful of substantial tickets** for a feature — roughly 3–6, each filling a session. A dozen thin ones means you sliced by layer or by file, not by behaviour.
22
+ So size a ticket to **earn its container and still land in one pass**: substantial, with room left at the end for the tests, the self-review and the commits — not the smallest slice you can name, and not the largest you can imagine fitting. Fine-grained slicing still matters — it just belongs *inside* a ticket, as the `acceptanceCriteria` list. The burner works red→green one criterion at a time and commits each green slice, so one ticket with six criteria produces six small commits and orients once; six one-criterion tickets produce the same commits and orient six times.
23
+
24
+ Expect a **handful of substantial tickets** for a feature — roughly 4–8, each worth a session. A dozen thin ones means you sliced by layer or by file, not by behaviour. Two or three giant ones means you will pay back the containers you saved, with interest, when they overrun.
23
25
 
24
26
  ## 1. Draft vertical slices
25
27
 
26
28
  <vertical-slice-rules>
27
29
  - 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.
28
30
  - A completed slice is demoable or verifiable on its own.
29
- - Each slice **fills** one fresh ~100k-token agent session working alone, without overflowing it.
30
- - **Merge by default.** 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.
31
- - **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 genuinely will not fit one session; (c) a prefactor, which is its own slice done first ("make the change easy, then make the easy change"); (d) the wide-refactor sequence below.
31
+ - Each slice is worth one fresh ~100k-token agent session working alone and **lands inside it** — the orientation at the front and the tests, self-review and commits at the back are fixed tax, so leave them room instead of filling the window to the brim.
32
+ - **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. Two drafts that share none of that are not one ticket merely because both would fit.
33
+ - **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.
32
34
  - **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.
33
- - Tickets with no blocking edge between them can burn in parallel — but a whole container of overhead is a steep price for width. Let parallelism justify a split only when both halves are already large.
35
+ - **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 for one container. Two touching the *same* files race to land, and the loser fails on a merge conflict: that width costs a re-burn, which is worse than the container it bought. Overlapping files → merge.
34
36
  </vertical-slice-rules>
35
37
 
36
38
  Give each ticket its **blocking edges** — the tickets that must finish before it can start. No blockers → it can start immediately.
@@ -47,7 +49,7 @@ Each ticket:
47
49
  - **title** — short, descriptive, in the domain vocabulary.
48
50
  - **goal** — the end-to-end behaviour this ticket makes work, from the user's/caller's perspective. Not a layer list.
49
51
  - **context** — everything the sandboxed agent needs and cannot ask for: file paths to touch, existing patterns/modules to follow, type/schema shapes, gotchas, how to run and verify. Be concrete (see above).
50
- - **acceptanceCriteria** — `string[]`, each independently *verifiable* (a behaviour you can observe, a command that passes). These are what the burner works red-green against, one at a time, committing each green one — so this is where the fine granularity lives. A substantial ticket having six or ten criteria is right, not a smell.
52
+ - **acceptanceCriteria** — `string[]`, each independently *verifiable* (a behaviour you can observe, a command that passes). These are what the burner works red-green against, one at a time, committing each green one — so this is where the fine granularity lives. A substantial ticket having four to eight criteria is right, not a smell; a list running well past that is a ticket that will not land in one pass.
51
53
  - **seams** — `string[]`, the public interfaces to test at (carry them from the spec's Seams section; prefer existing, prefer the highest, prefer one).
52
54
  - **blockedBy** — `number[]`, the seq numbers (from your 1..N ordering) of the tickets that *genuinely* gate this one. Only true gates.
53
55
 
@@ -55,10 +57,11 @@ Each ticket:
55
57
 
56
58
  There is no in-session quiz — the human's review is the **Burn** gate in the runcastle UI, reading these cards. So make them right before you emit. Check yourself, in this order:
57
59
 
58
- 1. **Can any two of these be one ticket?** Go pair by pair. Same files, shared type, sibling behaviours, or one whose blocker is the only thing it waits on and together they'd still fit a session → merge them. This is the check that matters most; do it before the others, because merging changes the rest.
59
- 2. Is each survivor a demoable vertical slice rather than a horizontal layer, and does it fill a session rather than rattle around in one?
60
- 3. Are the blocking edges minimal and genuinely true gates?
61
- 4. Is every `context` self-sufficient for an agent that cannot ask — now covering the whole of a bigger ticket, every file and pattern it touches?
60
+ 1. **Can any two of these be one ticket?** Go pair by pair. Same files, shared type, sibling behaviours, or one whose blocker is the only thing it waits on and together they'd still land in one session → merge them. Do this first, because merging changes the rest.
61
+ 2. **Would any one of these overrun?** A criteria list longer than one agent can take red→green — with tests, self-review and commits still to come — costs a whole second container to finish. Split it where its files stop overlapping.
62
+ 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?
63
+ 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?
64
+ 5. Is every `context` self-sufficient for an agent that cannot ask — covering every file and pattern the ticket touches?
62
65
 
63
66
  Then:
64
67
  - `mcp__runcastle__emit_tickets({ tickets: [...] })` — **emit the array; do NOT write ticket files.** It returns `{ stored, ids }`.