Tutorial: Outcome-First Fix¶
You'll finish this tutorial having watched attune fix repair a
real bug you seeded yourself — and, more importantly, having read
the receipt that proves it: which file changed, which
verification commands ran, and why the tool's own exit code can be
trusted. Then you'll do the same thing again without typing a
single path, using the /fix guided intake form in Claude Code.
Prerequisites¶
- Python 3.10 or newer
attune-ai11.2.0 or newer (pip install -U attune-ai)- A git repository to work in (scope verification uses git)
- For Step 5: the attune-ai plugin installed in Claude Code
The idea in one paragraph¶
You state what should be true ("exactly 100 units should price
as bulk") and how to check it (a probe: any command whose exit
code verifies the claim, like pytest). Attune runs the fix, then
re-runs your probes itself, in a real subprocess, after the fix
agent has finished — and the receipt's verdict comes from those
probes, never from the agent's own opinion of how it did.
Step 1 — Seed a bug you can verify¶
Create a scratch module with one off-by-one bug and a test suite that catches it:
scratch_pricing/pricing.py:
BULK_THRESHOLD = 100
def tier_for_units(units: int) -> str:
"""Orders of BULK_THRESHOLD units OR MORE are bulk."""
if units > BULK_THRESHOLD: # BUG: should be >=
return "bulk"
return "standard"
scratch_pricing/pricing_suite.py:
import sys
from pathlib import Path
sys.path.insert(0, str(Path(__file__).parent))
from pricing import tier_for_units
def test_boundary_order_is_bulk():
assert tier_for_units(100) == "bulk"
def test_small_order_is_standard():
assert tier_for_units(99) == "standard"
def test_large_order_is_bulk():
assert tier_for_units(101) == "bulk"
Confirm the bug is real — the boundary test fails:
Step 2 — Preview the contract (nothing runs)¶
attune fix "exactly 100 units should price as bulk" --workflow fix --scope scratch_pricing --probe "pytest scratch_pricing/pricing_suite.py"
Without --run, this is a dry preview. It renders the contract —
your goal verbatim, the derived done conditions, the constraints,
and the validated probes — and ends with:
note: execution not requested — pass --run to execute this contract
dry preview — nothing was executed
Read the "Done when" list carefully. This is the checkpoint where you confirm the probes actually test what you meant.
Step 3 — Run it and read the receipt¶
Arrow-up and append --run. The fix workflow edits the code, then
the CLI verifies independently:
workflow finished (success=True) — verifying independently...
🧾 Fix receipt
Changes made (attributed to this run):
- scratch_pricing/pricing.py
Probes (evaluated independently):
- [PASS] pytest scratch_pricing/pricing_suite.py (exit 0, 2902ms)
Safest next action: review the attributed diff and commit
receipt reflects independently evaluated probes — workflow exit was not trusted
Three things to notice:
- Attribution is measured, not claimed. The receipt compares against a pre-run snapshot. Files you already had in flight are listed separately as pre-existing and never blamed on the run.
- The probe was re-run by the CLI, argv and exit code
recorded. The workflow saying
success=Truedid not decide anything — the trailer line says so explicitly. - The exit code follows the probes.
echo $?prints 0 only because every probe passed, the diff stayed insidescratch_pricing, and scope was verifiable.
Check the diff yourself — the fix should be > → >= in
pricing.py, and the tests untouched:
Step 4 — Verify more than one thing¶
Plural probes are the point. A target probe plus a full-suite probe tells you the fix worked and broke nothing:
attune fix "exactly 100 units should price as bulk" --workflow fix --scope scratch_pricing --probe "pytest scratch_pricing/pricing_suite.py::test_boundary_order_is_bulk" --probe "pytest scratch_pricing/pricing_suite.py" --run
Each probe becomes its own line in the receipt, evaluated independently. If any fails, the receipt names every failing probe with its exact command, and the exit code is 1.
Step 5 — The same flow without typing paths (/fix)¶
In a Claude Code session with the attune-ai plugin, say:
The guided intake presents ONE form: your goal (carried from the
invocation), a scope picker whose options are derived from your
working tree's changed paths, a probe picker offering matching
test files, and a preview-vs-run choice. The composed
attune fix ... command is shown before anything executes — the
same contract, the same receipt, no paths typed from memory.
Exit codes¶
| Exit | Meaning |
|---|---|
| 0 | every probe passed, scope held, and scope was verifiable |
| 1 | the run completed but a done condition failed |
| 2 | the workflow crashed (a partial receipt is still printed) |
| 3 | CLI error or abstention — nothing ran |
Where to go next¶
attune fixvs/fix-test: the skill diagnoses a failing test for you in conversation;attune fixverifies an outcome you state. Neither replaces the other.- The full option reference lives in reference/fix; the design rationale in architecture/fix.