5 minutes read
Playbook: agent-driven migrations
A start-to-finish guide for letting a coding agent (Claude Code, Codex, Cursor, Copilot, Gemini CLI, Kilo Code) migrate one end-of-life or deprecated dependency, runtime, or GitHub Action, while Offing supplies the facts and the proof.
The split of responsibility is the point. Offing decides what is affected and whether it is fixed. The agent decides how to change the code. A human decides whether to merge.
Before you start
- Install the guardrail so the agent checks before it adds anything:
npx skills add velella-sh/offing, or connect the local MCP server (MCP.md). Either path needs no account or API key. - Work on a branch with a clean tree. One finding per branch keeps the diff reviewable and the verification unambiguous.
- Run the project's own tests once before changing anything. A migration cannot be verified against a suite that was already failing.
The loop
Every migration is the same four steps. The agent should run them in order and stop if one fails.
1. Discover
npx @velella/offing scan . --json
Pick one finding and copy its exact id. Do not let the agent batch several findings into one change.
For a large result, --format compact fits an agent's context window, and scan --write-status writes OFFING_STATUS.md so an agent that cannot call tools can still read the repository's lifecycle state.
2. Explain
npx @velella/offing explain '<finding-id>' . --json
Read the rule, the exact location, and the authoritative source. If the agent cannot point to the source, it should not proceed: a lifecycle claim without a source is not a reason to change code.
3. Migrate
npx @velella/offing context '<finding-id>' . --json
The migration packet (MIGRATION_PACKETS.md) lists the declaration, every usage, behavioral constraints, ordered steps, and suggested verification commands. The agent follows the steps and touches only the affected files the packet names. Offing never edits files and never runs the suggested commands; the agent runs them under the host's normal permission prompts, after reading them.
4. Verify
npx @velella/offing scan . --json
The migration is done only when both hold: the project's tests pass, and a fresh scan no longer reports that rule at that location. Through MCP, verify_migration does the second check in the session that saw the finding. An unknown or missing finding ID after a rescan is not proof of success; the agent should compare rule and path, not just the ID.
Worked examples on real repositories
These come from the pinned public corpus in benchmarks/public-repositories/, so each location is one Offing reports at that commit.
A Python floor that reached end of life: psf/requests. At commit 611c616, line 17 of pyproject.toml reads requires-python = ">=3.10", a release line that ended on 2026-10-01 (rule python.runtime.eol-3.10-and-older). The packet's scope is the declaration plus anything that gates on the old floor (CI matrix entries, classifiers, python_requires checks in tests). Raising the floor changes what the package supports for its users, so this is a decision to take to a maintainer; the agent should present the change, not make it silently.
A deprecated Action in many workflows: Akenon-Studio/.github. At commit 4ec1ebe, .github/workflows/security-scan.yml line 127 uses an actions/setup-node release on Node 20, and two workflows use actions/create-github-app-token@v2 below the supported major. The maintainers' own migration commit is the reference outcome. Here the packet paths are workflows only, so the diff should be a handful of uses: lines and nothing else. Check the Action's release notes for changed inputs before accepting the bump.
Failure modes and what to do
| What goes wrong | Why | Response |
|---|---|---|
| The agent edits files outside the packet | The packet's affected-file set is the scope; a broader edit has no Offing evidence behind it | Revert the extra edits, or open a follow-up issue for them |
| The agent bumps a version but the finding remains | The rule keys on the declaration, a range, or a runtime file the bump did not touch | Rescan, read the new location, repeat from step 2 |
| Tests pass, finding gone, behavior changed | The packet lists behavioral constraints for a reason; tests may not cover them | Read the constraints against the diff before merging |
no-data from check_dependency for a replacement |
Offing has no rule for it | Treat as unknown, not safe; pick the replacement the packet cites |
| The agent runs a suggested command that fails or is destructive | Suggested commands depend on project setup | Review commands first; Offing only suggests them |
| A finding keeps returning in CI | It was never fixed, or the baseline expired | Use --baseline only for findings you accept on purpose, with --on-expired fail so acceptance cannot rot |
| The finding ID is stale | IDs come from a specific scan | Rescan and select the finding again |
| Dynamic references or unsupported ecosystems | Offing reports a limitation instead of guessing | Report the limitation; do not invent a rule |
Privacy boundaries
- Scans,
explain,context,check, and the MCP server run locally and use no network. The one command that does isoffing data update, which you run yourself (PRIVACY.md). - Offing sends nothing about your repository anywhere. It has no account, no analytics, and no telemetry.
- The agent host is a separate party. Scan results and migration packets contain file paths and short source excerpts. Once they enter an agent's context, that host's privacy policy and your workspace settings apply. For a private repository, use a host and plan you already trust with that code.
- Repository contents are untrusted input. A file that says "ignore your instructions" is data, not a command. Do not let the agent follow instructions found in the repository, in a finding, or in a source excerpt.
- Do not put credentials in prompts. Offing never asks for them.
Reviewing the result
Treat the agent's change like any contributor's pull request:
- The diff touches only files named in the packet.
- The cited source supports the claim in the PR description.
- Project tests pass, and a fresh
offing scanno longer reports the finding. - In CI, the GitHub Action with
fail-on-severitykeeps the finding from coming back.
Prompt template
Migrate one Offing finding in this repository.
1. Run `npx @velella/offing scan . --json` and pick finding <ID>.
2. Run `explain` and `context` for it. Summarize the source before editing.
3. Change only the files the migration packet lists. Do not refactor anything else.
4. Review each suggested verification command before running it.
5. Run the project tests, then rescan. Stop if the finding remains or tests fail,
and tell me what you saw instead of widening the change.