{ "$schema": "http://json-schema.org/draft-07/schema#", "$id": "urn:awslabs:startups:migration-to-aws:state:phase-status", "$comment": "The $id URN uses 'migration-to-aws' as a stable schema namespace identity โ€” it is NOT a plugin name or skill invocation prefix. Do not rename it when the schema moves between plugins.", "title": ".phase-status.json", "description": "Canonical schema for the shared migration state file (.phase-status.json) that DSL-driven migration skills read and advance. The valid phase NAMES are NOT enumerated here โ€” they are whatever phases the skill declares (its phase files). This schema is skill-agnostic: adding a phase to a skill requires no change to this file.", "type": "object", "required": ["migration_id", "last_updated", "phases"], "additionalProperties": false, "properties": { "migration_id": { "type": "string", "description": "Matches the $MIGRATION_DIR folder name (e.g. 0226-1430). Set at creation; never changes." }, "last_updated": { "type": "string", "format": "date-time", "description": "ISO 8601 timestamp; updated after each phase-status change." }, "current_phase": { "type": "string", "description": "The phase to run next: a declared phase name, or the 'complete' terminal. Optional but recommended; when present it is authoritative for phase selection." }, "run_mode": { "type": "string", "enum": ["decide", "decide_and_execute"], "description": "Optional. Generate-consent flow state, not a design constraint (never appears in preferences.json). 'decide' = user stopped at the Decision gate; Generate is available on request but never auto-loaded. 'decide_and_execute' = user opted into execution artifacts; Generate may load. Absent = the gate has not been reached yet. Set only by a skill's post-Estimate Decision gate (see e.g. heroku-to-aws SKILL.md) or an equivalent resume-offer acceptance." }, "run_id": { "type": "string", "pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", "description": "Random UUID minted once at _init and never changed. Stable across pause/resume and, unlike migration_id, carries no timestamp. The run's identifier for telemetry events and for the plugin-to-web handoff." }, "owning_skill": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$", "description": "Telemetry identifier of the skill that owns this run (the skill directory name in upper case with hyphens as underscores, e.g. GCP_TO_AWS), written at _init. Telemetry attributes events to this key; a run without it emits nothing." }, "initiated_by": { "type": "string", "pattern": "^[A-Z][A-Z0-9_]*$", "description": "Identifier of the skill that invoked this run, when another skill did (e.g. LLM_TO_BEDROCK delegating to GCP_TO_AWS). Optional; sent as initiatingSkill when it names a migration skill." }, "phases": { "type": "object", "description": "One entry per phase the skill declares (backbone and sidebar). Keys are the skill's phase names โ€” this schema does not enumerate them.", "minProperties": 1, "additionalProperties": { "type": "string", "enum": ["pending", "in_progress", "completed"], "description": "Phase status. Progresses pending -> in_progress -> completed and never goes backward, except a confirmed re-entry reset (see INTERPRETER.md ยง _re_entry_guard). At most one backbone phase is in_progress at a time." } } } }