Chapter 5 of 7 · 9 min read
Cut anything that does not change the action, keep what pins a format
On this page
The api-pagination body is 60 lines and the review has come back with a budget: 30. It breaks into five blocks.
<!-- lines 1-7 -->
Our API guidelines were established in 2021 after several incidents involving
inconsistent pagination. Two services returned offset-based pages with unstable
ordering, and clients that paged through during a write saw duplicates. The team
believes cursor-based pagination is generally preferable for large collections.
<!-- lines 9-22: four steps -->
1. Return a `next_cursor` in the response envelope, null on the last page.
2. Accept `?cursor=` and `?limit=` (default 50, max 200).
3. Order by a stable, unique key. If the natural sort is not unique, append the
primary key as a tiebreaker.
4. Reject `?offset=` with 400 and the message `use cursor pagination`.
<!-- lines 24-50: three worked examples -->
### Example: /v2/invoices ...
### Example: /v2/webhook_deliveries ...
### Example: /v2/audit_events ...
<!-- lines 52-55 -->
The `billing-v1` service is exempt. Its clients pin page numbers in saved
reports; keep offset pagination there and do not add a `next_cursor`.The obvious cut is the seven-line paragraph at the top, and most reviewers stop there, which gets you to 53. The rest of the reduction has to come from somewhere, and the only way to choose is a test that can say no to a line somebody wrote on purpose.
The test is deletion, not grammar
The rule most teams reach for sorts by mood: keep imperatives, cut background. It is wrong in both directions and cheaply so. "Be careful when paginating large collections" is an imperative and changes nothing, because there is no action it selects for or against. "The orders table has 400 million rows" is a bare fact and changes a great deal, because it moves the agent from a single UPDATE to a batched backfill without being asked.
What actually distinguishes the two is effect on the plan, so the test measures that directly. Take five tasks the skill fires for, drawn from the request log rather than invented. Run each twice, once with the candidate text present and once with it deleted, and compare the plans the agent produces. If no plan differs, the text is not doing work. Two runs of five tasks against a 60-line body costs about four minutes and roughly 40,000 tokens, which is cheap enough to run on a block rather than argue about it.
| Block | Lines | Plans that differ (of 5) | Verdict |
|---|---|---|---|
| 2021 rationale, incident history | 7 | 0 | cut |
| "the team believes cursor is preferable" | 1 | 0 | cut, and rewrite as step |
| Four numbered steps | 14 | 5 | keep |
Example: /v2/invoices cursor encoding | 9 | 3 | keep |
Examples: webhook_deliveries, audit_events | 18 | 0 | cut |
billing-v1 exemption | 4 | 1 | keep |
The rationale block scores zero because everything it might have licensed is already stated four lines later as a step. The agent does not need to know that clients saw duplicates in 2021 in order to append a primary-key tiebreaker; step 3 says to append it. The belief sentence is worse than inert, because "generally preferable" is a hedge sitting above four unhedged steps, and its only possible effect is to weaken them. Both go, and the instruction they were gesturing at is written plainly:
Use cursor pagination for any endpoint returning a collection. Use offset
pagination only when the client must jump to an arbitrary page; say why in the
PR description.That is nine words shorter than the belief sentence it replaces and it names the exception condition and the evidence the reviewer will look for. A line like Run make schema-check before proposing any migration has the same shape: it selects an action, it happens at a definite point, and its absence is observable in the transcript.
The three examples split, which is the result that surprises people. Two of them demonstrate the same transformation on different endpoints and the plans are identical with or without them. The /v2/invoices example survives because it is the only place the cursor's wire format appears: v1: prefix, then base64 of the ordering key and the primary key, joined with a colon. No step describes that, and no step easily could without becoming a paragraph. When an earlier pass cut all three examples, the agent produced ?cursor=eyJpZCI6MTIzfQ== — a defensible encoding that the gateway rejected with a 400, because it validates the v1: prefix. An example that pins a format the steps cannot describe unambiguously is doing the work of a specification.
The billing-v1 exemption scores 1 of 5 and stays. That is the first place the raw count misleads, and the reason is in the shape of the failure rather than its frequency: on the one task where it matters, its absence produces a confident, plausible, wrong change to a service whose clients break silently.
The arithmetic on the whole cut is modest per invocation and not modest in aggregate. Sixty lines came to about 780 tokens and the surviving 28 to about 360, so each firing saves roughly 420 tokens. On a skill that fires 30 times a week that is 12,600 tokens a week, which is not the interesting part; the interesting part is that two of the six blocks were actively pulling against the steps, and their removal is what the fixture registered as a change in behaviour rather than in cost. Deleting inert text buys context. Deleting text that competes with the instructions buys compliance.
The counterexample that looks like it kills the rule
A senior engineer reading the draft guidance objects to "cut anything that does not change the agent's action" with a case from their own skill. The body contains one sentence — the payments service is the only one in the estate with strong consistency, meaning a read issued straight after a write is guaranteed to see it, while everything else is eventually consistent and may serve a stale value for a while — which is background by any grammatical reading and which somebody removed in a cleanup. The plans got measurably worse.
Running the deletion test on it produces the numbers behind that claim. With the sentence present, four of five tasks read back directly after a write to payments. With it removed, three of five added a poll-until-visible loop with 250 ms backoff, which adds about 1.2 seconds to each write path, and one of those loops swallowed a genuine write failure by retrying until the timeout and reporting success on a stale read.
The counterexample is valid as evidence and it does not touch the rule. What it shows is that the rule was being applied by inspecting grammar rather than by measuring effect: the sentence is background in mood and action-changing in fact, and the deletion test scores it 3 of 5. It is not an exception to "cut anything that does not change the action"; it passes that test outright. The mistake was in the shorthand people were using to apply it.
The restatement has to keep the burden of proof where it was, since the danger is a rule that softens into "include whatever seems useful", which excludes nothing and therefore costs the person adding text nothing. Two clauses do it. Cut any text for which nobody can name a task where its removal changes the plan, and demonstrate the change with a diff of two runs rather than with an argument. The person who wants the text carries that burden, and the artefact they produce is a fixture:
{
"candidate": "The payments service is the only one with strong consistency.",
"tasks": [
"add a refund endpoint that writes then returns the updated balance",
"backfill payment_method_id on payments from the vault",
"add an idempotency key to POST /v2/charges",
"expose a webhook after a successful capture",
"move the fee calculation into the payments service"
],
"runs_per_task": 2,
"pass_if": "plans differ on >= 1 task and the difference is worse without"
}There is a further move available once the test passes, which is that a fact whose effect on the plan you can state can usually be written as the plan change itself. "Do not poll for read-after-write on payments; its reads are strongly consistent. For every other service allow up to 2 s of staleness, polling at 250 ms for at most 8 attempts" is one line longer, unambiguous, and does not depend on the model drawing the same inference from the fact each time. Background that survives the deletion test is often an instruction that has not been written down yet.
The rule also needs a budget to be able to say no, which is what the 30-line cap on api-pagination supplies. Applied line by line without a cap, the test asks only whether a line does any work, and almost any line does some. Applied against a cap, it asks which lines earn scarce space, and blocks that score 1 of 5 have to argue against blocks that score 5 of 5. The cap is what makes the guidance function as guidance rather than as encouragement.
The second sort has a different question
Everything that survives the cut is not automatically body text. Deferral to a bundled file, whose mechanics topic 12.3 covered, is decided by a separate question: what fraction of the triggering tasks need this, and can the agent act at all if it is absent.
| Material | Tasks needing it | Placement |
|---|---|---|
| Four numbered steps | 100% | body |
| Cursor wire format example | ~40% | body — 9 lines, pins a format |
billing-v1 exemption | ~5% | body — absence is silently wrong |
| Pagination style of all 34 existing endpoints | ~15% | references/endpoints.md |
| OpenAPI fragment for the page envelope | ~20% | references/envelope.yaml |
| Offset-to-cursor migration script | ~10% | bin/convert-endpoint |
The two columns disagree on one row, and that row carries the discriminator. The billing-v1 exemption is needed less often than the endpoint inventory, and it belongs in the body while the inventory belongs in a file, because the agent working on a billing-v1 endpoint has no signal that an exemption exists to look for. Material that the agent will know to fetch can be deferred; material whose absence produces confident wrong output cannot, regardless of how rarely it applies. The inventory is safe to defer precisely because a task that needs it announces itself — the agent is looking at an endpoint and can go and read what style it uses.
Each pointer costs a line in the body, and that line is subject to the same deletion test as everything else. One line naming the file and the condition under which to read it — "before changing an existing endpoint, check its current style in references/endpoints.md" — passes. Three lines describing what the file contains does not, because the agent reads the file to find that out.
What the cut does not buy
Shorter is not the objective, and a body that survives the test at 44 lines should not be pushed to 30 by deleting things that score 5 of 5. The cap is a forcing function for comparison, so when everything left is load-bearing, the honest outcome is to raise the cap and say why in review.
Nor is the cut an exercise in compression. Rewriting the four steps as one dense sentence with three semicolons would reach 30 lines by a different route and produce a body that nobody on the team can review and that the agent reads as a single conjunction it may satisfy partially. The unit that gets deleted is a block with a job, and what remains keeps its spacing, its numbering and its ordinary sentences. A reviewer who cannot skim the body cannot tell you whether step 3 contradicts step 4, and that judgement is the only defence against the failure the last section describes.
The cut also says nothing about how reliably what remains is followed. api-pagination came out at 28 lines, every one of them passing the deletion test, and the endpoint shipped two weeks later with ?offset= accepted alongside ?cursor= because step 4 read, in the version that survived, "reject ?offset= where the client does not depend on it". A step can earn its place in the body and still be honoured about half the time, which is a property of its phrasing and of its neighbours rather than of its right to exist.
Writing a skill — read the current version at https://daviddominguez.dev/learn/en/writing-a-skill/cut-anything-that-does-not-change-the-action-keep-what-pins-a-format