benefit-contract
Skill14 starsChanged 16 days agoMITView on GitHub
Use at Phase 0.8 of a behaviour-changing wave, before any acceptance criteria are written. Produces the Benefit AC, the authority census and the integration count in .wave/ben.md.
In this repository7 other files it gives its agents.
- wave-execution-framework-v2Skill
- ac-writerSkill
- design-reviewerSkill
- green-implSkill
- red-testsSkill
- teet-verifySkill
- wave-orchestratorSkill
The file
We split this skill into the rules it gives an agent
3 rules. About 0 are common, 1 is distinctive, and 2 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
2
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 24
Write .wave/ben.md with three sections and, at the end, the line:
- Very specific: line 45
If the benefit can only be observed end to end, say so and name the command.
- Distinctive: line 98
No new symbol; the subject is the user.
≈ “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
Benefit Contract
You write the contract that says what the wave is for, before anyone writes a criterion, a design or a test.
Why this skill exists
A wave can run every gate green and still deliver nothing. The component is built, its tests pass, the alignment check says the build matches the spec — and the behaviour the wave was for never happens, because something that was already there still decides the outcome, or because nothing outside the new directory ever calls the new code.
RED and GREEN are written from the same brief, so a wrong brief produces a green suite. The output-alignment check compares the build with the spec, not the outcome with reality. Mutation testing measures the tests that exist. None of them asks whether the user's day changed.
That question is this phase's only job. You run when the wave changes existing behaviour.
Output
Write .wave/ben.md with three sections and, at the end, the line:
BEN-VERIFIED red=<n> authorities=<n>red is the number of failing benefit tests and must be at least 1.
authorities is the number of rows in the census and must be at least 1.
1. The Benefit AC
Exactly one sentence. Not a list, not a paragraph.
Three rules, all checkable:
- Its grammatical subject is not the new component. The subject is a person, or the surrounding system. If the sentence begins with the thing you are building, it is a description of the build, not of a benefit.
- Its verb is what that subject does differently once the feature exists.
- It names no symbol the wave introduces. No new class, function, table, column, flag, endpoint or file name. Search the finished sentence for each new identifier; a hit means rewrite. This is the rule that does the work, because a sentence that cannot mention the new code has to talk about the outcome instead.
Then back it with one named, executing test. You write it, you run it, and it fails — for the right reason, which you state. A test that fails because a file does not exist yet is not evidence about the benefit; a test that fails because the outcome does not happen is.
If the benefit can only be observed end to end, say so and name the command. [TEET] will run it.
2. The authority census
Name the decision the feature influences. One decision, in plain words.
Then list, by reading code, every existing component that already decides that same outcome. For each one:
| Column | What goes in it |
| Location | file and symbol |
| How it decides | the rule it applies |
| Input | what it reads to decide |
| On disagreement | what happens when it and the new feature want different outcomes |
| Resolution | which one wins today, and why |
The census must include the authority that produces the outcome the Benefit AC names. That is what makes the two artifacts interlock: if the Benefit AC says a returning user lands on a narrowed list, the census has to name whatever decides today what that list shows.
authorities is never 0. A feature that influences a decision nothing makes today is a new decision, and the census says exactly that, in one row.
3. The integration count, sign inverted
Count the files the wave expects to change outside the feature's own directory.
A behaviour-changing wave with a count of 0 or 1 has been hosted, not integrated. That is a finding, not an achievement. It needs a written justification naming the existing mechanism that will carry the behaviour to the rest of the system.
"Untouched" is not a score. Write the blast-radius table so that every row answers one question — must this change, and why not? — and a row that answers "no" carries the reason.
At the close: [BEN-CLOSE]
A separate team, with fresh eyes, writes .wave/ben-close.md after the last hardening phase and before the version bump. It does four things:
- Runs the named benefit test, recording the command, the exit status and the tail of the output.
- Orphan-state sweep. For every new class, table, flag or state the wave introduced, search for production readers outside the feature's own directory. Zero external readers means the rest of the system cannot see it. Name the reader that should exist, or justify its absence.
- Defeat list. Search the wave's own new comments and documents for any statement that the existing system undoes, ignores or overrides the feature. Each one is a requirement unless a named test guards it. Your own notes saying "the container resets this on mount, so we restore afterwards" are the clearest possible evidence that something is fighting the feature.
- Writes the verdict.
BENEFIT: DELIVEREDonly when the test passed and neither list holds an unjustified item. OtherwiseBENEFIT: UNDELIVERED, followed by the reasons.
UNDELIVERED does not block the wave. It is recorded, printed in the scorecard and announced once at the close. Never edit the Benefit AC to turn the verdict green — that is the one move this whole phase exists to make visible.
Worked example
Feature: remember my last search filters.
Benefit AC — wrong:
The filter-persistence service writes the active filter set to storage whenever it changes.
The subject is the new component, the verb is what the code does, and it names the thing being built. It would be green the moment the service exists, whether or not anyone's screen ever changes.
Benefit AC — right:
A returning user opens the results page and finds it already narrowed the way they left it, without touching the filter bar.
No new symbol; the subject is the user; the verb is what the user no longer has to do.
Backing test. Open the results page, apply two filters, end the session, start a new one, open the results page again, assert the visible result set is the narrowed one and the filter controls show those two values. It fails today because the page comes back unfiltered. That is the right reason.
Authority census. Who already decides what that page shows on load?
| Location | How it decides | Input | On disagreement | Resolution |
| route definition | supplies default query parameters when none are in the URL | the route's own defaults | the defaults overwrite a restored filter set | route wins — it runs first |
| results container, on mount | clears the filter state to "show everything" | none | the clear runs after a restore and erases it | container wins — it runs last |
| server list endpoint | applies a role-based default filter when the request carries none | the caller's role | a restored client filter is sent, so this branch is not reached | client wins when it sends filters |
| response cache | serves the previous unfiltered payload for a short window | the request URL | a restored filter changes the URL, so the entry misses | no conflict |
authorities=4. The second row is the one that matters: something already in the system resets exactly what the feature is trying to restore. That row is the reason the wave is not simply "add a service".
Integration count. If the plan changes only files inside the new service's own directory, the count is 0 and the feature is hosted, not integrated — nothing would ever ask it for the remembered filters. The honest plan changes the results container (to ask, and to stop clearing) and the route definition (to stop overwriting): count 2, with a blast-radius row for the endpoint and the cache saying why neither must change.
At the close. The orphan-state sweep asks whether anything outside the service's directory reads the stored filter set; if the answer is nothing, the feature is writing to a drawer no one opens. The defeat list picks up the comment someone added during GREEN — "the container still clears on mount; we restore after it" — and turns it into a requirement: either a named test pins that order, or the verdict says so.
Anti-patterns
- A Benefit AC that is really two sentences joined by "and". Split the wave instead.
- A census assembled from memory or from documentation. Read the code; cite file and symbol.
- A census with one row that is the new feature itself. The census is about what exists now.
- Reporting an integration count of 0 as good news.
- Writing the benefit test after the implementation, so it has never been red.
- Softening the Benefit AC once the verdict looks like it will be
UNDELIVERED.
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.

