worktreereview
Skill3 starsChanged 6 days agoApache-2.0View on GitHub
Review one finished or running worktree build (a Builder or any agent that worked in an Orca worktree) and produce an HTML report of where the time and tokens went, stage by stage, with lessons. Use when the owner says "/worktreereview <unit>", "analyze the <unit> build", "break down that build", or asks what a Builder run cost.
The file
Show its rules and text
We split this skill into the rules it gives an agent
15 rules. About 0 are common, 8 are distinctive, and 7 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈8
Distinctive · estimate
This author's own choices. Worth a look.
7
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 15
Only when the owner asks: /worktreereview <unit>, for a merged build or one still running.
- Very specific: line 34
every Codex session record whose working folder is the worktree (~/.codex/sessions).
- Very specific: line 50
Split the Build stage when it covers several jobs: Read notes.txt (the agent's own progress notes with UTC times) and find where it moved …
- Very specific: line 59
Read what the numbers cannot tell you: If the worktree still exists, read its evidence file (.claude/briefs/<unit>-evidence.md), above all …
- Very specific: line 78
Every figure you quote in a lesson must match the page's tables.
- Distinctive: line 116
Every page carries them, computed, with no words needed from you:
- Distinctive: line 142
Every time: is there a library, a shared helper or a cached step that would significantly speed up builds like this one?
- Distinctive: line 163
Always include what the report does not measure: what the plan really bills, hook time per tool call, bugs reopened after merge.
- Distinctive: line 181
Put the drafts to the owner one at a time, each with its details, and wait for an answer to each.
- Distinctive: line 185
No step of this skill and no script files an issue from a lesson without that approval.
- Distinctive: line 189
When the owner asks what became of past lessons, read the lessons of the filed review pages and, for each lesson, say which of these it is:
- Distinctive: line 201
Use this when the session asked for the review is busy with other work and its context is large (an orchestrator, for example): the words …
- Very specific: line 205
Run collect (steps 1 to 3), then copy writer-handoff.md (beside this file) to the scratch folder and fill in every <...>: the inputs, the …
- Very specific: line 215
When it ends, read run.json for its result and cost, check the page's figures against its tables, and carry on from step 7.
- Distinctive: line 226
Only a hook that prints something leaves a log row, so the per-hook table is a floor.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Worktree review
Turns one worktree's logs into a report the owner can read: a clock chart, a stage table with list prices, a drilldown per stage, tokens and cost by model, the Codex passes, and what the reviewer makes of it. The two scripts do every number; you write only the words.
When it runs
Only when the owner asks: /worktreereview <unit>, for a merged build or one still running. The merge
step does not run it, and nothing else starts it (the owner, 2026-10-14).
The page of computed numbers alone, with no words from you, is one command that uses no model:
python3 ${CLAUDE_PLUGIN_ROOT}/skills/worktreereview/scripts/review.py <worktree path> --no-openIt collects, renders, files the page under
~/.commandeck/reviews/<date>/<project>/<unit>-review.html and prints that path as its page: line.
The page flags a build past a threshold. Defaults: more than 30 minutes of Codex review in total;
more than a third of the clock on tests or idle waits when those also come to more than 10 minutes, the
flag naming the largest single command; more than 30 US dollars at list price (--max-review-min,
--max-test-share, --max-usd; the owner's answers at setup, saved as reviewLoop in the project
config, replace these defaults). A wait on a review pass run outside the Builder's session (the owner
ran it) or on a review tool that was down has its own slice of the clock and counts toward no flag.
It reads logs only, so it works after main has removed the worktree:
- the Claude session logs filed under the worktree's path (
~/.claude/projects/<path with - for />), - the helper logs beside them,
- every Codex session record whose working folder is the worktree (
~/.codex/sessions).
Steps
- Find the worktree path. It is
~/orca/workspaces/<project>/<unit>. If unsure, list~/.claude/projects/for the unit name. The folder need not exist any more. - Collect.
It prints the stages, totals, clock split, Codex passes and guard refusals, and writespython3 ${CLAUDE_PLUGIN_ROOT}/skills/worktreereview/scripts/collect.py <worktree path>result.jsonandnotes.txtto/tmp/commandeck/worktreereview/<project>/<unit>/. Stages are automatic: Build, then each Codex pass and the fixing between passes, then Wrap-up. Planning (a seat tag or spec name that says plan) before the Builder starts isPlan and judge, ahead of Build; its tokens and cost stay in that stage. - Split the Build stage when it covers several jobs. Read
notes.txt(the agent's own progress notes with UTC times) and find where it moved from one issue to the next and where building turned into its own checks. Run collect again with one--splitper boundary:
A split within a minute of the start renames the first stage. Say in the report that the boundaries are yours.--split "21:22:54=Build the two slow hooks" --split "22:08:11=Own checks before review" - Read what the numbers cannot tell you. If the worktree still exists, read its evidence
file (
.claude/briefs/<unit>-evidence.md), above all "Not done / not checked". Read the Codex findings inresult.json(passes[].message). A pass withtool_gapstrue had a reviewer say Serena was unavailable, timed out or could not index, so its verdict may rest on text search alone; the page notes it under the pass. Check whether main has merged it (git logon main, read-only) and what main said in the inbox. - Write
notes.jsonnext toresult.json. Keys:title,summary,stage_notes(by stage code),pass_findings(by pass number, one plain sentence each),lessons(title and points),not_measured,method, andshrink_review: one entry for each "Where it could shrink" item, by its bold title, marked covered by an open issue ({"item": "Long thinking", "issue": "#12"}, citing it) or uncovered with a one-line proposed fix ({"item": "Long thinking", "fix": "..."}). The page lists the marks and names any item left unmarked. - Render and check.
Every figure you quote in a lesson must match the page's tables. Recompute, do not recall. The page computes the "Where it could shrink" items, so the first render may list some under "Not reviewed yet": add apython3 ${CLAUDE_PLUGIN_ROOT}/skills/worktreereview/scripts/render.py <result.json> <notes.json> <out.html>shrink_reviewentry for each and render again. - File it where your role files reports for the owner and add the index line.
- Open it in Orca, in the worktree the owner reads reports in, as a rendered page:
Confirm withorca-ide tab create --url "file://<absolute path to the html>" \ --worktree "path:<that worktree>"orca-ide tab list --worktree "path:..." --jsonthat a tab carries the report's title. (orca-ide file openonly takes files inside the worktree and shows the source, not the page.) Then give the owner the clock split, the cost and the lessons in a few lines.
Time waiting on the owner is cut
Time a Builder sat waiting on the owner is not build time (the owner, 2026-10-14). collect.py cuts it
from every clock figure, share and flag, and leaves cost as it is: each --exclude START=END window
(ISO UTC, repeatable; review.py passes it through), and the part of each idle gap over two minutes with
nothing of the build running that sits inside an open decision in the main checkout's
.claude/metrics/decisions.jsonl that names the unit in its source or answer, or whose id is exactly <unit>, plan-<unit> or review-stop-<unit>-r<digits> (the last row per
decision and ask wins). A gap waiting on main or on nothing with no decision recorded is cut whole, and its
reason names the cause. Idle after the Builder's <UNIT>-DONE line is not cut by itself: with no owner
decision open it stays on the clock, as does time waiting on a merge. A gap with any session's background
job or a Codex run in it is kept, and a found window cuts only idle seconds. Overlapping windows cut
each second once. Once the worktree is removed nothing proves which checkout it came from: pass --main <main
checkout> (review.py passes it through), or no decision and no round archive is read. Still counted, and the page says so: a helper's request and tool subtotals, and
command seconds inside a cut Codex pass. The page and result.json (cut, cut_s) list every window with its start, end,
length and reason.
A hold main recorded with unit-hold.sh <unit> hold|release '<reason>' is cut the same way, reason hold,
except seconds a Codex run or background job covers. A stretch with no session open, no Codex run and no hold, as
long as an owner wait or longer, is listed in an "Unexplained idle" box (result.json: unexplained,
unexplained_s) and is not counted in any stage.
The two timing sections
Every page carries them, computed, with no words needed from you:
- Time off Claude's servers: tools, hooks, Codex and waiting. What it was, commands grouped by the script they ran, hook time before and after each tool call, each logged hook, each idle wait, the inside of each Codex pass, and every tool call in a sortable table.
- Time on Claude's servers, request by request: the wait before the model began, thinking and writing; by what the request did, by stage, by context size and by reply size; the slowest requests; every request in a sortable table; and "Where it could shrink", a computed list (single looks sent one at a time, the fixed cost of a round trip, long thinking, long replies, advisor consults, cache writes, repeated commands, time-limit kills, refusals).
Each "could shrink" figure is a ceiling the code worked out, not a ruling. Use them as leads: sort the tables, check the rows, and only then write a lesson.
What to look for in the lessons
- The largest slice of the clock, and whether one test or command explains it
(
background_jobsand the idle gaps name them). - Codex passes: how many, findings per pass, and whether several findings were one flaw fixed piecemeal. A pass with several threads started reviewers of its own.
- Whether work was split or batched, and what the split cost or saved.
- Calls times context: the agent's cost follows the number of model calls and the context each one re-reads. Note compactions and the size they fired at.
- Any tokens paid at full price: advisor calls send the context uncached.
- The dearest stage and why: open its drilldown (
stages[].detailholds its clock split, longest commands with their results, files edited, guard refusals and the agent's notes). - Faults in the tooling the run exposed, and what the agent left for main.
- Every time: is there a library, a shared helper or a cached step that would significantly speed up builds like this one? Answer it in a lesson, yes with what, or no.
The computed sections beyond timing
extras.py adds these to every page, with no words needed from you:
- What filled the context: estimated tokens by source, the largest items, content that entered twice, and caches a pause let expire. Thinking is what a request wrote beyond its text and tool calls, because the log keeps only its signature. The page prints the margin against the measured peak; it does not cover helpers. Its leads join "Where it could shrink".
- Main's share: main's and the Chief of Staff's requests during the build, as a floor (naming the unit) and a ceiling (the whole window).
- What each review found: findings per pass, repeats (a file or a
(class: <tag>)an earlier pass cited), findings the evidence likely rejected, and list price per fresh finding. - After the merge: later fixes and reverts in the build's files. Run
collect.pyagain days later to fill it; the page made at merge shows none yet.
Rules for the words
- Plain English, the point first. Explain a term or use a normal word.
- Tag anything you did not check as unverified or likely. Never guess a cause the logs do not show.
- Always include what the report does not measure: what the plan really bills, hook time per tool call, bugs reopened after merge. Main's share is a floor and a ceiling, never one figure.
- Dollars are list-price equivalents, priced call by call with the repo's one price table
(
scripts/dashboard_limit.py, the dashboard's own). Say "list price" every time. A model with no price there (a Codex name with no known tier) is unpriced: never guess a rate, and name what the total leaves out. A new Codex version of a known tier takes the tier's price; say that part is priced by tier, not by exact version. A response whose speed tier nothing recorded is an estimate. - Name each model by its name (Opus 5.5, Fable 5.1, Haiku 5.5), never only by its role.
- Helper output tokens are under-recorded in the logs; say so if you quote them.
- A lesson that proposes a change is your view. Say so, and do not present it as a ruling.
- Refer to a lesson as
<unit> L<n>: the page numbers its lessons L1, L2, ... in the order ofnotes.json.
A lesson becomes an issue only with the owner's approval
- Each lesson that proposes a change, and each "Where it could shrink" item the page marks
uncovered, becomes a draft issue, never an issue: the repo, a title, a body of 2 to 4 lines, the
labels, and the source lessons (
<unit> L<n>) or item. - Put the drafts to the owner one at a time, each with its details, and wait for an answer to each.
- File only the drafts the owner approves, as approved. A draft the owner declines or does not answer is not filed.
No step of this skill and no script files an issue from a lesson without that approval.
Sweep over past lessons (on demand)
When the owner asks what became of past lessons, read the lessons of the filed review pages and, for each lesson, say which of these it is:
- done: an issue closed or a commit made the change; name it.
- filed: an open issue holds it; name it.
- nothing yet.
Group the lessons that say the same thing on several pages as one, with each source lesson. For the "nothing yet" ones, make draft issues as above, ordered by what the lessons say they cost (the largest first), and put them to the owner one at a time.
Writing the lessons page in a separate writer session
Use this when the session asked for the review is busy with other work and its context is large (an orchestrator, for example): the words cost far less in a fresh session, which does not carry that large context.
- Run collect (steps 1 to 3), then copy
writer-handoff.md(beside this file) to the scratch folder and fill in every<...>: the inputs, the facts the logs do not show (step 4), and the lesson numbering. - From a new empty folder, launch the writer in the background:
claude -p "Read <scratch>/writer-handoff.md and do exactly what it says." --model <opus model> \ --setting-sources "" --disable-slash-commands --strict-mcp-config --no-session-persistence \ --allowedTools "Read,Write,Edit,Bash" --add-dir <scratch> ${CLAUDE_PLUGIN_ROOT}/skills/worktreereview \ --output-format json > <scratch>/run.json - When it ends, read
run.jsonfor its result and cost, check the page's figures against its tables, and carry on from step 7.
Known limits
- The clock's "tests" slice is found by matching each command's description against
--test-regex. Check a few of the longest commands before trusting a surprising number. - A worktree reused for a second agent (a fix agent after the first) shows as two sessions;
the time between them is "No agent session open". Use
--session <id prefix>for one alone. - A worktree's own
builder-events.jsonlover-counts refusals; the report uses the session log instead. - Only a hook that prints something leaves a log row, so the per-hook table is a floor. The hook time before and after each tool call is read from row times and is whole.
- A request starts at the last log row before its first block. The client's own API counter runs higher: it also counts helper agents, compaction and small background calls.
- The permission check before a tool runs sits inside "the tool itself"; it is not split out.
- The closing-pass field lists the passes after the closing one, but not a merge done by hand:
merge-unit-local.shrecords only its own merges. Checkgit logon main (step 4) for a merge of the unit after its closing pass, and say so in the report.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

