← Blog
10 min read

Eleven Skills and the Rule That Fires Them

Writing a skill for a coding agent is the easy half. The hard half is the policy file that decides when it fires, the artifact directories that let one stage hand off to another, and a scanner that measures what all of it costs.

Everybody working seriously with a coding agent ends up writing skills for it. A skill is a folder with a markdown file in it that says here is how to do this particular kind of work, and writing one is genuinely easy, and about a week later you have eleven of them and the agent is using approximately none of them unless you remember to ask.

That is the actual problem, and it is not a prompt-engineering problem. A skill that fires when you remember to invoke it is a bookmark. What turns a pile of skills into a harness is three things that sit underneath them: a policy file that says when each one fires without being asked, a set of directories on disk that let one stage hand off to the next, and a scanner that measures what the whole arrangement costs you in tokens.

This is a tour of mine. The plumbing that installs it — the symlinks, the generated settings, the shared cross-device memory — is written up separately on the project page; this post is about the skills themselves and how they connect.

The eleven

They fall into three groups, and the groups matter more than the names.

Foundational — these apply inside other work rather than being work of their own. kiss forces the simplest thing that actually works, climbing a ladder from "does this need to exist at all" through "is it already in this codebase" and stopping at the first rung that holds. tdd runs Red→Green→Refactor with verification gates. scientificmethod makes a falsifiable hypothesis, states a prediction, runs a real experiment, and records the verdict — so that an untested guess never gets presented as a fact.

The pipelineinvestigate turns a thin ticket, or a bare question, into a brief. plan turns the brief into dependency-ordered work streams. orchestrate turns the plan into running agents and drives each one to a merged review.

Cross-cuttingtemporal-planning reasons in calendar time rather than effort, because most agents estimate "two hours of work" and then behave as though the task finishes in two hours, even when the change will sit in a review queue until Thursday. model-routing picks the model and effort level at dispatch from a static, arguable table. optimizer turns measured context waste into deterministic tooling. adversarial-review attacks an artifact before it goes out. wikikb maintains knowledge bases that compound instead of evaporating.

Two more get named in the policy but do not ship with the skills, and the distinction is worth keeping straight: code-review is a Claude Code built-in, and taskforge is a CLI on PATH, not a skill at all — asking for it as one just returns "Unknown skill."

investigate
Turn a thin ticket — or a bare question — into an implementation-ready brief.
.investigations/
plan
Break it into shippable streams, ordered by dependency, with a Gantt view.
.plans/
orchestrate
A durable worker per stream, driven to a merged review.
.orchestration/
while the work is being donekisstddscientificmethodcode-review
across the whole pipelinetemporal-planningmodel-routing
before anything is deliveredadversarial-review
The three stages hand off through directories on disk, not through conversation. Each one writes an artifact the next one reads, so a stage can be resumed by a session that was not there when it started.

The handoff is a directory, not a conversation

The thing I would change about most agent setups is that the stages talk to each other through the transcript. That works until the session ends, and then the context is gone and the next session reconstructs it badly.

Here each stage writes a file. investigate leaves a brief in .investigations/; plan reads that and leaves a dependency graph in .plans/; orchestrate reads that and keeps a status board in .orchestration/. temporal-planning puts wall-clock estimates in .timelines/. All four directories are gitignored, because they are per-machine working state rather than configuration.

The payoff is resumability. A plan written on Tuesday by a session that no longer exists is still a file, and a session on Thursday can pick it up without me re-explaining anything. It also means the reasoning is auditable — when a stage gets something wrong I can read what it believed at the time, instead of guessing.

The part that actually makes it work

Skills are inert without something to fire them. The layer that does that is a single rules file which loads into every session, and states for each skill whether it is a MUST or a SHOULD and what the trigger is.

MUSTkissany coding task at all
MUSTtddcode that changes observable behaviour
MUSTcode-reviewbuilt-inbefore every commit that changes code
MUSTscientificmethodthe cause or the best option is not already known
MUSTinvestigate"look into", "figure out", "why does…" — the phrasing, not the ticket
SHOULDplanwork needs splitting or sequencing
SHOULDorchestrateseveral streams running at once
SHOULDtemporal-planninga step takes real elapsed time
SHOULDmodel-routinga stream is about to be dispatched
SHOULDoptimizerthe ledger flags a recurring hotspot
SHOULDwikikbknowledge worth compounding past the task
SHOULDtaskforgeCLIwork that outlives the session
SHOULDadversarial-reviewanything someone will act on

Eleven of these are packaged skills. The two marked built-in and cli are not — they are governed by the same policy file without shipping with it.

Precedence — an explicit instruction from me overrides the defaults for that turn. tdd outranks kiss on tests, without exception: kiss governs how big the solution is, never whether its tests exist.

The policy file loads every session, which is what turns a skill from something I invoke into something that applies. MUST means it fires unless I say to skip; SHOULD means it fires unless there is a reason not to.

A few of these earn their place by being annoying.

investigate is triggered by how I phrase a request, not by whether a ticket exists. If I say "look into", "dig into", "figure out", or "why does…", the skill runs and leaves a brief — and the rule explicitly refuses the obvious escape hatches: not because the answer looks obvious, not because it is only one file, not because the context is already loaded. An answer that felt obvious and was wrong is the exact failure that rule exists to prevent.

code-review is a MUST before every commit, and the reason is that there is no CI on these repos. Nothing compiles or tests anything unless a human runs it. A single review pass over one merged change found six defects, every one reproduced by mutation — a permission surface widened four-fold by glob expansion, a shell injection through a quoted directory name, and two assertions that could not fail under any input. None of it was visible to the test suite, because the tests were part of what was wrong.

Which is the general lesson underneath adversarial-review too: a passing suite proves the assertions pass, not that they assert anything. So that skill has a rule that a finding needs a concrete failure scenario or it gets discarded rather than softened into a nit, and a second rule that any verification in the artifact — a test, a monitor, a gate — is itself unverified until it has been made to fail on purpose.

Two tools that hold state

taskforge is where the work itself lives. It is file-backed task management for one human and several agents: markdown with YAML frontmatter, a web UI for people and a deterministic JSON-emitting CLI for agents, patch-based writes so an agent mutates one field rather than handing back a whole file with plausible corruption in the parts it was never meant to touch. It is open source: the project page has the design write-up, and the code is at github.com/aarontrank/taskforge.

The rule about it is less about creating tasks than about not lying. Before a session ends, the task's status has to match reality. An unfinished task is fine — in-review is a legitimate resting state. The failure is a task still marked running after the review was posted, because that is invisible to everyone and it makes every later report wrong.

wikikb is the other one, and it solves the problem where an agent researches something properly, tells you the answer, and the answer evaporates. It maintains real knowledge bases — sources kept immutably in raw/, written pages in wiki/, a schema per wiki saying what page types exist. The single most important detail is boring: the wiki home is at one fixed path that is the same on every machine. Let each session pick its own root and nothing ever compounds, which is the whole point.

It deliberately lives outside the config repo, because raw/ fills up with downloaded PDFs and images and those have no business in something you clone onto every machine.

Measuring what it costs

Everything above adds context, and context is not free, and the way it is not free is counterintuitive enough that I had to build something to see it.

A tool result is paid twice: once as output when it lands, and again as a cache read on every subsequent turn. So its real cost is its size multiplied by the number of turns that follow it. On one real session that turned 300,382 raw tool-result tokens into 83,194,646 amplified ones — a factor of 277.

SessionEnd hook
fires when a session closes
every session
scan.py
reads the transcript, writes findings — never content
~0 tokens
ledger.jsonl
ranked by amplified cost, not payload size
828 findings / 162 sessions
optimizer
designs a deterministic replacement and proves the saving
skill
known-alternatives.json
so the fix is reused rather than rediscovered
registry — still empty
registered fixes feed back into the scanner, which flags the expensive pattern automatically from then on
The measuring half runs on every session and costs no model tokens at all — it is Python reading a transcript. The registering half is built and not yet exercised: nothing has been entered in the registry, so the feedback edge is designed rather than proven.

Three things fall out of that, and all three are the opposite of the obvious move:

  • Early context is worth roughly 400× late context. A 3,708-token result landing at the start outranks an 18,601-token one landing near the end.
  • Always-loaded surface is the most expensive thing there is — tool schemas, skill bodies, steering files. They are paid from turn one, every turn. Which is an uncomfortable finding for a post about how many skills I have installed.
  • Frequency beats size. A poll loop re-reading a 7.5k payload six times costs more than one 40k read.

So ranking by payload size — the obvious approach — is simply wrong, and the scanner ranks by amplified cost instead. It runs as a hook when a session closes, it is Python reading a transcript, and it costs zero model tokens. Backfilling it across 162 real sessions produced 828 findings in 4.7 seconds.

Agents that outlive the wait

The last piece is about what happens when the work outlives the session that started it.

Most planned work is gated on a code review, and a code review is gated on a human being available. A sub-agent ends when it replies. So you dispatch a sub-agent, it does good work, it posts a review, it returns — and when feedback lands the next morning there is nobody holding the context and a fresh agent has to reconstruct everything.

The fix is a durable worker: a long-lived session in its own git worktree that stays alive across the whole review wait and every round of feedback, and that I can talk to directly rather than through the orchestrator. Sub-agents are still right for short, self-contained, ungated work. They are the wrong shape for anything a human has to approve.

depth 0
orchestrator
holds the status board; spawns, never relays
depth 1
worker
own git worktree
stream A
worker
own git worktree
stream B
worker
own git worktree
stream C
depth 2
sub-agent
short, ungated work only
one level of fan-out, and no further — a depth-2 child may not spawn at all. Depth is kept on disk, not in an inherited environment variable.
What stops it eating the machine — in the order that actually matters
1Budget governor$50 fleet, $11 per projected workerinferred, not measured
2Spawn stagger0.5 s between spawnsmeasured, at 0.5 s only
3Worker-count ceiling20 live sessionsmeasured to 20, short-lived only
A sub-agent ends when it replies, so review feedback landing a day later finds nobody holding the context. A durable worker is a session in its own git worktree that stays alive until the review is merged — and that I can talk to directly, without the orchestrator relaying.

The ordering of those three controls is the part worth arguing about, because it is not the order anyone reaches for. The instinct is to cap the number of workers. But every rate limit this machine has ever hit was a session limit or a monthly spend limit — cumulative-usage quotas, not throughput limits. A worker-count cap does not constrain cumulative spend at all; twenty workers just burn the quota twenty times faster. So the budget governor is the primary control and the count ceiling is the weakest of the three.

I want to be honest about the evidence there, because the skills insist on it and it would be strange to drop the habit here. The stagger and the concurrency ceiling are measured. The budget governor is inferred — reasoned from three real quota events plus a proven cost linearity, not from a ramp that ever actually reached a budget wall. The concurrency ramp that produced those numbers spent $2.84 of Haiku, roughly four orders of magnitude below where the real limits fire, so it could not possibly have detected them.

What it actually buys

The honest summary is that none of this makes the agent smarter. It makes it consistent, which is a different and more useful thing. The same investigation happens whether or not I remembered to ask for one. The tests get written before the code because a rule says so and not because I was in the mood. The expensive path gets measured rather than guessed at.

And the failure mode it protects against is not the agent doing something dramatic. It is the quiet one: a plausible answer, delivered confidently, that nobody checked — and then built on.