Land a complex change
A complex change rarely fails at the part that was planned. It fails at the edges: the file that got tidied because it was already open, the guard that has been green since the day it was written and could never have failed, the migration that got applied because applying it was the obvious next step, the second consumer nobody mapped. The diff is reviewable and the review still misses these, because a reviewer sees what changed — not what was allowed to change, and not which of the green checks were ever capable of turning red.
Two costs, named apart because different mechanics prevent them. A side effect is work the change did that nobody declared. A regression is behaviour that used to hold, does not any more, and had nothing watching it. This skill contains the first with a budget and catches the second with a gate per affected surface — and when the work turns out to be bigger than the map said, it stops rather than absorbing the difference quietly.
It changes the repository. Everything it changes was declared first.
What contains a change
Three artifacts, produced in this order. None of them is a status report; each one is a thing the run can be checked against afterwards.
- The side-effect budget. The declared touch-set — every path, symbol, schema object and config key this change is permitted to alter, derived from the blast map rather than from memory — plus the classes of act that are out of bounds regardless of what the map says, plus the rule for what happens when reality exceeds the declaration. A budget written after the work is a description of the diff, which is a different document with the same shape.
- The regression gate ladder. For every surface the map marks affected, the guard that would catch a regression there — watched failing before the change and passing after. A guard that was green on both sides proves that it ran, not that it works. Surfaces with no guard at any rung are recorded as unguarded, in the deliverable, where a reviewer reads.
- The change band and its act gates. How much apparatus this change is worth, scored before anything is spent, with one rule that overrides the score: anything irreversible in the plan raises the floor and arms consent at the act, not at the plan.
The mechanics behind them are ordinary and the discipline is the hard part. In one measured repository, four separate CI guards were added unarmed — each passed from the day it landed, each was reported as coverage, and none of them was ever capable of failing. Nothing in that repository was broken by a bad guard. Things were broken by the absence a green guard was standing in front of.
What this skill does not own
| The job | Whose it is | What this skill does with it |
|---|---|---|
| Which model runs which role | model-routing |
Names roles and bands only. This skill chooses no models, and it does not override model-routing's Procedure step 2. |
| Dispatching children and seeing what they did | agent-lifecycle |
Consumes it; keeps no child bookkeeping of its own. |
| Durable, regenerable, CI-gated repo artifacts | derive-codebase-context |
Reads what already exists; builds nothing durable. |
| Answering one question about the code | investigate-codebase |
Delegates every search, and inherits its complexity rubric rather than restating it. |
| What a set of changes would affect | blast-area |
Consumes its map as the budget's source, and re-enters it when the budget is breached. |
| Drawing that map | visualise-blast-area |
Optional. Renders the same envelope; changes nothing in it. |
| Describing what the change did | describe-changes |
Hands it the landed diff at the end. |
| The review loop over the pull request | request-blocks-review and blocks |
Hands over the budget, the ladder and the residuals; runs no review loop of its own. |
| Intake, reproduction and candidate offers for a report | resolve-problem-report |
Receives a spec from it; never performs its own intake. |
Install the companions with npx skills add crissmoldovan/agent-skills.
When to Use
- A change crosses more than one surface — code and schema, code and jobs, code and config — and no single reviewer, and no single compiler, sees the whole path.
- A refactor claims "no behaviour change" and something has to hold it to that claim.
- A migration is part of the work, and the plan contains an act that cannot be taken back.
- A deletion is part of the work, and the evidence that it is safe has to outlive the session that gathered it.
- The last attempt at this change grew: the branch touched files nobody asked for, and review turned into an argument about scope instead of about correctness.
- The work runs unattended, or across several sessions, and "what were we allowed to touch?" has to survive the gap.
- A spec has arrived from a report pipeline and now has to be built without disturbing the rest of the system.
Do not use it to work out what a change would hit — blast-area owns that, and this skill
consumes its map rather than deriving a second one. Do not use it to answer a question about
how the code works; that is investigate-codebase, which both skills call for their searching.
Do not use it to take a report from intake through reproduction and root cause to candidate
fixes — resolve-problem-report owns that pipeline and delegates its spec-implement-verify half
here by name. Neither trigger is a superset of the other. A report can correctly end with no
change at all: a refutation, a question, a "this was fixed three weeks ago". A change frequently
starts with no report behind it — a roadmap item, a dependency bump, a deprecation with a date on
it. If you hold a report and no chosen fix, start there and arrive here; if you hold a chosen
change and no report, start here and do not manufacture one. Do not use it to describe what
landed — describe-changes reads the diff — or to run the review loop over the pull request,
which is request-blocks-review and blocks. And do not use it as a project-management wrapper
around a body of work with no definable touch-set: the budget is the skill, and a budget over
an undefined change is a formality that will be quietly widened by lunchtime.
Prerequisites
- The change stated as changes, with a shape. Every element names a thing — file, symbol, schema object, endpoint, config key — and an operation: add, rename, drop, retype, move, delete. And the change carries a shape: feature, refactor, migration, or a stated mix, because the shape decides what the gates have to prove. Per-shape entries are in change shapes. Complete when: every element has a thing and an operation, and the shape is written down.
- A blast map at a named base revision. Surfaces, break times, deploy ordering, and the map's own "what this map cannot see". The budget is derived from this; a touch-set assembled from memory is a wish. Complete when: the map's envelope is in hand — or the run is at the light band and the absence of a map is recorded as a declared, not derived, touch-set.
- The repository's own checks located and runnable here. Test command, typecheck, lint, build, CI configuration, and any manual runbook step that constitutes a check. Complete when: each is named with the command that runs it, and at least one has been run in this checkout to prove the apparatus works.
- A revision you can return to. A recorded base sha, a clean or explicitly-recorded dirty tree, and a branch that is not the one that deploys. Complete when: base sha, dirty state and the revert path are written down.
- Every irreversible act in the plan enumerated, before the first edit. Applying a migration, writing to production data, deleting anything not restorable from history, sending an outbound message to a person, a deploy with no rollback. Complete when: the list exists — or is empty and says so.
- Attendance declared, conservatively. If you cannot confirm a human will see and answer a question in this run, it is unattended. Unattended changes both the act policy — an irreversible act stops the run rather than proceeding with a note — and whether a run record is written. Complete when: the run is marked attended or unattended before the first gate.
Procedure
Name the shape, and the claim the shape makes. A feature claims new behaviour: its gates are new checks that must fail before the code exists. A refactor claims no behaviour change: its gates are existing checks that must pass unmodified, and any test that had to be edited to keep it green is a behaviour change wearing a refactor's clothes — say so rather than editing it. A migration claims a data or schema transition: it has two halves, an ordering between them, an irreversible act, and a window in which one half is new and the other is old. Mixed changes take the strictest gate of each shape they contain, and are better split. The per-shape budget and ladder deltas are in change shapes.
Score the change band before anything is spent, and let an irreversible act set the floor. The rubric belongs to
investigate-codebase— five signals, each scored 0–2 against an observable you actually ran, summed into light / normal / deep, with a boundary total rounding up one band. Run that rubric; do not restate it and do not invent a second one. One rule is this skill's own and it is not a restatement: for a change, cost-of-being-wrong is 2 whenever the plan contains an irreversible act. Not "may contain" — contains. That forces at least the normal band and it arms the act gates in step 8. Re-score after the map arrives, because the map changes the rubric's inputs; a two-file change that turns out to cross four surfaces was never a light-band change, it was a mis-scored one. Announce the band on one code path, before any child is dispatched, whether or not anyone is watching. What each band buys here:Band Apparatus Landing light declared touch-set, one gate on the single affected surface one step, one revert normal blast map required, budget derived from it, one gate per affected surface steps ordered by the map, each separately revertible deep the above, plus an adversarial pre-mortem against the budget and a staged landing staged, with a checkpoint between halves Band-to-role assignment, the escalation triggers and the degradations are in change bands and act gates.
Take the map. Do not rebuild it, and do not substitute recollection for it.
blast-areareturns surfaces with break times, searched negatives with their controls, a deploy ordering with its reason, and a mandatory list of what it could not see. Read that last part first: it is the part that decides how much of the budget is guesswork. If the map is missing a surface you know exists, that is a defect in the map — send it back rather than patching the hole in the budget, or the budget will be the only place the surface is recorded and the reviewer will never see it.Declare the side-effect budget, and declare it before the first edit. One artifact, four parts, written down where the reviewer will read it:
BUDGET <change name> base <sha> band <light|normal|deep> shape <feature|refactor|migration> ALLOWED path or symbol operation surface (from the map) EXPECTED EFFECT surface what changes there break time OUT OF BOUNDS the four standing classes + anything this change adds ON BREACH stop → re-enter blast-area → extend | split | abandon (never widen silently)Everything not in ALLOWED is out of bounds by default — that is what makes it a budget rather than a list of intentions. Include the awkward entries explicitly: generated files, lockfiles, formatter sweeps, import reorders. A formatter that rewrites two hundred files it was never asked about is a side effect with a tidy diff. The full artifact, its field semantics and a worked budget are in the side-effect budget.
Set the out-of-bounds classes, and keep them out. Four classes stand outside every budget by default, whatever the map says, and each is released only by a consent that names the specific act at the moment of the act:
- Applying a migration — writing the schema change to a live database, as opposed to writing the migration file.
- Writing to production data — including a "small" backfill, and including a run of a job whose effect is a write.
- Deleting anything not restorable from history — data, buckets, branches, tags, secrets, and code whose only copy is the working tree.
- Sending an outbound message to a person — mail, chat, ticket comment, notification, status change that pages someone.
Add the repository's own: rewriting history, touching credentials, and editing the gates themselves, which deserves its own line because a change that alters the guard it is being judged by has removed the only thing that could contradict it.
Arm one gate per affected surface — and watch each one fail before you change anything. This is the ladder, and it is per surface, not per repository. For each surface the map marks affected, find the guard that would catch a regression there and place it on the highest rung you can actually reach:
Rung Guard Evidence it is armed 1 an automated check that fails before and passes after the failing output, quoted, from before the change 2 an automated check that cannot fail first (purely additive work) a recorded baseline, plus a mutation: break the thing deliberately, watch the check fail, restore 3 a written manual observation with an expected value the procedure and the before-value, recorded 4 none — recorded as an unguarded surface what would guard it, and what that would cost Rung 1 is the ordinary case for a feature and for a bug fix: write the check first, run it, watch it fail for the right reason — not because the file does not exist yet, not because of a typo in the test name. Rung 2 exists because some correct work cannot make an existing check fail, and a check that has never been observed failing is a claim; the mutation converts it into evidence and costs about a minute. Rung 4 is not a failure of the run — it is the run's most useful output, and it must appear in the deliverable rather than in a thought. A surface listed as affected with no row on the ladder is the one shape of omission this skill exists to make impossible. The full ladder, per-surface guard classes, and the failure modes of each rung are in the regression gate ladder.
Land in the map's order, in steps that revert one at a time. The deploy ordering came with a reason attached; follow it, including what runs in the window between halves. Keep each step independently revertible — one concern per commit — because a change that can only be reverted whole converts a small regression into a full rollback, and everyone will therefore choose to patch forward under pressure instead. Stay inside ALLOWED. Fixing something you noticed in passing is the definitional side effect, however small and however correct the fix is: it lands in a follow-up with its own budget, or it lands as an explicit extension in step 9.
Gate every irreversible act at the act, and take none of them unattended. Consent lives here, not at band selection — a mode question at the start is the wrong moment, because nothing irreversible is imminent and the person answering has no context yet. At the moment before the act, state four things: what is about to happen, what it touches, what it cannot undo, and what happens if you do not do it. Then wait. An unattended run does not take an irreversible act. It stops, leaves the change staged, and reports exactly what remains to be done — a cron job that applied a migration because it seemed like the natural next step has made a decision nobody was present to make. And remember that the act's record is part of the act: in one measured repository, a migration applied to production while its file sat unmoved in a staging folder broke the next deploy from a clean checkout, twice inside one day, because the file's location was the applied-history record and it disagreed with reality.
When something outside the budget appears, stop the work and re-enter at
blast-area. This is the rule the artifact exists to enforce, and it is not advisory. A new caller, a second registry, a consumer in another repository, a column read from a string literal, a surface the map said was empty and is not — the work stops there. Re-enter the mapping skill with the new fact, get an updated map, and then take one of exactly three dispositions, in the open:- Extend — re-declare the budget with the new entries, announce the extension and the re-scored band, and continue. An extension is a document change, not a shrug.
- Split — land what is inside the original budget, and carry the discovery into a separate change with its own map and its own budget.
- Abandon — the discovery invalidates the approach. Say what was found; that finding is usually worth more than the change was.
What is never available is the fourth option everybody takes by default: absorbing the discovery into the current work because it is small, related, and right there. That is how a two-file change becomes an eight-file change and the review becomes an argument about scope.
Close the ladder: pass-after for every gate, unguarded surfaces still visible. Run every guard again and record its result next to the fail-before evidence from step 6. A gate that never failed before is recorded unproven, not passed — including one that was green all along because it never covered the surface. Refactor shapes have one extra rule: if a test had to be modified to make it pass, the claim of no behaviour change is refuted, and the honest output is the behavioural delta rather than an edited assertion.
Check what the gates cannot see, and state the undo path. Every guard you can run is a guard over the surfaces the tooling reaches. Carry the map's break times forward: the
silentitems are precisely the ones no green check will ever report on — a value that stops being written, a job that stops registering, a query built from a string, a row in a datastore naming something you renamed. List them as residual risks with the observation that would settle each. Then state the undo path in the change's own terms: what a revert restores, what it does not, and what data written during the window a revert cannot bring back. "Revertible" without that second half is how a rollback becomes an incident.Hand off the description and the review loop; do not perform them here.
describe-changeswrites what the change did, anchored to the diff.request-blocks-reviewandblocksown the review loop over the pull request. Give the reviewer three things beside the diff — the budget, the ladder with its unguarded rows, and the residual risks — because a reviewer with only a diff can check whether the change is correct but cannot check whether it was contained.Document the run when the branch calls for it. The convention is below, and it is identical in every skill of this family that supports it.
The run record
Documenting the run. Write a full record when the invocation carries --document (or
an unmistakable phrase such as "document the run"), at docs/land-complex-change/<UTC-date>-<slug>.md
in the host repository — and always when the run is unattended, in which case it goes to the
harness scratch directory, never the repository, and its path is named in the final answer;
if you cannot confirm a human will see and answer a question in THIS run, treat it as
unattended. Attended with no token: stay silent, offering once only if the run ends with an
unresolved contradiction or an open blocking question. Five headings, in order: What was
searched / Who searched it / What each found / How it reconciled / What was decided and why,
closing with what this run could not see. No secret values, machine-absolute paths, child
transcripts or raw logs; no unmarked inference or unearned benefit claims; never edit an
earlier record, and never let writing one change the answer.
Records are written per the run-record convention.
For a change, the five headings carry the change's own material: the budget and the map it came from under what was decided and why; every gate with its fail-before and pass-after evidence under what each found; each budget breach, its re-entry and its disposition under how it reconciled; and the unguarded surfaces and residual risks in what this run could not see.
Usage Examples
Here is the blast map for dropping the status column. Turn it into a touch-set budget before you
write any code, and stay inside it — if you find a reader the map missed, stop and tell me
instead of fixing it while you are in there.
This is a pure refactor and the claim is no behaviour change. Before you start, show me which
existing test covers each surface on the map and which surfaces have nothing watching them at
all. I would rather know the ladder has holes now than find out from production.
Build the spec on the branch. The migration file is part of the work, but do not apply it —
leave it staged, and tell me the ordering, what runs in the window between the halves, and what
a revert would not bring back.
Run this one unattended overnight. Land what is inside the budget, stop at anything
irreversible rather than deciding for me, and document the run (--document) so I can read what
happened and what is left before I approve the rest.
This looked like a two-file change and it is turning into eight. Stop, re-score the band, get
the map redone with what you just found, and come back with an extended budget before you write
another line.
Before review: give me the gate ladder with the fail-before output for each guard, the surfaces
you could not guard, and the residual risks the tests cannot see. The diff I can read myself.
Pitfalls
- The budget written after the work. It will match the diff exactly, prove nothing, and read as diligence. The budget's whole value is that it was falsifiable while there was still time.
- "While I was in there." The single most common source of side effects, and it never arrives as a bad idea — it arrives as a small, correct, obviously-worth-doing fix that nobody asked for and nobody will review as a change.
- A guard nobody watched fail. Green from the day it was written, reported as coverage, incapable of failing. Four of them in one measured repository, and each cost more than having no guard, because no guard invites care and a green one invites confidence.
- A silently widened budget. Extension is legitimate; extension without an announcement is the failure. The difference between them is one sentence written down.
- Absorbing the discovery instead of re-mapping. The new fact is evidence that the map was wrong, which means the rest of the map is now suspect too — not just the part you noticed.
- Reverting the code and calling it reverted. Rows written during the window survive the revert. Jobs that fired stay fired. Messages that went out stay sent.
- Editing the test to make it pass. In a refactor that is the behaviour change announcing itself, and editing it is how the announcement gets suppressed.
- A green suite mistaken for coverage. A suite that passes over an unguarded surface is not evidence about that surface. It is evidence about the others.
- Applying the migration because you were already at the prompt. The act gate exists exactly where the momentum is strongest, which is why it is placed at the act and not at the plan.
- The unattended run that took the irreversible act and left a note. A note is not consent. Nobody was there; that is the entire premise of the branch.
- One commit for the whole change. Then the only available revert is total, so under pressure everybody patches forward instead — which is the state the budget was meant to avoid.
- Confusing the diff with the touch-set. Generated output, lockfiles and formatter sweeps belong in the budget as declared entries, or the diff will contain hundreds of files nobody decided on.
- Changing the gate you are being judged by. Occasionally necessary, never incidental, and always its own change with its own review — a guard edited inside the change it guards has removed the only thing that could have contradicted the change.
- Handing the reviewer a diff and nothing else. They can check correctness from a diff. They cannot check containment from a diff, because containment is a claim about what is not there.
- Leaving the act's record out of the plan. An irreversible act whose record is not updated in the same breath leaves the repository disagreeing with production — and the disagreement surfaces later, in someone else's deploy.
Verification
- The change's shape is named — feature, refactor, migration, or a stated mix — and the claim that shape makes is written down.
- The band was scored with
investigate-codebase's rubric (not a second, local one) before anything was spent, and announced before any child was dispatched. - Where the plan contains an irreversible act, cost-of-being-wrong was scored 2, the floor was raised to at least normal, and the act gates were armed — or the plan contains none and says so.
- The band was re-scored after the map arrived, and any change of band was announced.
- The budget was declared before the first edit, and it lists ALLOWED entries, expected effects per surface, the out-of-bounds classes, and the on-breach rule.
- The touch-set was derived from the blast map — or the run is light-band and the touch-set is explicitly recorded as declared, not derived.
- Generated files, lockfiles and formatter output are either declared in the budget or absent from the diff.
- Every surface the map marks affected has a row on the gate ladder, with its rung.
- Every rung-1 guard has quoted fail-before evidence; every rung-2 guard has a recorded baseline and a mutation that was watched failing and then restored.
- Unguarded surfaces appear in the deliverable, each with what would guard it and what that would cost.
- Every gate was re-run after the change, and any guard that never failed first is recorded
unprovenrather than passed. - For a refactor: no existing test was modified to keep it green, or the behavioural delta is reported as the finding.
- The work landed in the map's deploy order, in steps that can be reverted one at a time.
- Every breach of the budget stopped the work, re-entered
blast-area, and resolved to extend, split, or abandon — recorded in the open, with none absorbed silently. - No irreversible act was taken without consent at the act naming what it touches and what it cannot undo; no irreversible act was taken in an unattended run.
- Where an irreversible act was taken, the record that tracks it was updated in the same change.
- Residual risks are listed for the map's
silentitems, each with the observation that would settle it, and the undo path states what a revert does not restore. - The description and the review loop were handed to
describe-changesandrequest-blocks-review, and the reviewer received the budget, the ladder and the residuals alongside the diff. - Where a record was written, it exists at
docs/land-complex-change/<UTC-date>-<slug>.md(or the harness scratch path for an unattended run, named in the final answer), under the five required headings.
Deeper reading
- The side-effect budget: the artifact field by field, how the touch-set is derived from a blast map, the four standing out-of-bounds classes with the consent each requires, the breach procedure with its three dispositions, and a worked budget.
- The regression gate ladder: the four rungs with their evidence requirements, the guard classes that fit each of the map's surfaces, arming by mutation, how guards go quietly dead, and how unguarded surfaces are reported.
- Change bands and act gates: what each band buys for a change, the irreversible-act floor rule, the escalation and de-escalation triggers, the act-gate script, the unattended stop rule, and the degradations.
- Change shapes: feature, refactor and migration entries with the
budget and ladder deltas for each, mixed shapes, and the boundary with
resolve-problem-reportin both directions. - A worked landing: one migration-shaped change end to end — map to budget to ladder, a mid-run discovery that stopped the work and re-entered the map, the extension that followed, and the surface that stayed unguarded.
- The run-record convention: when a record is written, where it goes, its five headings, the prohibitions, and a worked record.