runcastle 1.0.25 → 1.0.27
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 +792 -103
- package/drizzle/0011_steep_ted_forrester.sql +2 -0
- package/drizzle/0012_greedy_lionheart.sql +1 -0
- package/drizzle/0013_sudden_naoko.sql +21 -0
- package/drizzle/0014_ordinary_mystique.sql +4 -0
- package/drizzle/meta/0011_snapshot.json +771 -0
- package/drizzle/meta/0012_snapshot.json +778 -0
- package/drizzle/meta/0013_snapshot.json +785 -0
- package/drizzle/meta/0014_snapshot.json +817 -0
- package/drizzle/meta/_journal.json +28 -0
- package/index.js +790 -101
- package/package.json +1 -1
- package/skills/burner/prepare-project.md +13 -2
- package/skills/packs/runcastle/skills/ideate/SKILL.md +2 -0
- package/skills/packs/runcastle/skills/revisit/SKILL.md +18 -8
- package/web/assets/index-CBycxXwk.js +138 -0
- package/web/assets/index-yePFYqdd.css +1 -0
- package/web/index.html +2 -2
- package/web/assets/index-B4LHcg8h.css +0 -1
- package/web/assets/index-DrjQZmZ4.js +0 -110
package/package.json
CHANGED
|
@@ -23,12 +23,14 @@ You pay both costs once, here, so that no one pays them again.
|
|
|
23
23
|
|
|
24
24
|
You are running in the **same sandbox image and with the same setup command** that ticket agents will get. That is deliberate: a test baseline captured somewhere else is not comparable to what those agents will see, and a wrong baseline is worse than no baseline — an agent trusts it and files its own breakage under "already red".
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
A dependency install was **attempted** before you started, with this command (or none was needed):
|
|
27
27
|
|
|
28
28
|
```
|
|
29
29
|
{{SETUP_COMMAND}}
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
**It is not guaranteed to have succeeded.** That command is a guess, derived from which lockfiles happen to be present — and the command that actually works is `setupCommand`, the first thing this run exists to establish. So before you trust any test result, confirm dependencies really are installed: check that the tooling runs and that `node_modules` (or this stack's equivalent) is populated. If the install did not happen, finding the command that works is your first job — try the permissive form, the flags the error output itself recommends, or whatever the repo's own CI does — and report that as `setupCommand`. Put the failure you saw in `notes`, because a repo whose stock install does not work from a clean checkout is exactly the kind of thing every agent after you would otherwise rediscover.
|
|
33
|
+
|
|
32
34
|
## How to work
|
|
33
35
|
|
|
34
36
|
1. **Read before running.** Start from `package.json` scripts (and any workspace manifests), `Makefile`, `justfile`, `Taskfile`, CI workflow files under `.github/workflows/`, and the README. CI config is the highest-signal source in the repo: it is the set of commands somebody already proved works from a clean checkout.
|
|
@@ -46,12 +48,18 @@ Dependencies have already been installed by this command (or none was needed):
|
|
|
46
48
|
|
|
47
49
|
## Keys you must NOT run
|
|
48
50
|
|
|
49
|
-
`devCommand` and `dbResetCommand` describe the **human's local machine**, not this sandbox. This container has no dev database and nowhere useful to serve a dev server.
|
|
51
|
+
`devCommand`, `driveSetupCommand`, `driveStopCommand`, `driveEnv` and `dbResetCommand` describe the **human's local machine**, not this sandbox. This container has no dev database, no docker daemon you should be starting stacks in, and nowhere useful to serve a dev server.
|
|
50
52
|
|
|
51
53
|
**Read these from configuration, never execute them.** Find them in `package.json` scripts, `docker-compose.yml`, ORM config (Prisma `schema.prisma`, Drizzle `drizzle.config.*`, TypeORM, Rails `database.yml`, Django `settings.py`), the Makefile, or the README, and report them as *proposals* with the file you found them in as evidence. If the repo has no database, omit `dbResetCommand` entirely — do not invent one.
|
|
52
54
|
|
|
53
55
|
For `dbResetCommand` specifically: report the command that **rebuilds the dev database from the migrations in the working tree** — e.g. `npx prisma migrate reset --force`, `bun run db:reset`, `rails db:reset`, `python manage.py migrate` after a drop. A human will be shown this command and asked whether to run it; it will never be run automatically. Prefer the repo's own script (`bun run db:reset`) over a raw tool invocation when one exists.
|
|
54
56
|
|
|
57
|
+
For `driveEnv`: these are `KEY=VALUE` lines overlaid on the dev server's environment for the duration of a test drive, with `{{slug}}`, `{{branch}}` and `{{id}}` (the slug reduced to `[a-z0-9_]`, safe as a database name) substituted per drive. The point is a **database per branch**: `DATABASE_URL=postgres://localhost:5432/myapp_{{id}}` in `driveEnv`, `createdb -T myapp myapp_{{id}}` in `driveSetupCommand`, `dropdb --if-exists myapp_{{id}}` in `driveStopCommand`.
|
|
58
|
+
|
|
59
|
+
Only report it when the repo tells you the variable's real name and shape — read `.env.example`, `docker-compose.yml`, or the ORM config, and keep the host, port and credentials **exactly** as they appear there, changing only the database name. If you would have to invent any part of the connection string, omit the key. A URL that is right in shape and wrong in substance points the developer's dev server at a database that does not exist, and it will look like our bug, not a missing setting. Clone the existing dev database in the setup command (`-T`/template) rather than migrating an empty one, so the drive starts with the data the developer already has.
|
|
60
|
+
|
|
61
|
+
For `driveSetupCommand`: this runs on the developer's machine immediately before their dev server starts, when they check out a branch to try it by hand. Report the shortest command that leaves the project runnable — `docker compose up -d && bun run db:migrate`, `make dev-up`, `bin/setup`. Chain steps with `&&` so a failing step stops the rest. Do **not** assemble one out of tools you merely found installed; if the repo does not describe a startup procedure, or the dev command is genuinely self-sufficient, omit the key. This one is run automatically on a real machine, so a wrong guess costs more than a missing answer. `driveStopCommand` is its counterpart (`docker compose down`) and is omitted just as freely.
|
|
62
|
+
|
|
55
63
|
## Write your findings
|
|
56
64
|
|
|
57
65
|
Write the file `.runcastle/prep.json` at the repo root and **commit it**:
|
|
@@ -73,6 +81,9 @@ The file is exactly this shape. Every key is optional — **omit any key you cou
|
|
|
73
81
|
"verifyCommands": { "value": "pnpm --filter @acme/web typecheck\npnpm --filter @acme/web test", "evidence": "both run and exit 0; the repo-wide `pnpm test` errors with 'no projects matched'" },
|
|
74
82
|
"knownFailures": { "value": "3 failing before any change: api/auth.test.ts (2), web/upload.test.ts (1)", "evidence": "full suite on HEAD: 412 passed, 3 failed" },
|
|
75
83
|
"devCommand": { "value": "pnpm dev", "evidence": "package.json scripts.dev — not executed (host-only)" },
|
|
84
|
+
"driveSetupCommand": { "value": "docker compose up -d && createdb -T acme_dev acme_{{id}} && pnpm db:migrate", "evidence": "README 'Local development'; compose defines postgres + redis — not executed (host-only)" },
|
|
85
|
+
"driveStopCommand": { "value": "docker compose down && dropdb --if-exists acme_{{id}}", "evidence": "counterpart to the compose stack in README — not executed (host-only)" },
|
|
86
|
+
"driveEnv": { "value": "DATABASE_URL=postgres://acme:acme@localhost:5432/acme_{{id}}", "evidence": ".env.example line 3, database name swapped for the per-drive one — not executed (host-only)" },
|
|
76
87
|
"dbResetCommand": { "value": "npx prisma migrate reset --force", "evidence": "prisma/schema.prisma present; scripts has no db:reset — not executed (host-only)" },
|
|
77
88
|
"notes": "The e2e suite needs a running Postgres and could not run in this sandbox."
|
|
78
89
|
}
|
|
@@ -28,6 +28,8 @@ Interview the human about every aspect of the feature until you reach a genuinel
|
|
|
28
28
|
- **Look up facts; ask only for decisions.** If a fact is discoverable in the codebase — how something currently works, what a type looks like, whether a pattern already exists — read/grep the repo and find it. Never make the human be your grep. *Decisions* are theirs: put each one to them and wait.
|
|
29
29
|
- **Model the domain as you go** (forked from domain-modeling): challenge terms that conflict with existing usage, sharpen fuzzy words to one canonical meaning ("you said 'account' — Customer or User?"), invent concrete edge-case scenarios that force precision, and cross-check claims against the actual code ("your code cancels whole Orders, but you just said partial — which is right?").
|
|
30
30
|
- **Probe the size early.** Within your first few questions, ask enough about scope to judge whether this is one-session-sized or map-sized — how many independent areas it touches, what has to be researched or prototyped before decisions can lock, how wide the design tree looks. Make that call up front, not thirty questions deep into one corner. If it smells map-sized, go to §3 *now* rather than rabbit-holing first.
|
|
31
|
+
- **Ask the slicing question: how sure are you this is what you want?** Put it to them early, plainly, with your recommendation attached. **Sure, and small** → spec the whole thing; it is one lap and will likely merge on lap 1 — the old linear flow, verbatim. **Unsure, or large** → recommend a thin **lap 1**: a walking skeleton of the uncertain part, or one sub-feature slice they can actually test-drive, with the consciously deferred scope parked in the spec's `## Later laps` section (from there it seeds the next lap's session alongside the test notes). **Say the recommendation out loud and let them answer** — this is a judgment you offer, not a rule you enforce, and the human decides. There is no mode and no flag: how many laps a feature takes is a description of how it went.
|
|
32
|
+
- **Size and certainty are orthogonal.** Mapping (§3) is for ideation too big to *think* through in one session; laps are for a feature too uncertain to *spec whole*. A feature can need both, one, or neither — a small but fuzzy feature gets a thin lap 1 and no map; a big but settled one gets a map and still merges on lap 1.
|
|
31
33
|
|
|
32
34
|
Do not move on until the human confirms you have reached shared understanding.
|
|
33
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: revisit
|
|
3
|
-
description: Fold late-arriving information into a feature whose sessions are finished — amend the docs, then reconcile tickets (update/cancel/emit)
|
|
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
4
|
disable-model-invocation: false
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -8,6 +8,8 @@ disable-model-invocation: false
|
|
|
8
8
|
|
|
9
9
|
The human came back: they remembered a constraint, changed their mind, or learned something that the grilling/spec didn't capture. This session's job is to make the **record** and the **ticket queue** match the new reality — nothing more. The pipeline does not move; whatever phase the feature is in, it stays in.
|
|
10
10
|
|
|
11
|
+
That is the ordinary revisit, and it is what the moves below describe. The one exception is a **lap**: a Rethink from review opens this same session with a lap kickoff and a bigger job — see **Lap mode** below, and work that section instead.
|
|
12
|
+
|
|
11
13
|
## Order of operations
|
|
12
14
|
|
|
13
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.
|
|
@@ -23,18 +25,26 @@ The human came back: they remembered a constraint, changed their mind, or learne
|
|
|
23
25
|
- `done` work now wrong → emit a NEW ticket that corrects it. Never touch done/burning tickets.
|
|
24
26
|
5. **Close.** `mcp__runcastle__record_event({ type: "feature.revisited", message: "<one-line gist of what changed>" })`, then tell the human what state you left things in — especially whether a re-Burn is needed (pending tickets exist and the feature is at `implementation`).
|
|
25
27
|
|
|
26
|
-
##
|
|
28
|
+
## Lap mode (a Rethink)
|
|
29
|
+
|
|
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
|
+
|
|
32
|
+
1. **Digest what the drive taught.** Your inputs are the previous lap's section of `docs/features/<slug>/test-notes.md` and the spec's `## Later laps` section, both injected into your context. **Both may be absent** — that is not an error and not something to go hunting for: interview the human from scratch instead. What did they hit, what surprised them, what do they want instead now they've used it?
|
|
33
|
+
2. **Context.** `mcp__runcastle__get_feature_context` — the feature (including its `lap`), the docs, and the full ticket history across every lap. Skim what the last lap actually landed and what failed before you interview.
|
|
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
|
+
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
|
+
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`.
|
|
37
|
+
6. **Emit this lap's tickets.** `mcp__runcastle__emit_tickets({ tickets })` — only the work this lap will burn; the rest stays in `## Later laps`. Reconcile stale pending tickets with `update_ticket` / `cancel_ticket`; `done` work that is now wrong gets a NEW ticket, as always.
|
|
38
|
+
7. **Advance the pipeline.** `mcp__runcastle__complete_phase` through `ideation` → `spec` → `tickets`, here, without opening another session. It will refuse to cross into implementation — that gate is the human's Burn click, which is the point.
|
|
39
|
+
8. **Hand to Burn.** Tell the human in a line or two what this lap does, then:
|
|
27
40
|
|
|
28
|
-
|
|
41
|
+
> Lap `<n>` is specced and carded. Review the ticket cards and click **Burn** — I'll stop here.
|
|
29
42
|
|
|
30
|
-
|
|
31
|
-
2. **Interview about the test drive.** Ask what they hit — bugs, rough edges, missing polish, follow-on tweaks. One concrete change per fix ticket.
|
|
32
|
-
3. **Emit fix tickets.** New work → `emit_tickets`. If a just-burned ticket was wrong, emit a NEW fix ticket (never reopen `done` work). Reconcile stale pending tickets with `update_ticket` / `cancel_ticket`.
|
|
33
|
-
4. **Hand back to Burn.** Do NOT advance the phase. When the cards are ready, tell the human to review them and click **Burn** — burning from review loops the feature back through implementation, and the run returns it to review when it finishes. The loop repeats until they click Merge & ship.
|
|
43
|
+
If the rethink turns out to be genuinely big — whole branches of design reopened, decisions hanging on material nobody has read — say so and escalate the way ideation would (`/runcastle:ideate` §3, the map), rather than grinding it out here.
|
|
34
44
|
|
|
35
45
|
## Do NOT
|
|
36
46
|
|
|
37
|
-
- **Never call `complete_phase
|
|
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.
|
|
38
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.
|
|
39
49
|
- **Never implement changes.** Code changes ride tickets; the burner (or a human) implements them.
|
|
40
50
|
|