| name | define-change |
| description | Brownfield planning for one feature or serious change to an existing app — audit the code's blast radius, decide how the change lands through a user-triaged ledger under docs/planning/changes/, and deliver an EPIC_N.md (with GitHub milestone + tracking issue) that create-issues consumes unchanged. Use for planning a feature, refactor, or migration in existing code (not brand-new projects, not trivial one-file fixes), or to resume a change ledger with open decisions. |
Define Change
A change to an existing app is not a new project: the stack is decided, the
conventions exist, and most "options" are constrained by code that already runs. What
still needs deciding is how the change lands — extend or refactor, migrate or
version, flag or big-bang — and those are the user's calls, not yours. This skill
walks them through a decision ledger (same mechanics as define-scope — its four
principles apply throughout: facts looked up never asked, decisions the user's never
yours, triage is the dial, nothing finalized until confirmed) and delivers one
docs/epics/epic-<N>-<slug>/EPIC_<N>.md, format-identical to split-epics output,
so create-issues and implement-epic work downstream without knowing which
pipeline produced it.
One change = one epic. That's the contract; the size guard in Step 2 protects it.
Output format: OKF
Both directories this skill touches — docs/planning/ (where it adds
changes/change-<N>-<slug>/) and docs/epics/ (which it establishes if absent,
including log.md) — are separate OKF v0.1 bundles. Three shared references define
what this skill writes, and it reads all three before writing anything:
../_shared/bundle-interfaces.md (English content, bundle and link rules, reserved
files, committing what you write), ../_shared/ledger-interfaces.md (the decision
doc), and ../_shared/pipeline-interfaces.md (the epic file schema). A fourth governs
nothing it writes: ../_shared/feedback-interfaces.md, the closing reflex for what
this run teaches about this skill, read at the handoff.
Layout this skill owns:
docs/planning/changes/
change-<N>-<slug>/
index.md # decision listing = durable triage view (no frontmatter)
01-<slug>.md # one Decision concept doc per decision
docs/epics/
epic-<N>-<slug>/
EPIC_<N>.md # the deliverable (written last)
The change number and the epic number are assigned independently: each is the next
free number in its own directory (docs/planning/changes/change-* and
docs/epics/epic-* respectively) — they will often differ, and that's fine; the
epic's frontmatter records the linkage.
Step 0: Prerequisites and resume
Resume first: if any docs/planning/changes/change-<N>-<slug>/ exists with open
decisions, this is a resume. Read its index.md and every decision doc, report the
tally (decided / open / n-a), and jump to the matching step: open decisions → Step 4
or 5; all decided but no EPIC file → Step 6; EPIC written but gh_issue: null (a
run where gh was skipped or failed) → Step 6.3 to finish only the GitHub objects.
Never re-ask a decided item. If several
changes are in flight, ask which one (or whether this is a new change).
Prerequisites: read docs/planning/SPECS.md and docs/planning/CONVENTIONS.md —
plus docs/planning/DRIFT.md when it exists: the register of standards earlier
implementation proved unworkable (../_shared/pipeline-interfaces.md). It is the
cheapest correction available to the audit below, because it names where the two
documents above already disagree with the code.
- Missing (either): stop and say so. Offer to run the
map-codebase skill first
— it reverse-engineers the codebase into exactly these two files. Do not inline a
half-baked audit as a substitute: every recommendation below leans on these docs,
and a wrong stack assumption poisons the whole ledger. If the user insists on
proceeding without them, they can — but note it in the ledger docs so downstream
readers know the ground truth was verbal.
- Present but stale:
map-codebase stamps SPECS.md's frontmatter with a
mapped_commit extension field. Compare it with git rev-parse HEAD; if they
differ, report roughly how far the docs trail the code (git rev-list --count <mapped_commit>..HEAD commits) and offer a map-codebase refresh — but let the
user proceed on the existing docs if they judge the drift irrelevant to this
change. No mapped_commit field (greenfield-authored docs) → nothing to check.
Step 1: Intake
Take whatever the user gives — a sentence, a spec fragment, a rant about what the app
can't do. Separate what's decided (things the user has clearly already chosen) from
what's open. Read SPECS.md, CONVENTIONS.md, and any docs the user points at before
asking anything. Only for genuine blanks that block the audit ("which of these two
apps in the monorepo?", "is there a deadline?") ask orienting fact-questions — one at
a time, few, and stop as soon as you can start the audit. If you can draft a credible
recommendation without asking, don't ask; triage is where the user corrects you
cheaply.
Step 2: Impact audit — facts, not questions
This is the step define-scope doesn't have, and it's why brownfield planning gets
its own skill: before any decision is enumerated, read the actual code and
establish the blast radius. Never ask the user what the code does — that is always a
lookup. Establish:
- Modules and files touched — where the change lands, what it must modify versus
merely call.
- Contract breaks — public APIs, CLI flags, exported types, events, file formats:
what existing consumers would break, and who those consumers are.
- Schema and data migrations — tables/collections affected, whether existing rows
need transforming, whether the migration is reversible.
- Affected tests — which suites cover the touched modules, which will break by
design versus by accident.
- Feature interactions — existing behavior that overlaps, conflicts, or silently
assumes the thing being changed.
Record the audit as facts inside the relevant decision docs' Question sections (the
audit is why each decision exists) — it doesn't need a standalone document.
Size guard — apply it here, before the ledger exists. If the audit reveals the
change is really multi-epic sized — several independently shippable chunks, weeks
each, spanning unrelated subsystems — say so plainly and recommend the
define-scope → split-epics route instead: that pipeline exists precisely to cut
big scopes into epics, and stretching one EPIC file to hold it would just push the
overload onto create-issues. Let the user either shrink the change to one-epic
size or switch pipelines. Don't proceed into the ledger with a change you believe is
oversized without flagging it.
Step 3: Enumerate the decision ledger
Pick the change's slug (lowercase kebab-case, ASCII, ~40 chars) and number <N>
(next free under docs/planning/changes/). Build the decision list from the audit
plus the user's input. Typical categories — instantiate the ones this change raises,
skip the rest (unlike define-scope's checklist, there's no fixed coverage guarantee
to mark N/A against, though status: na with a one-line reason is still the right
move for a category you considered and ruled out):
- Approach — extend the existing implementation, refactor first, or rewrite the
affected part. Almost always first; most other recommendations hang off it.
- Data migration / backward compatibility — transform existing data or leave it,
support old formats/clients or cut them.
- Rollout strategy — feature flag, big bang, phased by user segment.
- API/contract versioning — version the endpoint, evolve in place, deprecation
window for the breaks the audit found.
- Scope boundary — which adjacent cleanups the audit surfaced are explicitly OUT
(the brownfield equivalent of non-goals; without this, every change snowballs).
- Acceptance criteria — what "done" observably means for this change.
Add project-specific decisions the audit raises that no list anticipates. Number in
dependency order, record depends_on. One file per decision,
docs/planning/changes/change-<N>-<slug>/<nn>-<slug>.md, using the decision doc
template from ../_shared/ledger-interfaces.md (tags: [decision, change],
phase: change, plus the change/change_slug extension fields). Ground every
Question in audit facts (name the files, the contracts, the row counts) and every
Recommendation in SPECS.md/CONVENTIONS.md — "it depends" is not a recommendation.
Create change-<N>-<slug>/index.md (no frontmatter) listing every decision
mechanically: * [Title](<nn>-<slug>.md) - <status>.
Step 4: Triage — the batch pass
The batch pass exactly as ../_shared/ledger-interfaces.md defines it, over the
ledger Step 3 built, with change-<N>-<slug>/index.md as the index it keeps current.
Step 5: Deep-dive the flagged items
The deep-dive pass exactly as ../_shared/ledger-interfaces.md defines it, restating
each question with the audit facts that motivated it. The refresh rule bites hardest
on the approach decision: "rewrite" instead of "extend" routinely flips the migration
and rollout recommendations that hang off it.
Step 6: Confirm, then write the epic
When nothing is open, summarize the decided set in a few lines and get the user's
explicit confirmation — this step also gates the GitHub writes below, so it does the
job of split-epics' preview step. Only then:
1. Establish docs/epics/ if absent (bundle root index.md + log.md, per the
OKF section above). Pick the epic number <N> — next free across docs/epics/
regardless of which skill created the existing epics — and the epic slug (usually
the change slug).
2. Write docs/epics/epic-<N>-<slug>/EPIC_<N>.md — the epic file template
from ../_shared/pipeline-interfaces.md, format-identical to split-epics output
so create-issues works unchanged, with tags: [epic, change] and
source: docs/planning/changes/change-<N'>-<slug>/index.md.
Fill the body from the decided ledger only — settled fact, no option-weighing. Goal
from the intake + approach verdict; Scope from the approach/migration/rollout
verdicts, concrete enough that the audit's file-level findings survive (this file,
not the ledger, is what create-issues treats as the source of truth); Out of scope
straight from the scope-boundary verdict; Acceptance criteria from its decision;
Dependencies is usually "None" (link other epics OKF-style, bundle-relative, if this
change genuinely depends on one). ## Context links the planning bundle with plain
relative paths — [Technical specs](../../planning/SPECS.md),
[Conventions](../../planning/CONVENTIONS.md) — and the change ledger
(../../planning/changes/change-<N'>-<slug>/index.md) so implementers are one click
from every rationale. Notes carries surviving risks and audit findings worth
preserving. Don't add resource yet — it arrives with the GitHub issue.
3. Create the GitHub milestone and tracking issue exactly as split-epics does
(its Steps 6–7). In short: get owner/repo (gh repo view --json nameWithOwner -q .nameWithOwner); find-or-create a milestone titled Epic <N>: <title> (split-epics
bundles scripts/ensure_milestone.sh for this — use it if that skill is installed,
otherwise find via gh api repos/<owner>/<repo>/milestones and create only if
missing); reuse the repo's existing epic label or create a plain epic one; create
the issue with gh issue create --title "Epic <N>: <title>" --body-file <tmpfile> --milestone "Epic <N>: <title>" --label epic, the body summarizing
goal/scope/acceptance criteria and linking docs/epics/epic-<N>-<slug>/EPIC_<N>.md.
Then write back to the EPIC frontmatter: status: open, gh_issue: <number>,
milestone: <milestone number>,
resource: https://github.com/<owner>/<repo>/issues/<number>, refreshed timestamp.
No remote, or gh fails? Degrade gracefully: keep the local files, leave
gh_issue/milestone as null and status: draft, and tell the user exactly what
was skipped and how to finish later (fix gh auth / add a remote, then re-run —
Step 0's resume path lands back here, and create-issues refuses politely until
gh_issue is set).
4. Update both bundles: add the epic's bullet to docs/epics/index.md (tracking
its description + status); append a docs/epics/log.md entry; update
docs/planning/ root index.md to list the change ledger and append a
docs/planning/log.md entry. If the okf-docs validator is available, run it against
both bundles. Then commit and push both per the commit rule in
../_shared/bundle-interfaces.md — the epic issue you just created links back to
EPIC_<N>.md, and that link 404s for everyone until the push lands.
Idempotency: if the EPIC file already exists with gh_issue set, don't recreate
either it or its GitHub state — report and stop. If the ledger changed since the
epic was written (a reopened decision), regenerate the affected body sections, never
touch gh_issue/milestone, and log the update.
Handoff
Tell the user the next step: create-issues on this epic to break it into PR-sized
issues, then implement-epic to run them. The change ledger stays put — it's the
durable rationale behind every line of the EPIC file.
Where this change introduces something the codebase didn't already depend on — a new
service, a hosted dependency, a key, a toolchain — mention check-prerequisites in the
same breath, once: it probes those and returns what only the user can supply, before
implement-epic gates on the same list. Brownfield mostly builds on what already runs,
so this is a pointer for the changes that add a dependency, not a standing step.
Then close the run per ../_shared/feedback-interfaces.md — silently, unless this run
turned up something about this skill that clears both its filters.
Revisiting a decision later
Same as define-scope: reopen with status: open, keep the old verdict visible as
history in the Verdict section, refresh dependents, and — if the EPIC file exists —
regenerate its affected sections after the new verdict, with log entries in both
bundles. If implementation already started (issues/ exists under the epic), point
out which issues the reversal touches before regenerating anything.