Migration Management
Intro
A Migration is processkit's tracked representation of an upstream version
bump. When a host installer/sync tool notices a new processkit,
host-installer, or community-package version, it writes a Migration
entity into context/migrations/pending/.
The agent and the user work through it together over one or more sessions —
drafting a plan, applying changes, then archiving the result. This skill is
how the agent navigates that lifecycle.
Migration is also the upstream-source-version transition primitive. Use it for
every upstream Schema change, every adopted third-party spec version change
(A2A, ACP, Cilium CRDs, kagent CRDs if adopted), and every auditable data-fix.
The Migration records the exact source version transition through
source_api_version, source_processkit_version, target_api_version, and
target_processkit_version; pk-doctor treats missing version metadata as a
schema vocabulary error for v2 content.
Two subtype patterns are canonical:
Migration{kind=schema-extension}documents an extension to a Schema vocabulary such asArtifact.spec.known_kindsorBinding.spec.known_types.Migration{kind=data-fix}documents a correction to already-emitted data. For LogEntry repair, the original event is superseded by reference and never deleted; this preserves the append-only event-log invariant.
Overview
The migration directory layout
context/migrations/
├── INDEX.md ← always-loaded summary of all migrations
│
├── pending/ ← briefings waiting for human review
│ └── MIG-<id>.md
│
├── in-progress/ ← migrations being worked on
│ └── MIG-<id>.md
│
└── applied/ ← completed migrations (historical record)
└── MIG-<id>.md
The directory the file lives in mirrors the entity's spec.state. Moving
the file is part of the state transition — transition_migration does both
the file move and the spec update atomically.
The lifecycle
┌──────────────┐
│ pending │ ← host sync writes new migrations here
└──────┬───────┘
│ user reviews briefing, approves a plan
▼
┌──────────────┐
│ in-progress │ ← agent works through plan, updates progress_notes
└──────┬───────┘
│ all plan items done, user accepts
▼
┌──────────────┐
│ applied │ ← terminal, kept as history
└──────────────┘
Either pending or in-progress can transition to `rejected`
(terminal side branch — "we looked at this and decided not to take it")
Always-loaded INDEX.md
context/migrations/INDEX.md is a small file the agent loads at session start.
It summarizes every migration's state and the next action. Update it whenever
you transition a migration. Sample shape:
# Migrations Index
## Pending (2)
- **MIG-bright-owl** — processkit v0.3.0 → v0.4.0 (generated 2026-04-15)
4 affected skills, 2 affected processes, 8 new entities, 1 removal
- **MIG-calm-river** — aibox 0.14.4 → 0.15.0 (generated 2026-04-12)
## In progress (1)
- **MIG-quick-hawk** — processkit v0.2.0 → v0.3.0 (started 2026-04-08)
Plan: 6 items, 4 done. Next: migrate workitem-management overrides.
## Applied (4)
| Date | Migration |
|------------|------------------------------------------|
| 2026-04-15 | MIG-... — processkit v0.1.0 → v0.2.0 |
| 2026-03-20 | MIG-... — aibox 0.13.0 → 0.14.0 |
Workflow when starting a session
- Read
context/migrations/INDEX.md— get the migration state at a glance. - If anything is in
pending/and the user hasn't asked about something else, surface it: "There's a pending migration MIG-bright-owl from processkit v0.3.0 to v0.4.0. Do you want to review it now?" - If anything is in
in-progress/, surface it: "We're 4/6 of the way through MIG-quick-hawk. Want to continue?" - Otherwise proceed with the user's request.
Workflow for a pending migration
- Read the briefing — open the file in
pending/. The body has a summary, list of affected files (with classification), new additions, removals. - Draft a plan — propose a project-specific migration plan to the
user. Keep it small: each item is one concrete action ("update the
release process to call the new gate", "rename
fooreferences in our overrides", etc.). Prefer many small items over a few big ones. - Get approval — show the plan, ask for explicit approval. Do NOT start changing files until approved.
- Transition — when approved, set
spec.state: in-progress, setspec.plan: <approved plan>, and move the file frompending/toin-progress/. - Update INDEX.md.
Workflow for an in-progress migration
- Read
spec.planandspec.progress_notes— figure out where you are. - Pick the next plan item, do the work, get user confirmation per item if non-trivial.
- Append a
progress_notetospec.progress_notesafter each completed item — date, actor, what was done. - When all items are done, ask the user to accept. On acceptance:
- Set
spec.state: applied, setspec.applied_atandspec.applied_by - Move the file from
in-progress/toapplied/ - Update INDEX.md
- Optionally write a
LogEntry(event_type: migration.applied)
- Set
Rejecting a migration
If the user decides not to take an upstream change:
- Set
spec.state: rejected,spec.rejected_reason: "..." - Move the file from
pending/(orin-progress/) toapplied/(rejected migrations live inapplied/because that's their terminal home — "applied" here means "decision finalized", not "code applied") - Future host sync runs see this migration in
applied/and do NOT re-propose the same transition.
Gotchas
Agent-specific failure modes — provider-neutral pause-and-self-check items:
- Drafting a Migration without a rollback plan. Every Migration must answer "what happens if this fails halfway through". A Migration without rollback is a one-way door — and one-way doors deserve much more scrutiny than the typical Migration flow allows. State the rollback explicitly even when it's "manual restore from backup".
- Running a Migration without a dry-run pass. Always preview the changes before applying. A dry-run that shows the diff surfaces typos, ordering issues, and unintended scope before they hit the live tree.
- Forgetting to log
migration.applied(andmigration.failed). The audit trail is how the next Migration knows where the last one stopped. Skipping the log strands future maintainers. - Sequencing dependent Migrations wrong. If Migration B
assumes the state Migration A produced, applying B before A is
guaranteed corruption. Use the
depends_onfield and verify the dependency graph before scheduling. - Migration markers without actual diffs. Don't create a Migration entity for "fix the thing" if there's no concrete before/after state. Migrations are records of mechanical transitions, not aspirational notes.
- Treating "no-op migrations" as harmless. A Migration that does nothing should not exist — delete it. Otherwise it pollutes the audit trail with phantom transitions.
- Editing an applied Migration after the fact. Migrations are
immutable history. If the Migration was wrong, write a NEW
Migration that corrects it (
migration.corrected), don't rewrite the old one.
Full reference
Migration entity shape
See src/primitives/schemas/migration.yaml for the authoritative schema.
Frequently-used fields:
| Field | Type | Notes |
|---|---|---|
source |
string | aibox, processkit, processkit-acme, ... |
from_version |
string | semver tag |
to_version |
string | semver tag |
state |
enum | pending / in-progress / applied / rejected |
summary |
string | one-line for INDEX.md |
affected_files |
list[object] | path + classification per file |
affected_groups |
list[string] | logical groups touched |
plan |
string | the approved migration plan (markdown) |
progress_notes |
list[object] | append-only progress log |
Data-fix migrations are required for legacy shapes
Compatibility shims are temporary aids for discovering drift, not a
terminal state. When pk-doctor reports legacy vocabulary, non-canonical
IDs, filename-policy drift, wrong directory layouts, placeholder
timestamps, or stale references, record a kind: data-fix Migration
that names every affected file and the canonical target shape. Apply
the migration through migration-management MCP tools so indexes,
schemas, state-machine transitions, and event logs stay consistent.
Do not resolve strict schema/storage drift by adding project-local
legacy_known_* allowlists or by documenting a permanent policy
exception. If a legacy value should become current vocabulary, ship an
explicit schema migration that adds it to the authoritative
known_* field before new entities use it.
The five file classifications
| Classification | Meaning |
|---|---|
changed-upstream-only |
Upstream changed it, the user has not touched it. Safe to take the new version with one approval. |
changed-locally-only |
The user has edited it, upstream is unchanged. No-op for this migration — but worth flagging that the file diverges. |
conflict |
Both have changed. Must be resolved by hand (or by agent + user). Highest priority. |
new-upstream |
Upstream added a new file. Decide whether to take it. |
removed-upstream |
Upstream removed a file the project still uses. Decide whether to remove locally or keep as a project-local fork. |
Why migrations are tracked entities, not just markdown files
Three reasons:
- Queryable. The
index-managementMCP server indexes them. An agent can ask "what migrations are pending?" without filesystem walking. - State machine validation. Transitions are validated by the migration
state machine — you cannot accidentally move a file from
applied/back topending/. - Cross-referenceable. A
WorkItemcan reference aMigration(spec.related_migrations: [MIG-bright-owl]), so if a migration spawns work, the agent can find the linkage. Same forDecisionRecord.
Interaction with PROVENANCE.toml
When a host sync tool generates a Migration, it diffs the
PROVENANCE.toml files of the old and new upstream versions. This gives
the change set without having to walk every file. The diff script
(scripts/processkit-diff.sh) is the canonical implementation; host
installers can call it or reimplement its logic.
Interaction with the upstream reference templates
The project's context/templates/processkit/<version>/ directory holds
a verbatim copy of every file shipped by the pinned processkit release
(installed and refreshed by the host installer). The migration's
affected_files classification is computed by comparing three SHAs on
the fly:
- template SHA — read from
context/templates/processkit/<version>/<file>(the as-installed reference) - cache SHA — what upstream NOW says (from the host installer's runtime cache, fetched from the pinned processkit tag)
- live SHA — what's in the project right now
Three SHAs → five classifications. See the host installer migration generation logic for the truth table. The pinned source URL + version + resolved commit live in the project lockfile at the project root (Cargo-style), not in a separate manifest file.
Agent-facing MCP tools
This skill ships an MCP server — see
mcp/SERVER.md for the contract. The server exposes
five tools that together cover the full lifecycle: list_migrations
and get_migration for discovery, start_migration for the
pending → in-progress edge, apply_migration for the terminal
in-progress → applied edge (with implicit-start support from a
pending file), and reject_migration for the
{pending, in-progress} → rejected branch. Every write-side tool
stamps the appropriate timestamp (started_at / applied_at /
rejected_at), moves the file between the pending/, in-progress/,
and applied/ directories, refreshes context/migrations/INDEX.md
(preserving the hand-written Notes column and the trailing
## CLI Migrations section), and writes a migration.* event via the
side-effect log helper.
In gateway deployments, these tools are available only when migration-management is present in the installed/merged MCP configuration. The gateway must not infer or expose unrelated processkit servers from this skill's documentation.
list_migrations() without arguments is intentionally an active-work
view: it returns only pending and in-progress migrations. Historical
applied and rejected migrations accumulate for the life of a project
and must be requested explicitly with state="applied" or
state="rejected" to avoid filling status/resume context.
What this skill does NOT do (yet)
- Generate migrations — that's the host installer/sync tool's job.
- Apply file changes — the agent does the actual edits as part of walking the plan. This skill describes the workflow but doesn't ship a tool that auto-edits.