runcastle 1.0.23 → 1.0.24
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 +422 -57
- package/index.js +422 -57
- package/package.json +1 -1
- package/pty-host.cjs +5 -1
- package/skills/burner/implement-ticket.md +32 -3
- package/skills/burner/resolve-conflict.md +10 -1
- package/web/assets/{index-BcWKL2hj.js → index-Cpwb7Nqn.js} +2 -2
- package/web/assets/{index-fWdYT4eC.css → index-DBVAMsFt.css} +1 -1
- package/web/index.html +2 -2
package/package.json
CHANGED
package/pty-host.cjs
CHANGED
|
@@ -91,7 +91,11 @@ function handleSpawn(msg) {
|
|
|
91
91
|
useConpty: opts.useConpty !== false,
|
|
92
92
|
})
|
|
93
93
|
} catch (e) {
|
|
94
|
-
|
|
94
|
+
// Name the file we were asked to run. node-pty's own message for a failed
|
|
95
|
+
// PATH search is `File not found: ` with the *resolved* path appended — i.e.
|
|
96
|
+
// empty, exactly when it matters — so without this the server sees a spawn
|
|
97
|
+
// failure with no indication of what it tried to spawn.
|
|
98
|
+
send({ t: 'error', message: `spawn failed for "${msg.file}": ${e && e.message}` })
|
|
95
99
|
process.exit(1)
|
|
96
100
|
return
|
|
97
101
|
}
|
|
@@ -8,14 +8,27 @@ You are a single agent in a sandbox on branch `feature/<slug>`. You have **one t
|
|
|
8
8
|
|
|
9
9
|
You run **non-interactively** (`claude --print`), for up to a few fresh iterations against the same worktree:
|
|
10
10
|
|
|
11
|
-
- **Ending your turn ends your process.** There are no background-task completion notifications in print mode — a "the notification will re-invoke me" plan never fires. Never end your turn to wait on a background command; run long commands (dependency installs, full test suites) in the foreground with a generous timeout, or poll a backgrounded command to completion *within* the same turn.
|
|
11
|
+
- **Ending your turn ends your process.** There are no background-task completion notifications in print mode — a "the notification will re-invoke me" plan never fires. Never end your turn to wait on a background command; run long commands (dependency installs, full test suites) in the foreground with a generous timeout, or poll a backgrounded command to completion *within* the same turn. **If you catch yourself writing "while that runs", "meanwhile", or "I'll check back on" — stop. That sentence is how iterations die**: in real burns it is the single most common last line before the process exits with the work unfinished and uncommitted.
|
|
12
|
+
- **A next iteration is not a free retry.** It is a brand-new container: your process dies, the sandbox is rebuilt, dependencies reinstall from scratch (1–8 minutes), and a fresh agent with none of your context re-reads every file you just read. Budget roughly ten wasted minutes per iteration you burn. Finishing in one is worth real effort.
|
|
12
13
|
- **You may not be the first iteration.** A previous iteration may have left commits or uncommitted work on this branch. Check `git status` and `git log` before starting; continue the work, don't redo it.
|
|
13
14
|
- **Signal completion.** When the ticket is fully done — every acceptance criterion verified, self-review finished, all work committed — print exactly `<promise>COMPLETE</promise>` as the last line of your final message. Do the same after writing `BLOCKED.md` (see hard rules). Without the signal, the harness assumes you were cut off and spends another iteration.
|
|
15
|
+
- **Run the repo's test command as the repo defines it.** Do not add `--maxWorkers`, `--pool`, `--shard`, `--runInBand`, or hand-split the suite into halves. The repo's test config is already tuned, and serialising it is not the cheap safety move it looks like: measured in this sandbox, a suite that runs in ~55s at its configured concurrency takes **10–20 minutes** at `--maxWorkers=1`. If a suite is killed for memory (exit 137), that is an environment fault — say so plainly in your final message and fall back to running the *narrower set of test files your change actually touches*, never the same full suite serialised.
|
|
14
16
|
|
|
15
17
|
## Where to work
|
|
16
18
|
|
|
17
19
|
{{WORKSPACE_NOTES}}
|
|
18
20
|
|
|
21
|
+
## Use your file tools, not the shell
|
|
22
|
+
|
|
23
|
+
You have `Read`, `Grep`, `Glob`, `Edit`, and `Write`. **Use them.** Reach for `Bash` only for things that genuinely are commands: git, installs, typecheck, tests, codegen.
|
|
24
|
+
|
|
25
|
+
This is not a style preference — it is one of the largest measured costs in real burns, where agents ran **1641 Bash calls and not one file tool**:
|
|
26
|
+
|
|
27
|
+
- **Reading.** `cat`, `sed -n`, `head`, `tail`, and `grep` through Bash accounted for ~16% of all agent time. `Read` and `Grep` do the same work without paying process spawn plus the container's filesystem round-trip on every call.
|
|
28
|
+
- **Editing.** Rewriting a file by piping a `python3 - <<'PY' … s.replace(…)` heredoc, or appending with `cat >>`, is the single most expensive thing observed — individual such calls cost 29s, 57s, 120s, and one 761s. `Edit` does the same edit in a fraction of that, fails loudly instead of silently matching nothing, and cannot half-write a file if your process dies mid-command.
|
|
29
|
+
|
|
30
|
+
If you catch yourself writing a heredoc to edit a file, stop and use `Edit`.
|
|
31
|
+
|
|
19
32
|
## The ticket
|
|
20
33
|
|
|
21
34
|
```json
|
|
@@ -30,6 +43,16 @@ You run **non-interactively** (`claude --print`), for up to a few fresh iteratio
|
|
|
30
43
|
|
|
31
44
|
{{DOCS_DIGEST}}
|
|
32
45
|
|
|
46
|
+
## How to verify
|
|
47
|
+
|
|
48
|
+
{{VERIFY_NOTES}}
|
|
49
|
+
|
|
50
|
+
Whatever the commands are, spend them well — a full suite on a monorepo is minutes of your budget, and in real burns re-running one was the largest single waste:
|
|
51
|
+
|
|
52
|
+
- **Capture, then read.** Redirect a full run to a file (`<command> > /tmp/test-run.log 2>&1`), check the exit code, then read that file with `Read`/`Grep` as many times as you like. Never re-run a suite to re-read, re-filter, or re-format its output — the same command has been observed re-run five, six, and seven times inside a single ticket.
|
|
53
|
+
- **Run the whole thing rarely.** Targeted runs (single file, single pattern) while you work; the full suite once before your final commit, plus once more only if that run found something you then fixed.
|
|
54
|
+
- **Never `git stash` to get a clean-tree comparison.** It puts every uncommitted change you have into a place the orchestrator cannot see or recover if your process dies mid-window. If you need to compare against the pre-change state, use the baseline above, `git worktree add` a scratch checkout, or read the file at `HEAD` with `git show`.
|
|
55
|
+
|
|
33
56
|
## How to work
|
|
34
57
|
|
|
35
58
|
1. **Orient.** Read the ticket's `goal`, `context`, `acceptanceCriteria`, and `seams`. Read the files its `context` names and the existing patterns it points to. Match the conventions of the surrounding code — you are extending this codebase, not starting a new one.
|
|
@@ -38,11 +61,13 @@ You run **non-interactively** (`claude --print`), for up to a few fresh iteratio
|
|
|
38
61
|
|
|
39
62
|
3. **Red → green, one criterion at a time.** Where a test framework exists in the repo: for each acceptance criterion, write the failing test at the seam first, then the minimal code to pass it, then move on. One slice per cycle — do not bulk-write tests for imagined behaviour, and do not recompute the expected value the way the code does (use known-good literals). Where the repo has **no** test framework: verify each criterion by actually running the code / driving the demoable path, and state what you ran.
|
|
40
63
|
|
|
41
|
-
4. **
|
|
64
|
+
4. **Commit early, commit often — an uncommitted slice is a slice you can lose.** A commit is the *only* thing that survives your process ending. Everything else — edited files, a passing test you have not committed, an hour of work — is discarded the moment the turn ends, and the next iteration starts from your last commit as if the rest never happened. Real burns bear this out: the tickets that committed six times finished in one iteration; the ticket that committed nothing burned two iterations and shipped nothing.
|
|
65
|
+
|
|
66
|
+
So: **the moment a slice is green, commit it.** Do not save commits up for a tidy end-of-ticket batch, do not wait for the next criterion, do not let more than about ten minutes of work sit uncommitted. Before each commit run typecheck and the *relevant* tests — **never commit red** — then commit using the convention:
|
|
42
67
|
|
|
43
68
|
`{{COMMIT_CONVENTION}}`
|
|
44
69
|
|
|
45
|
-
|
|
70
|
+
A half-done ticket with four green commits is a good outcome the next iteration can finish. A nearly-done ticket with zero commits is a total loss.
|
|
46
71
|
|
|
47
72
|
5. **Self-review before you finish (forked code-review — two axes).** When all acceptance criteria pass, review your own diff along both axes, then fix what you find and re-run typecheck + tests:
|
|
48
73
|
- **Standards** — does the diff follow the conventions of the surrounding code? Watch for the smells: duplicated logic (extract it), mysterious names (rename), primitive obsession (give the concept a type), speculative generality (delete anything the ticket did not ask for), feature envy, data clumps.
|
|
@@ -51,6 +76,10 @@ You run **non-interactively** (`claude --print`), for up to a few fresh iteratio
|
|
|
51
76
|
|
|
52
77
|
## Hard rules
|
|
53
78
|
|
|
79
|
+
A few of these are enforced by a tool hook, not just stated here: `git stash`, test-runner concurrency flags, and rewriting files through interpreter heredocs are **denied** before they run. A denial is policy, not a broken environment — read its reason, take the alternative it names, and carry on. Do not try to route around it.
|
|
80
|
+
|
|
81
|
+
- **Never end your turn with uncommitted green work.** If you are about to stop for any reason — done, blocked, unsure, out of room — commit what is green first. This rule outranks tidiness: a scrappy commit beats losing the work.
|
|
82
|
+
- **Never `git stash`.** See "How to verify" — stashed work is invisible to the orchestrator and unrecoverable if your process dies.
|
|
54
83
|
- **Never expand scope beyond the ticket.** If you notice adjacent work, worthwhile refactors, or another ticket's territory, leave it. Note it in your final commit body if it matters; do not do it.
|
|
55
84
|
- **No questions, no guessing into the void.** Resolve ambiguity from the ticket context and the code. If two readings both satisfy the acceptance criteria, take the smaller one.
|
|
56
85
|
- **If genuinely blocked** (a dependency ticket's output is not there, the environment fails, or a requirement truly cannot be resolved from context + code): **commit nothing for the blocked part**, write `BLOCKED.md` at the repo root stating precisely what blocked you and what is needed to unblock, print `<promise>COMPLETE</promise>`, and stop. Green, complete parts may still be committed; the blocked part must not be. The orchestrator reads your exit state from the commits — a part with no commits is read as not done.
|
|
@@ -12,8 +12,11 @@ There is **no human to ask** — everything you need is in this prompt and in th
|
|
|
12
12
|
|
|
13
13
|
You run **non-interactively** (`claude --print`), for up to a few fresh iterations against the same worktree:
|
|
14
14
|
|
|
15
|
-
- **Ending your turn ends your process.** There are no background-task completion notifications in print mode — a "the notification will re-invoke me" plan never fires. Run long commands (test suites) in the foreground with a generous timeout.
|
|
15
|
+
- **Ending your turn ends your process.** There are no background-task completion notifications in print mode — a "the notification will re-invoke me" plan never fires. Run long commands (test suites) in the foreground with a generous timeout. If you catch yourself writing "while that runs" or "meanwhile", stop — that sentence is how an iteration dies mid-merge.
|
|
16
|
+
- **An unfinished merge does not survive you.** Resolved-but-uncommitted files are discarded when your process ends, and the next iteration restarts the merge from scratch. Once the conflicts are resolved and the tree is sane, land the merge commit — then verify and fix forward on top of it.
|
|
16
17
|
- **Signal completion.** When the merge commit is in, print exactly `<promise>COMPLETE</promise>` as the last line of your final message. Do the same after writing `BLOCKED.md` (see hard rules).
|
|
18
|
+
- **Run the repo's test command as the repo defines it** — no `--maxWorkers`, `--pool`, `--shard`, or `--runInBand`. Serialising a suite has been measured at 10–20 minutes for work its configured concurrency does in under a minute.
|
|
19
|
+
- **Use `Read`, `Grep`, and `Edit`** for files; keep `Bash` for git, typecheck, and tests. Reading with `cat`/`sed` and editing with `python3` heredocs is among the largest measured time sinks in real burns.
|
|
17
20
|
|
|
18
21
|
## Where to work
|
|
19
22
|
|
|
@@ -59,10 +62,16 @@ These commits landed on `{{FEATURE_BRANCH}}` while you were working — this is
|
|
|
59
62
|
|
|
60
63
|
4. **Watch for semantic conflicts, not just textual ones.** Files that merged cleanly can still be broken by the other side's rename, signature change, or moved export. After resolving the marked hunks, run typecheck and the tests covering both sides' touched code, and fix what the merge broke.
|
|
61
64
|
|
|
65
|
+
Verify with these commands, and spend them carefully — capture a full run to a file (`<command> > /tmp/verify.log 2>&1`) and grep that file rather than re-running the suite to re-read it. Never `git stash` mid-merge: it is unrecoverable if your process dies, and it will strand the merge state.
|
|
66
|
+
|
|
67
|
+
{{VERIFY_NOTES}}
|
|
68
|
+
|
|
62
69
|
5. **Complete the merge.** Stage the resolved files and commit — keep it a merge commit (`git commit --no-verify`, do not `--squash`, do not rebase, do not amend your earlier commits). A merge commit whose message names what was reconciled is ideal.
|
|
63
70
|
|
|
64
71
|
## Hard rules
|
|
65
72
|
|
|
73
|
+
`git stash`, test-runner concurrency flags, and interpreter-heredoc file rewrites are **denied by a tool hook** before they run. A denial is policy — read its reason, take the alternative it names, and carry on.
|
|
74
|
+
|
|
66
75
|
- **Never expand scope.** Resolving the conflict is the whole job. No refactors, no improvements, no adjacent fixes, not even tempting ones in the files you are already editing.
|
|
67
76
|
- **Never lose committed work.** Not yours, not theirs. `git merge --abort` + "start over from one side" is not a resolution.
|
|
68
77
|
- **Never push, and never touch the remote.** The orchestrator lands your branch itself once the merge commit exists.
|