docs/installer-migrations.md
This document defines the migration layer for GSD installs and upgrades. It is for contributors who need to retire files, move install surfaces, rewrite runtime config, or preserve user data while changing how GSD is installed.
After reading this document, a contributor should be able to add a new installer migration without guessing which files are safe to remove or how to protect local user changes.
The installer already handles several upgrade behaviors:
Those behaviors are currently distributed across install branches. That works for isolated fixes, but it makes feature retirement risky. A future change can remove a file from the package while leaving stale installed copies behind, or delete a user-created file because it happens to live inside a GSD-managed directory.
The migration layer exists to make upgrade behavior explicit, reviewed, and repeatable.
Managed file
A file that GSD installed and recorded in the install manifest. Managed files can be replaced automatically when unchanged. If changed locally, they must be backed up or merged.
User-owned file
A file created or maintained by a user workflow or by the user directly. These files must never be removed just because they sit under a GSD directory.
Unknown file
A file found under an install root that is not in the manifest and is not classified as user-owned. Unknown files are preserved unless a migration explicitly classifies them with evidence.
Migration
A versioned change set that can inspect the current install, produce a plan, and apply that plan after safety checks pass.
Plan
A list of proposed filesystem and config actions. A plan is safe to show to a user. It describes what will happen and why, without mutating disk.
Journal
A per-run record of applied actions and rollback data. It exists so failed installs can restore the pre-run state where possible.
The migration layer uses the existing file manifest and adds one install-state record.
The existing manifest remains the ownership baseline. It records the installed GSD version, install mode, and hashes for distribution-owned files.
The invariant is strict:
The installer writes an install-state file next to the manifest.
Required fields:
{
"schema": 1,
"runtime": "codex",
"scope": "global",
"installed_version": "1.50.0",
"install_mode": "full",
"applied_migrations": [
{
"id": "2026-05-11-codex-hooks-layout",
"package_version": "1.50.0",
"checksum": "sha256:...",
"applied_at": "2026-05-11T00:00:00.000Z"
}
]
}
The checksum is calculated from the migration definition. If an applied migration's checksum changes, the installer must warn and refuse to silently re-run it. Fix-forward migrations should use a new migration id.
Each migration exports a plain record plus pure planning logic.
Required fields:
module.exports = {
id: '2026-05-11-runtime-layout-example',
title: 'Move legacy commands into runtime skills',
description: 'Move legacy runtime command files into the generated skill layout.',
introducedIn: '1.50.0',
runtimes: ['claude', 'codex', 'gemini'],
scopes: ['global', 'local'],
destructive: true,
plan(ctx) {
return [];
}
};
The Installer Migration Authoring Guard Module rejects records that omit id,
title, description, introducedIn, scopes, destructive, or plan.
runtimes remains optional only for migrations intentionally shared by every
runtime, but scope must always be explicit so an author cannot accidentally
broaden local/global behavior.
The plan(ctx) function receives an install context with runtime, scope,
target directory, previous manifest, install state, package manifest, and
filesystem helpers. It returns actions. It must not mutate disk.
Migrations may use helper predicates such as:
isManaged(relPath)isUserOwned(relPath)hashMatchesManifest(relPath)exists(relPath)readJson(relPath)readToml(relPath)Migrations produce a small set of action types. The executor owns mutation, backup, rollback, and reporting.
Remove a path only when it is known to be GSD-managed and unchanged from the previous manifest, or when the migration provides a purpose-built detector for an old GSD-owned shape.
Authoring guardrail: every remove-managed action must include
ownershipEvidence explaining the manifest entry, generated marker, or
purpose-built detector that proves GSD ownership.
Use for retired hooks, old generated agents, deprecated command files, and stale runtime-specific generated artifacts.
Back up a managed path before removal because the file differs from the previous manifest. The user gets a clear report and can inspect the backup.
Use when a feature retires a managed file that users may have patched.
Move a managed path to a new managed path. If the source was locally modified,
the action becomes backup-and-move or a conflict.
Use for layout migrations such as command directories moving into skills.
Rewrite a structured config file through a parser or existing structural helper. String replacement is only acceptable for narrowly-scoped marker blocks with tests for line-ending and ordering variations.
Use for runtime config, hook registrations, feature flags, and generated agent registration blocks.
The initial executor support is rewrite-json: a migration reads JSON through
readJson(relPath), returns the next parsed value in the action, and may set
deleteIfEmpty: true when the remaining structure is empty. The executor owns
the disk write, journal entry, rollback snapshot, and runtime/scope filtering.
Use this for legacy JSON config cleanup such as Codex hooks.json, where GSD
can prove ownership of individual generated hook commands but not the whole
file.
Authoring guardrail: every rewrite-json action must include
ownershipEvidence, and the migration record must include runtimeContract
citing docs/installer-migrations.md#runtime-configuration-contract-registry.
Declare that a path is user-owned and must survive surrounding directory replacement. This action is informational in dry-run output and blocks non-interactive apply until a later interactive baseline migration can ask for an explicit user choice.
Use for profile, preferences, hand-authored instructions, and future workflow outputs.
Record a manifest-managed file in the first-time baseline without mutating it. The executor writes a journal entry and install-state entry so later upgrades know the baseline scan completed.
Use only from the first-time baseline scanner.
Record a user-owned or unknown file discovered under a known install surface without mutating it. Unknown files default to this action unless they look like retired GSD-generated artifacts that need an explicit user choice.
Use only from the first-time baseline scanner.
Stop non-interactive destructive migration and ask in interactive mode. The prompt must present concrete choices such as preserve, back up, remove, or move. The default is preserve.
Use when classification is ambiguous and guessing could lose data.
The installer runs migrations before materializing the new package payload.
The Phase 4 install integration wires this flow into the normal install/update
entry point for every supported runtime: Claude Code, Antigravity, Augment,
Cline, CodeBuddy, Codex, Copilot, Cursor, Gemini, Hermes Agent, Kilo, OpenCode,
Qwen Code, Trae, and Windsurf. The installer invokes the same migration runner
with baselineScan: true, reports the projected action rows, applies safe
non-interactive actions before materialization, persists install state only after
package materialization and finalization succeed, and fails before writing new package files when the runner
returns blocked user-choice actions.
Phase 1-3 built the planning, apply, rollback, install-state, baseline, and migration-record mechanics. Those phases did not prove the normal install entry point across every runtime. Phase 4 owns that guardrail with an all-runtime install matrix that exercises safe managed cleanup and blocked user-choice artifacts for each runtime above.
If any apply step fails, the executor uses the journal to restore modified paths where possible. Rollback must never delete files that were not created or modified by the current installer run.
The migration runner supports a dry-run mode that prints the plan and exits without changes.
Dry-run output groups actions by risk:
The same planner powers dry-run and apply. There must not be a separate "preview-only" code path.
Never remove an unknown file. Unknown files are preserved unless a migration contains a specific detector proving the file is a stale GSD artifact.
When a path is in the previous manifest:
User-owned artifacts are defined once and consumed by both preservation and manifest-writing code. Adding a user-owned artifact requires a regression test that proves it is preserved across reinstall and omitted from the manifest.
Runtime config is mixed ownership. GSD may own marker blocks, generated agent sections, or hook entries, but it does not own the whole file unless the file was created as a GSD-only file. Config migrations should remove or rewrite only the owned portion.
Last upstream documentation check: 2026-05-11.
This registry is the source of truth for migrations that touch host runtime configuration. Each row records:
Migration authors must read the matching row before producing a
rewrite-config, move-managed, or destructive cleanup action. If upstream
docs change, update this registry, update docs/ARCHITECTURE.md, and add tests
for the new shape before changing migration behavior.
| Runtime | What GSD installs | Where GSD installs it | Config ownership boundary | Upstream contract snapshot |
|---|---|---|---|---|
| Claude Code | Global skills in skills/gsd-*/SKILL.md; local slash commands in commands/gsd/*.md; agents in agents/gsd-*.md; hooks in hooks/; settings.json registrations | Global CLAUDE_CONFIG_DIR or ~/.claude; local ./.claude | GSD owns only generated skills, local commands, gsd-* agents, hook files, and GSD hook/statusLine entries in settings.json | Slash commands, settings, hooks, subagents; docs not versioned, checked 2026-05-11 |
| OpenCode | Flat markdown commands in command/gsd-*.md; agents in agents/gsd-*.md; config updates in opencode.json or opencode.jsonc | Global OPENCODE_CONFIG_DIR, dirname(OPENCODE_CONFIG), XDG_CONFIG_HOME/opencode, or ~/.config/opencode; local ./.opencode | GSD owns generated command/agent files and GSD entries in structured config only | Config; docs published 2026-05, checked 2026-05-11 |
| Kilo | OpenCode-style flat markdown commands in command/gsd-*.md; agents in agents/gsd-*.md; config updates in kilo.json or kilo.jsonc | Global KILO_CONFIG_DIR, dirname(KILO_CONFIG), XDG_CONFIG_HOME/kilo, or ~/.config/kilo; local ./.kilo | GSD owns generated command/agent files and GSD entries in structured config only | Custom subagents; docs not versioned, checked 2026-05-11 |
| Gemini CLI | TOML slash commands in commands/gsd/*.toml; agents in agents/gsd-*.md; settings.json feature flag, hooks, and statusline | Global GEMINI_CONFIG_DIR or ~/.gemini; local ./.gemini | GSD owns generated commands/agents/hooks and only GSD settings entries; local command copy may be skipped when global GSD commands already exist | Custom commands, configuration; docs checked 2026-05-11 |
| Codex | Skills in skills/gsd-*/SKILL.md; agents as source markdown plus per-agent TOML in agents/; [agents.gsd-*] and hooks in config.toml | Global CODEX_HOME or ~/.codex; local ./.codex | GSD owns generated skills, generated agent TOML, agents.gsd-* config sections, [features].codex_hooks when added by GSD, and GSD hook entries | Codex config schema, Codex developer docs; docs not versioned, checked 2026-05-11; installer compatibility sentinel: Codex 0.124.0 agent table shape |
| GitHub Copilot | Skills in skills/gsd-*/SKILL.md; agents as .agent.md; repository instructions in copilot-instructions.md | Global COPILOT_CONFIG_DIR or ~/.copilot; local ./.github | GSD owns generated skill/agent files and GSD-authored instruction files; no hook/statusline ownership | Repository custom instructions, Copilot CLI custom instructions; GitHub Docs product docs, checked 2026-05-11 |
| Antigravity | Skills in skills/gsd-*/SKILL.md; agents in agents/; Gemini-style settings.json hooks when installed by GSD | Global ANTIGRAVITY_CONFIG_DIR or ~/.gemini/antigravity; local ./.agent | GSD owns generated skills/agents/hooks and GSD settings entries only | Checked 2026-05-11 against available Antigravity install/config material; this row records GSD's Gemini-compatible settings contract as the compatibility baseline for installer migrations. |
| Cursor | Skills in skills/gsd-*/SKILL.md; agents in agents/; rule references under rules/ | Global CURSOR_CONFIG_DIR or ~/.cursor; local ./.cursor | GSD owns generated skills/agents and GSD rule files or references; no hook/statusline ownership | Cursor rules; docs not versioned, checked 2026-05-11 |
| Windsurf | Skills in skills/gsd-*/SKILL.md; agents in agents/; rule references under rules/ | Global WINDSURF_CONFIG_DIR or ~/.codeium/windsurf; local ./.windsurf | GSD owns generated skills/agents and GSD rule files or references; no hook/statusline ownership | Windsurf public rule docs were source-limited in search results as of 2026-05-11; installer targets the common workspace rules convention ./.windsurf/rules and must be rechecked before migrations rewrite rules |
| Augment Code | Skills in skills/gsd-*/SKILL.md; agents in agents/ | Global AUGMENT_CONFIG_DIR or ~/.augment; local ./.augment | GSD owns generated skills/agents only; no hook/statusline ownership | Augment Agent Skills, Augment IDE skills; IDE skills public beta in VS Code 0.789.0+, checked 2026-05-11 |
| Trae | Skills in skills/gsd-*/SKILL.md; agents in agents/; rule references under rules/ | Global TRAE_CONFIG_DIR or ~/.trae; local ./.trae | GSD owns generated skills/agents and GSD rule files or references; no hook/statusline ownership | Public Trae docs expose AI settings and .rules announcements, but no stable skills/config API was found as of 2026-05-11; migrations must treat this row as source-limited |
| Qwen Code | Claude-compatible skills in skills/gsd-*/SKILL.md; agents in agents/; optional common hook/settings integration through GSD | Global QWEN_CONFIG_DIR or ~/.qwen; local ./.qwen | GSD owns generated skills/agents/hooks and GSD settings entries only | Qwen commands and skills; docs last updated 2026-05-06 |
| Hermes Agent | Category skills under skills/gsd/ with DESCRIPTION.md plus nested gsd-*/SKILL.md; agents in agents/; optional common hook/settings integration through GSD | Global HERMES_HOME or ~/.hermes; local ./.hermes | GSD owns generated skills/gsd/ category content, generated agents, and GSD settings entries only | Hermes configuration, Hermes skills, working with skills; docs checked 2026-05-11 |
| CodeBuddy | Skills in skills/gsd-*/SKILL.md; agents in agents/; optional common hook/settings integration through GSD | Global CODEBUDDY_CONFIG_DIR or ~/.codebuddy; local ./.codebuddy | GSD owns generated skills/agents/hooks and GSD settings entries only | CodeBuddy CLI skills, CodeBuddy IDE skills; docs checked 2026-05-11 |
| Cline | Rule-based integration via .clinerules for current installer output | Global CLINE_CONFIG_DIR or ~/.cline; local project root .clinerules | GSD owns the generated .clinerules file only when it created or manifest-tracked it; no hooks/statusline ownership | Cline rules; docs prefer .clinerules/ directory and still detect legacy rule files, checked 2026-05-11 |
Before applying a migration, the executor records enough data to restore:
Rollback is best-effort but must be loud when incomplete.
The first migration should classify an existing install rather than attempt to fix every historical layout.
It should:
The Phase 3 implementation adds a gated baseline migration record,
2026-05-11-first-time-baseline-scan. The runner passes baselineScan: true
when the installer wants this first-time scan. Without that flag, discovery is
safe for normal migration runs and the baseline record plans no actions.
The baseline action contract is:
record-baseline for manifest-managed filesbaseline-preserve-user for known user-owned files and unknown files that do
not look like stale GSD-generated artifactsprompt-user for stale GSD-looking artifacts that are not manifest-provenThis baseline is the escape hatch for old installs that predate full migration tracking. It gives the user a reviewable redistribution/removal plan without requiring the installer to infer every past release transition perfectly.
When a feature removes or moves install artifacts, the PR must include:
The author must answer these questions in the migration file:
Every migration runner change should cover:
This sequence keeps the first implementation small: the existing installer continues to materialize files, while the migration runner takes ownership of cleanup, classification, and reviewable destructive changes.
The design borrows from established upgrade systems: