| name | amend-plan |
| description | Adjust an existing implementation plan in `ai-plans/` after implementation has started or finished. Updates the plan file (revising existing phases or appending new ones), then for each affected phase that was already implemented adjusts its commits (`git commit --amend` or new commits) on the phase branch, force-pushes the rewritten branch, rebases every downstream stacked phase branch, force-pushes each, and refreshes the PR-context files. Use when the user says "amend the plan", "update phase N", "add a phase to plan X", "the spec changed, fix the plan", or "rewrite the implementation for phase N". NOT for one-off changes to a single file unrelated to a plan; use the regular implement skill for that. Agents push branches and open PRs via `open-pr-from-context` after review passes. |
Amend Plan
Revise a plan in ai-plans/ after work has begun. Companion to implement-plan: same agents, same review gates, same PR-context flow — but the orchestrator's job here is history rewriting instead of forward execution.
The flow is destructive (force-push). Every modification is gated on user confirmation. Default disposition for any ambiguous case is "stop and ask" — never force-push without an explicit per-branch Confirm from the user.
Unsupported commit strategy
This skill does not yet support commit_strategy = modular-commits. Detected from .vinta-ai-workflows.yaml policies.commit_strategy (or TRACKING_{plan-id}.md run_options.commit_strategy_resolved when the project policy is ask and a run is already in flight).
This project's policies.commit_strategy is ask — at amend time, read TRACKING_{plan-id}.md run_options.commit_strategy_resolved first. When it resolves to stacked-branches, the full amend flow below runs unchanged. When it resolves to modular-commits, refuse with the guidance in this section.
Amending under modular commits requires rewriting an arbitrary number of inline atomic commits across a shared plan/{plan-id-kebab} branch. The git topology is fundamentally different from the per-phase stacked branches this skill is designed around — the rewrite plan, force-push targets, and downstream rebase fan-out all differ. Full support is tracked as a follow-up.
Resolve the amendment one of three ways:
- Append a new phase — extend the plan with the change as a new
Phase N+1, then run implement-plan. Cleanest path; preserves the existing commit log.
- Hand-craft the amendment —
git rebase -i plan/{plan-id-kebab} (or git commit --fixup + git rebase --autosquash) on the plan branch, force-push, and re-run review manually. Skip this skill entirely.
- Re-run the plan from scratch on a new branch — abandon the in-flight commits (leave them for audit), regenerate the plan with plan-feature, implement forward.
Refuse with this guidance; do not proceed.
Working assumptions
-
Repo: vinta_schedule_api (Django 6 + DRF + Strawberry GraphQL + Celery, multi-tenant (OrganizationModel), Postgres, deployed to Render). Conventions: AGENTS.md.
-
Plan files: ai-plans/YYYY-MM-DD-FEATURE_NAME_PLAN.md.
-
Lint: docker compose run --rm api uv run ruff check ./. Format: docker compose run --rm api uv run ruff format ./.
-
Type / build gate: docker compose run --rm api uv run python manage.py check --deploy plus full mypy via docker compose run --rm api uv run mypy ..
-
Unit / integration tests: docker compose run --rm api uv run pytest -n auto; per-app via docker compose run --rm api uv run pytest <app>/tests/ -n auto.
-
Migrations: docker compose run --rm api uv run python manage.py makemigrations --check (gate) + docker compose run --rm api uv run python manage.py migrate (apply). Raw-SQL DB code (functions, views, materialized views, triggers, procedures) routes through common/raw_sql_migration_managers.py — see add-migration.
-
Code host: GitHub. PR creation policy: agents create PRs — every phase opens a PR via the bundled prs-context file + open-pr.sh.
-
Co-author trailer policy: forbidden. Commits must not include Co-Authored-By: AI trailers.
-
Default branch: main.
-
Branch naming convention (set by implement-plan): plan/{plan-id-kebab}/phase-{phase.id}.
When to use
- A spec change forces a phase body rewrite (different acceptance, different decisions in the plan's Guiding Decisions).
- A phase was implemented but the resulting diff is wrong / incomplete (caught post-merge to the phase branch but pre-merge to
main).
- A new phase needs to slot in between two existing ones, or be appended.
- A guiding decision in Guiding Decisions changed and it cascades into multiple phases.
When NOT to use
- Phase already merged to
main. History cannot be retroactively rewritten on main. The change must go in as a new phase appended to the plan, implemented forward via implement-plan. This skill detects this case and refuses to force-push a merged branch.
- Single-file change unrelated to a plan. Use the regular implement skill / direct edit.
- The plan never started. No commits to amend; just edit the plan file and run implement-plan normally.
Step 0 — Understand the change + parse the plan
-
Identify the plan file. Same logic as the implement-plan "Locate + parse plan" step: ask the user (path or feature name); ls ai-plans/ + grep; confirm before proceeding.
-
Capture the requested change. The user's prompt is the source. If vague, interview via AskUserQuestion:
- "Which phases are affected?" — enumerate phase ids from the plan's Phased Rollout section.
- "Is this a body rewrite of an existing phase, a new phase to slot in, or a Guiding Decisions change that cascades?"
- "What's the new acceptance criterion / change list?" — verbatim.
Don't infer scope. The plan-amend is the user's contract, not yours.
-
Parse the plan. Same structured fields as implement-plan's "Extract structured fields" step: plan id, Goals + Non-goals / Guiding Decisions / Data Model Changes / phase records from Phased Rollout / Risk & Rollout Notes through Touch List.
-
Read the tracking file ai-plans/TRACKING_{plan-id}.md if present. Its Completed Phases section tells you which phase branches were pushed and which model + base were used. If absent → git branch -a | grep plan/{plan-id-kebab} to enumerate pushed phase branches.
-
Build a per-phase state map. For every phase in the plan, record:
| Field | Source |
|---|
phase.id, phase.title | plan's Phased Rollout section |
state | one of not-started / in-progress / implemented-not-merged / merged-to-default |
branch | tracking file or git, pattern plan/{plan-id-kebab}/phase-{id} |
base | tracking file or git merge-base origin/<branch> <prev-branch>; root phase bases on main |
pr_status | .vinta-ai-workflows/prs-context/{feature-kebab}/phase-{id}.md frontmatter (pending / published) when the file exists |
merged_to_default | `git branch --merged origin/main |
merged-to-default = true blocks any commit rewrite for that phase — see Step 3 — Refuse force-pushes that can't work below.
-
Classify the requested change by phase impact, in priority order:
body-rewrite — existing phase keeps its id; body changes. Cascades downstream because rewritten commits get new SHAs.
insert-new — new phase between existing ones. Cascades downstream because every later phase rebases onto the new branch.
append-new — new phase tacked on after the last one. No downstream cascade. Implementation runs forward via implement-plan — this skill hands off after editing the plan file.
guiding-decisions-change — change inside the plan's Guiding Decisions section. Cascades into every phase that referenced the decision.
-
Evaluate amendment blast radius — recommend restart when too big. Amending in place stops being a good deal once the rewrite work approaches re-implementation. Compute these signals from the per-phase state map + the requested change:
| Signal | Threshold suggesting RESTART |
|---|
Phases needing body-rewrite ÷ total implemented phases | ≥ 50% |
guiding-decisions-change cascading into | ≥ 50% of implemented phases |
Phases in merged-to-default (immutable, must be append-new) | ≥ 2, AND remaining work also rewrites earlier phases |
| New body materially changes the data-model contract from Data Model Changes | rewrite of >2 phases hinges on it |
| Estimated touched LoC across rewrites | ≥ 70% of original implementation diff size (rough estimate via git diff --stat <base>..<branch> summed across affected branches) |
| Multi-author phase branches in the rewrite queue | ≥ 1 (force-push erases collaborator local state) |
| Approved PRs in the rewrite queue | ≥ 2 (re-review burden becomes non-trivial) |
Any two signals tripping → mark the amendment as high-blast-radius. Three or more → mark as restart-recommended.
When high-blast-radius or restart-recommended, surface to the user via AskUserQuestion before showing the standard step-8 confirmation:
- "This amendment looks large enough that restarting from scratch may cost less than rewriting in place. Tripping signals: . Restarting means: rewrite the plan as a fresh
YYYY-MM-DD-FEATURE_NAME_PLAN.md (today's date), abandon the current phase branches (leave them in place for audit), run implement-plan on the new plan from scratch. Amending in place keeps history but force-pushes branches and re-spawns implementer/reviewer/fixer agents per phase."
Options:
Restart — draft a new plan, abandon current branches
Amend in place — proceed knowing the cost (you'll show me the force-push plan next)
Stop — let me think / talk to the team first
On Restart:
- Help the user draft a new
YYYY-MM-DD-FEATURE_NAME_PLAN.md with today's date (paired with the spec, same FEATURE_NAME). This skill does not write the new plan body — point at plan-feature (or create-spec first if the spec also changed).
- Annotate the old plan: at the top, add
**Superseded YYYY-MM-DD by ../YYYY-MM-DD-FEATURE_NAME_PLAN.md** — reason: <one line>. Append the same line under ## Amendments.
- Leave the old phase branches alone — useful audit trail, no force-push needed.
- Update
TRACKING_{plan-id}.md to mark the plan superseded; preserve all completed-phase entries.
- Hand off to plan-feature. This skill exits.
On Amend in place: proceed to step 8 (the original confirmation gate, renumbered). On Stop: exit cleanly; nothing written.
When the signals show low-blast-radius (≤1 tripping), skip the recommendation entirely and go straight to step 8. Don't pad easy amendments with restart questions.
-
Confirm with user before any write. Show: requested change, classified type, list of affected phase branches with their state + merge status, downstream branches that will be rebased, the force-push plan. Use AskUserQuestion:
Proceed — I authorize the force-pushes listed
Refine — let me adjust scope (loop back to step 2)
Stop
Anything in merged-to-default state shown explicitly with a warning. Do not include those in the force-push list — user must convert those to append-new phases instead.
Step 1 — Edit the plan file
Always the first write. Plan file is durable; commits get rewritten next.
-
Body rewrites — replace the affected ## Phase {id} block inside Phased Rollout verbatim with the new body. Keep phase.id stable so branch naming stays valid.
-
Inserts — choose a new id. Two conventions are common:
- Decimal:
1.5 between 1 and 2 (matches existing patterns in some Vinta plans). Branch becomes plan/{plan-id-kebab}/phase-1.5.
- Letter:
1b between 1 (relabeled 1a) and 2. Requires renaming 1 → 1a inside Phased Rollout + updating downstream references.
Ask the user. Default: decimal — no rename of existing ids.
-
Appends — new ## Phase N+1 block at end of Phased Rollout. Same shape as siblings: Goal, Suggested AI model, reusable_skills, Changes, Tests, Acceptance.
-
Guiding Decisions changes — rewrite the affected row. Add a one-line note at the top of Guiding Decisions ("Amended YYYY-MM-DD: replaced storage shape from X to Y; affects phases 2, 3, 4.") so reviewers see what shifted. Reference the changed row by its Decision column name, not by a §N.M shorthand.
-
Bump the amendment log. At the bottom of the plan, under ## Amendments, append:
- **YYYY-MM-DD** — <one-line summary of change>. Affected phases: <ids>. Branches force-pushed: <branch-list>.
Create the section if it doesn't exist. This is the audit trail; preserve every entry.
-
Commit the plan edit on main (or wherever the plan file lives — the plan file itself is not branched per phase). Commit message: Amend plan: <summary>. Conventional Commits format: type(scope): subject — e.g. feat(calendar): add bundle availability filter, fix(public_api): correct organization scope on bookings query.
Step 2 — Build the rewrite queue
For each phase classified as needing commit rewrites (body-rewrite for already-implemented phases, downstream phases for insert-new / body-rewrite / section-2-decision-change), build a queue ordered by branch stack depth: parent first, children after.
For each entry record:
branch, base (parent in the stack — may be main or another phase branch).
change_kind: amend-existing (modify the phase body's effect on the diff) or rebase-only (parent moved, no body change for this phase).
commits_to_amend: list of SHAs the orchestrator may rewrite (look at git log <base>..<branch>).
Phases in not-started state are deferred to implement-plan — not rewritten here.
Step 3 — Refuse force-pushes that can't work
Before any write to remote, block on these conditions:
-
Phase merged to main. History on main is immutable in practice. Tell the user: "Phase X already merged to main. The amendment must be a new phase appended to the plan, not a rewrite. Re-run with classification append-new and execute via implement-plan."
-
Branch's PR was reviewed and approved. Force-pushing destroys reviewer context. Surface: list approved PRs by URL, ask AskUserQuestion:
Proceed — I'll re-request review after force-push
Stop — too disruptive, redesign as forward phase
-
Branch protection rules block force-push. gh api repos/{owner}/{repo}/branches/{branch}/protection (or glab equivalent). If the branch is protected, force-push will fail noisily — surface the rule, stop.
-
Multiple authors on the branch. git log --pretty=format:%ae <base>..<branch> | sort -u | wc -l > 1 → other developers committed too. Force-push erases their local state. Surface, require explicit Yes, I've coordinated with <names> confirmation.
If any block triggers and the user can't dismiss it: stop. Don't proceed further. Tell the user the rewrite path is unavailable; suggest an append-new phase as the fallback.
Step 4 — Per-phase rewrite loop
For each entry in the rewrite queue, in stack order:
4a. Check out the branch
git fetch origin
git checkout {branch}
git reset --hard origin/{branch}
4b. For change_kind = amend-existing only — apply the body change
Spawn an implementer subagent. Prompt shape (token-efficient, mirrors the implement-plan "Prepare agent prompt" step):
You are amending {phase.id}: {phase.title} of plan {plan.id}.
## Repo
vinta_schedule_api (Django 6 + DRF + Strawberry GraphQL + Celery, multi-tenant (OrganizationModel), Postgres, deployed to Render).
## Read first
1. AGENTS.md — repo conventions.
2. ai-plans/{plan-filename}, the **Goals + Non-goals**, **Guiding Decisions**, and **Data Model Changes** sections, plus the rewritten phase body inside **Phased Rollout**.
3. The current diff: `git diff {base}...HEAD` — what's already on this branch.
## What changed in the plan (verbatim)
{Diff between old phase body and new — produce via `diff <(old-body) <(new-body)`. Or, if the plan was rewritten in place, the new body verbatim with a note "this replaces what was here before".}
## Your task
Bring the diff on this branch into compliance with the new phase body. You may:
- Edit existing files this branch already modified.
- Add new files when the new body requires them.
- Remove files this branch added that the new body no longer needs.
## How to record the change
Default: ADD A NEW COMMIT on top of the existing branch. Title: "Amend phase {phase.id}: <summary>". This preserves the original implementation as a separate commit and makes the amendment auditable in `git log`.
Use `git commit --amend` ONLY when:
- The branch has exactly one commit, AND
- The amendment is small (≤30% of the original diff size), AND
- The user explicitly authorized amend in Step 0.
## Adding new third-party dependencies
Before running any install command (`npm add`, `pnpm add`, `yarn add`, `pip install`, `poetry add`, `uv add`, `cargo add`, `go get`, `gem install`, equivalents), check the package's SPDX license against the project's forbidden list — see the **Dependency licenses** section in [AGENTS.md](AGENTS.md) for the full list, the per-package overrides, and any project-specific notes.
Quick lookup:
- **npm / pnpm / yarn**: `npm view <pkg> license`.
- **PyPI**: `pip index versions <pkg>` then read the project metadata, or open `https://pypi.org/project/<pkg>/`.
- **Cargo**: `cargo metadata --format-version 1 | jq '.packages[] | select(.name=="<pkg>") | .license'` (after a temporary `cargo add` in a scratch dir, or read `Cargo.toml` upstream).
- **Go**: open the module's repo `LICENSE` file directly.
- **Gem**: `gem specification <pkg> licenses`.
If the license is in the forbidden list AND the `(package, license)` pair is **not** listed under **Approved overrides** in AGENTS.md:
1. Stop. Do not run the install command.
2. Surface the violation to the user with: package name, SPDX identifier, why it's forbidden, link to the upstream license.
3. Offer alternatives (search the ecosystem for an MIT / Apache-2.0 / BSD-licensed equivalent) before asking for an override.
4. If the user grants a one-off override, the orchestrator must record it in `policies.dependency_licenses.allowed_overrides[]` of `.vinta-ai-workflows.yaml` (package + SPDX + one-line reason) before re-running the install. Undocumented overrides leak into the diff and the reviewer agent will flag them.
**License unknown / undeclared.** When the lookup above returns no license, an empty value, `UNKNOWN`, `SEE LICENSE IN <file>`, or only an unstructured `LICENSE` file in the repo with no SPDX identifier, treat it as a **policy decision the user owns** — don't guess, don't auto-infer, don't fall back to "assume MIT". The package may be unlicensed (all-rights-reserved by default in most jurisdictions), proprietary, or simply missing metadata.
1. Stop. Do not run the install command.
2. Surface to the user: package name, what was found (e.g. "the `license` field is absent in `package.json`", "PyPI metadata returned `UNKNOWN`", "no LICENSE file in the repo"), the upstream repo / registry URL so the user can verify.
3. Ask via `AskUserQuestion`: `Skip — find a licensed alternative`, `Treat as forbidden — refuse install`, `Treat as allowed — record an override` (the third option only when the user has independently confirmed the license off-channel; record the resolved SPDX in `allowed_overrides[]` with the source in the `reason` field, e.g. `"unlicensed but author confirmed MIT via GitHub issue #42"`).
4. Don't add the dep until the user picks one of the three.
Transitive deps follow the same rule, but checking every transitive license at install time is impractical — the project's CI (or a separate license-audit run) handles the deep walk. The subagent's responsibility is the **direct** add.
## Working instructions (same loops as implement-plan)
1. Read existing code paths your changes touch.
2. Edit. Match existing patterns.
3. Inner loop: `docker compose run --rm api uv run ruff check ./` → `docker compose run --rm api uv run pytest <new-test-path> -vs` → scoped suite (`docker compose run --rm api uv run pytest <app>/tests/ -n auto`).
4. Iterate 2–3 until green.
5. Outer gate: `docker compose run --rm api uv run python manage.py check --deploy` AND `docker compose run --rm api uv run pytest -n auto`.
6. Stage explicitly: `git add <app>/... vinta_schedule_api/... ai-plans/... tests/... — explicit per-path`.
7. Commit. Conventional Commits format: `type(scope): subject` — e.g. `feat(calendar): add bundle availability filter`, `fix(public_api): correct organization scope on bookings query`.
8. Do **not** add `Co-Authored-By: Claude` (or any other AI) trailer to commits — the project forbids them.
9. **Do NOT push. Do NOT force-push.** Orchestrator owns the remote.
## Required output
- Status: SUCCESS or FAILURE.
- New commit SHA(s) added (or amended SHA).
- 5–15 line summary.
- Deviations from new body + reasoning.
For change_kind = rebase-only (downstream phase whose parent moved): skip the agent. The work is purely git topology.
4c. Run the three-layer review (same as implement-plan)
Same Layer 1 / 2 / 3 / fix-loop as the implement-plan "Thorough review" step. The reviewer agent gets the new phase body to walk against. Layer 2 walks: every "Changes" item in the new body, every "Tests" entry, the new acceptance line.
Skip Layer 3 only when change_kind = rebase-only (no body change → no compliance walk). Layers 1 + 2 still apply (verify the rebase didn't lose unrelated work).
4d. Rebase onto the (possibly-rewritten) parent
git fetch origin
PARENT_TIP=$(git rev-parse origin/{base})
git rebase $PARENT_TIP
Conflicts:
- Spawn a fixer subagent with the conflict body + new phase body + parent's tip diff. Same agent type as the implement-plan "Fix loop" subsection.
- Fixer resolves, runs inner + outer gate.
- Orchestrator continues the rebase:
git rebase --continue.
Repeat until the rebase finishes clean. If the fixer can't resolve after one retry → stop. Surface to user; do not push a half-rebased branch.
4e. Force-push (with confirmation)
AskUserQuestion:
Force-push <branch> now (was authorized in Step 0)
Pause — let me look at the local state first
On confirm:
git push --force-with-lease origin {branch}
Use --force-with-lease, not --force. It refuses to overwrite if the remote moved since the last fetch — protects against another developer's pushes the orchestrator didn't see.
If --force-with-lease rejects: another developer pushed. Stop. Re-fetch, re-apply, re-confirm.
4f. Refresh the PR-context file (when present)
For the rewritten branch, look for .vinta-ai-workflows/prs-context/{feature-kebab}/phase-{phase.id}.md:
- File missing — skip; nothing to refresh.
- File
status: pending — rewrite the file to reflect new title / description / comments per the prs-context-template. Status stays pending. The user will publish later via open-pr-from-context.
- File
status: published — the existing PR is auto-updated by the force-push (GitHub/GitLab pick up the new tip). But:
- Inline comments may now reference SHAs that no longer exist. They'll appear as "outdated" in the PR UI.
- If the new diff has materially different comment-worthy spots, regenerate the
# Comments block, set status: pending, and re-run open-pr.sh on the file. The script reuses the existing PR, posts new comments. Old "outdated" comments stay visible in the PR for audit; that's the platform's behavior.
When rewriting the # Description body, honor project.pr_template_paths from .vinta-ai-workflows.yaml — same rule as step 2 of the implement-plan "Open PR via context file" step: follow the project's PR template structure, fill new sections with phase-specific content from the rewritten body, leave un-fillable placeholders untouched. If the prior file used a different template than the project now declares (template was added or swapped between the original implement-plan run and this amend), prefer the current pr_template_paths choice — surface the change to the user when the body shape shifts visibly.
Always include in the publish-log block at the bottom of the file:
- YYYY-MM-DDThh:mm:ssZ — branch force-pushed (amend-plan); old SHA <x>, new SHA <y>
4g. Update tracking file
Update ai-plans/TRACKING_{plan-id}.md for the rewritten phase:
- Append to its
Completed Phases entry: Amended YYYY-MM-DD: <summary>; new SHA <x>; force-pushed.
- Don't remove the original summary — keep history.
Step 5 — Final report
After every queue entry processes:
-
Print a per-branch summary:
plan/{plan-id-kebab}/phase-1 amended (commits added: 1; force-pushed)
plan/{plan-id-kebab}/phase-2 rebased (no body change; force-pushed)
plan/{plan-id-kebab}/phase-3 rebased (no body change; force-pushed)
plan/{plan-id-kebab}/phase-4 pending (not yet implemented; will use new parent next implement-plan run)
-
List any PR-context files now at status: pending that need re-publishing.
-
List any phases blocked from rewrite (Step 3 refusals) with the recommended forward path.
-
Reminder: reviewers on existing PRs need a re-review request — force-push erases context. Send a short comment on each affected PR (the orchestrator can do this via the PR CLI if the project's PR policy = "agents create PRs"; otherwise hand off to the human).
Important rules
- Never
--force. Always --force-with-lease. Protects against silent overwrites.
- Plan file edit is the first write. Commit the new plan body before any branch rewrite. The plan is the contract; the branches are the artifact.
- Recommend restart when blast radius is high. The blast-radius step inside Step 0 evaluates signals; ≥2 tripping signals → surface a restart option to the user before any force-push plan is shown. Don't quietly amend a half-rewrite of the whole plan.
- Never use
§N shorthand to point at sections — neither in this skill body, the rewritten plan body, the amendment log entry, nor any prs-context refresh. Always use the section's full name (and link when possible). §N references are hard for humans to follow and shift when section numbering moves.
- Phases merged to
main are immutable. Convert to append-new phases. Refuse to attempt rewrites.
- Confirm every force-push individually. No batch "confirm all".
- Use
--force-with-lease (already stated; worth restating).
- Three-layer review on every rewritten branch. Same standard as implement-plan. The amendment isn't done until Layer 3 passes.
- PR-context file is a derived artifact. Refresh it after the rewrite; never edit the file as a substitute for fixing the diff.
- Subagents commit but never push. Orchestrator owns force-push. or open PRs themselves.
- No AI co-author trailers in commits. The project forbids them; treat any AI trailer as a BLOCKER.
- License check before any new dep. Refuse
npm add / pnpm add / pip install / poetry add / uv add / cargo add / go get when the package's SPDX license is in the forbidden list — see AGENTS.md Dependency licenses. User can grant a one-off override after acknowledging the violation; record the override in policies.dependency_licenses.allowed_overrides before re-running.
- Stop on Tier-4 failure (model escalation, same rules as the implement-plan "Pick model" step).
- Stop on rebase failure the fixer can't resolve in one retry. Don't ship half-rebased branches.
Quick checklist (orchestrator, per amendment run)
What this skill does NOT do
- Does not execute new (
not-started) phases. That's implement-plan's job. Edit the plan, then hand off.
- Does not rewrite history on
main. Refuses up front.
- Does not auto-bypass branch protection. Surface the rule, stop.
- Does not amend commits made by humans on the branch unless explicitly authorized. Multi-author branches require explicit confirmation.
- Does not delete the plan file or its branches even when an amendment makes some phases obsolete. Mark obsolete phases in the plan body with
**Superseded YYYY-MM-DD by phase {new-id}**; leave their branches alone (or let the user delete manually).