src/main/resources/targets/claude/skills/core/internal/plan/x-internal-verify-phase-gates/SKILL.md
Validates phase transitions for orchestrators under Rule 25. Four modes: --mode pre (assert predecessor phases completed before entering phase N), --mode post (assert all child tasks of phase N are completed AND all expected artifacts exist on disk), --mode wave (post-Batch-B verification of parallel wave completeness: N child TaskUpdate completed + N artifacts exist), --mode final (terminal gate composing with x-internal-verify-epic-integrity). Reads execution-state.json.taskTracking.phaseGateResults and TaskList task state; writes back the gate result. Emits a single-line JSON envelope {passed, mode, skill, phase, expectedTasks, completedTasks, missingTasks, expectedArtifacts, missingArtifacts, wallclockMs, timestamp}. Exit 0 on passed, 12 on failure, 13 on malformed args, 14 on task-resolution timeout. First skill in the x-internal-* convention authored by EPIC-0055; eighth overall (after status-update, report-write, args-normalize, story-load-context, story-build-plan, story-verify, story-resume, epic-build-plan, epic-integrity-gate, epic-branch-ensure, story-report) and the eighth under internal/plan/.
npx skillsauth add edercnj/claude-environment x-internal-verify-phase-gatesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
🔒 INTERNAL SKILL Esta skill é invocada apenas por outras skills (orquestradores). NÃO é destinada a invocação direta pelo usuário. Callers principais:
x-implement-epic,x-implement-story,x-implement-task,x-release,x-orchestrate-epic,x-review-codebase,x-review-pr,x-manage-pr-merge-train(após retrofits em stories 0055-0003 a 0055-0010). Oitava skill no subdirinternal/plan/(apósx-internal-load-story-context,x-internal-build-story-plan,x-internal-verify-story,x-internal-resume-story,x-internal-build-epic-plan,x-internal-verify-epic-integrity,x-internal-write-story-report). Primeira skill introduzida por EPIC-0055.
Validates phase transitions in orchestrators governed by Rule 25. A phase gate is a synchronous, fail-fast check that runs around each numbered ## Phase N section: a PRE gate ensures the predecessor phases are cleanly completed; a POST gate ensures every child task of the current phase is completed AND every artifact the phase was supposed to produce exists on disk. Rule 25 moves enforcement from "Stop hook notices afterwards" (Rule 24 defense-in-depth) to sync gate before the phase is marked completed — missing tasks or artifacts abort the lifecycle with exit 12.
Responsibilities (single):
pre / post / wave / final).phaseGateResults in execution-state.json and TaskList for any non-completed sibling task.--expected-tasks against TaskList looking for completed status on each, AND stat-check every path in --expected-artifacts.phaseGateResults[] entry to execution-state.json.taskTracking via x-internal-update-status (delegated).Non-responsibilities (explicit):
TaskUpdate(status: ...)). The caller owns task state transitions.verify-phase-gates.sh, story-0055-0002) reads phaseGateResults[] this skill writes.TaskCreate unless --mode wave --emit-tracker true is set (Rule 25 Invariant 6 exception — one tracker task to surface wave wall-clock).x-internal-verify-story / x-internal-verify-epic-integrity own those — this gate composes with them via --mode final.| Aspect | Value | Rationale |
| :--- | :--- | :--- |
| Path | internal/plan/x-internal-verify-phase-gates/ | Read-and-compute carve-out, co-located with other story/epic gate skills. |
| Frontmatter visibility | internal | Generator filters from /help menu. |
| Frontmatter user-invocable | false | Declarative complement. |
| Frontmatter model | haiku | Zero-reasoning lookup (RULE-023 utility tier). |
| Body marker | > 🔒 **INTERNAL SKILL** | Rule 22 §Body marker. |
| Allowed tools | Read, Bash | Read for artifact stat; Bash for jq + state-file I/O via x-internal-update-status. |
Bare-slash form intentionally omitted — never invoked by a user. All invocations follow Rule 13 Pattern 1 (INLINE-SKILL):
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode pre --skill x-implement-story --phase Phase-1 --state-file ai/epics/epic-XXXX/execution-state.json")
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode post --skill x-implement-story --phase Phase-1 --expected-tasks 101,102,103,104,105,106 --expected-artifacts ai/epics/epic-XXXX/plans/arch-story-0060-0001.md,ai/epics/epic-XXXX/plans/plan-story-0060-0001.md --state-file ai/epics/epic-XXXX/execution-state.json")
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode wave --skill x-review-codebase --phase Phase-2 --expected-tasks 201,202,203,204,205,206,207,208,209 --state-file ai/epics/epic-XXXX/execution-state.json")
| Flag | Required | Default | Description |
| :--- | :--- | :--- | :--- |
| --mode <tier> | M | — | One of pre, post, wave, final. Case-insensitive; normalized to lowercase. |
| --skill <name> | M | — | Calling orchestrator name (recorded in the envelope and state file). |
| --phase <Phase N> | M | — | Exact phase label (matches ## Phase N — Name header). |
| --parent-task-id <id> | O | — | Integer ID of the parent TaskCreate that owns the phase. Used to discover children via TaskList. |
| --expected-tasks <ids> | O | — | Comma-separated integer task IDs the gate must see as completed. Required for post, wave, final. |
| --expected-artifacts <paths> | O | — | Comma-separated relative paths that MUST exist on disk. Required for post and final. Optional for wave. |
| --timeout-s <N> | O | 10 | Integer seconds to retry-poll if any --expected-tasks is still in_progress. Exceeded → exit 14. |
| --emit-tracker <bool> | O | false | Only legal with --mode wave. Emits a single tracker TaskCreate with subject = <skill> › <phase> › wave-tracker for CLI visibility. |
| --state-file <path> | O | auto | Path to execution-state.json. Auto-derived from $CWD/ai/epics/epic-XXXX/execution-state.json when epic is unambiguous. |
On success (exit 0) and on failure (exit 12) the skill writes a single-line JSON object to stdout:
| Field | Type | Always Present | Description |
| :--- | :--- | :--- | :--- |
| passed | Boolean | yes | true when all checks passed. |
| mode | String | yes | Echo of --mode. |
| skill | String | yes | Echo of --skill. |
| phase | String | yes | Echo of --phase. |
| expectedTasks | Array<Integer> | yes | Echo of --expected-tasks ([] if absent). |
| completedTasks | Array<Integer> | yes | Subset of expectedTasks confirmed completed in TaskList. |
| missingTasks | Array<Integer> | yes | expectedTasks \ completedTasks. Empty when passed=true. |
| expectedArtifacts | Array<String> | yes | Echo of --expected-artifacts. |
| missingArtifacts | Array<String> | yes | Subset of expectedArtifacts that do NOT exist on disk. Empty when passed=true. |
| wallclockMs | Integer | yes | Gate execution time in milliseconds (monotonic clock). |
| timestamp | String (ISO-8601) | yes | Gate completion timestamp in UTC. |
The envelope shape is authoritative. phaseGateResults[] in execution-state.json receives a slim projection: {phase, mode, passed, missingArtifacts, missingTasks, timestamp}.
| Code | Name | Condition |
| :--- | :--- | :--- |
| 0 | OK | passed=true |
| 12 | PHASE_GATE_FAILED | passed=false — at least one missing task or missing artifact |
| 13 | PHASE_GATE_MALFORMED | Unknown flag, missing required flag, --emit-tracker true without --mode wave, --expected-tasks on --mode pre, etc. |
| 14 | PHASE_GATE_TIMEOUT | An expected task is still in_progress after --timeout-s seconds of polling |
| 64 | EX_USAGE | --mode value not in {pre, post, wave, final} or --phase fails Rule-25 subject regex |
No partial-success state: the first non-recoverable error aborts the run and yields a non-zero exit.
Parse arguments with the shared Rule-14 while (($#)) loop. Normalize --mode to lowercase. Validate:
--mode ∈ {pre, post, wave, final} — else exit 64.--phase matches Rule-25 subject regex — else exit 64.--mode post|wave|final: --expected-tasks MUST be present.--mode post|final: --expected-artifacts MUST be present.--mode pre: --expected-tasks / --expected-artifacts MUST be absent (checks come from state file only) — else exit 13.--emit-tracker true requires --mode wave — else exit 13.--state-fileWhen --state-file absent, search upward from $PWD for ai/epics/epic-*/execution-state.json. Exactly one match → proceed. Zero or multiple → exit 13 with STATE_FILE_AMBIGUOUS.
taskTracking.enabled=falseRead taskTracking.enabled from the state file. Default is true when the field is absent (Rule 19 fallback). Only when the value is explicitly false, emit:
{"passed":true,"mode":"<mode>","skill":"<skill>","phase":"<phase>","expectedTasks":[],"completedTasks":[],"missingTasks":[],"expectedArtifacts":[],"missingArtifacts":[],"wallclockMs":<N>,"timestamp":"<t>","note":"taskTracking disabled (legacy mode)"}
and exit 0. This is the Rule-19 backward-compat path — legacy epics treat gates as no-ops.
--mode preRead execution-state.json.taskTracking.phaseGateResults[]. Let N = int(phase.replace("Phase-","")). For every k ∈ [0, N-1], at least one entry with phase="Phase-k" AND mode="post" AND passed=true MUST be present. Missing entries → missingTasks=[<Phase-k-placeholder>], passed=false, exit 12.
No artifact checks in PRE mode.
--mode postid ∈ --expected-tasks, retrieve its status. Use TaskList via Bash (e.g., reading from ~/.claude/state/tasks/<session>/tasks.json when available, falling back to execution-state.json.taskTracking.taskIndex[]). completed → add to completedTasks; else add to missingTasks. If any task is in_progress, poll every 500ms up to --timeout-s; persistent → exit 14.path ∈ --expected-artifacts, [[ -e "$path" ]] — present → drop; absent → append to missingArtifacts.passed = missingTasks.isEmpty() && missingArtifacts.isEmpty().--mode waveSame as POST, but:
completed simultaneously (no ordering assumption).--expected-artifacts is optional — waves that only produce task commits (no files) are valid.--emit-tracker true, emit exactly one TaskCreate(subject: "<skill> › <phase> › wave-tracker") upfront and TaskUpdate(status: "completed") at the end, regardless of result.--mode finalSuperset of POST. Additionally scans the Rule-24 mandatory artifact set (when --skill = x-implement-story): verify-envelope, review-story, techlead-review, story-completion-report. Acts as the synchronous Rule-24 gate the Stop-hook normally enforces asynchronously.
phaseGateResults[] entryAtomically append to execution-state.json.taskTracking.phaseGateResults[] via:
Skill(skill: "x-internal-update-status",
args: "--file <state-file> --type phase-gate --phase <phase> --mode <mode> --passed <true|false> --missing-artifacts <comma-list> --missing-tasks <comma-list>")
The status-update skill owns the flock-protected read-modify-write.
Single-line JSON via jq -nc with all fields. Exit 0 on success, 12 on failure.
The skill is idempotent by design:
--mode, --skill, --phase) is a replay: it re-reads task state + artifact state, writes a fresh phaseGateResults[] entry, and returns the current result. Previous entries remain for audit history.x-internal-update-status's flock (file-level).| Mode | Budget |
| :--- | :--- |
| pre | < 50ms (single file read + jq projection) |
| post | < 100ms + artifact stat × N (usually < 150ms total) |
| wave | same as post |
| final | < 200ms (adds Rule-24 scan) |
Target global overhead across an epic with ~30 gates: < 5 seconds, i.e., well below the EPIC-0055 DoD threshold of "< 2% wall-clock overhead".
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode pre --skill x-implement-story --phase Phase-2 --state-file ai/epics/epic-XXXX/execution-state.json")
Output:
{"passed":true,"mode":"pre","skill":"x-implement-story","phase":"Phase-2","expectedTasks":[],"completedTasks":[],"missingTasks":[],"expectedArtifacts":[],"missingArtifacts":[],"wallclockMs":12,"timestamp":"2026-04-24T10:30:00Z"}
Exit: 0.
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode post --skill x-implement-story --phase Phase-1 --expected-tasks 101,102,103,104,105,106 --expected-artifacts ai/epics/epic-XXXX/plans/arch-story-0060-0001.md,ai/epics/epic-XXXX/plans/plan-story-0060-0001.md")
Output:
{"passed":true,"mode":"post","skill":"x-implement-story","phase":"Phase-1","expectedTasks":[101,102,103,104,105,106],"completedTasks":[101,102,103,104,105,106],"missingTasks":[],"expectedArtifacts":["ai/epics/epic-XXXX/plans/arch-story-0060-0001.md","ai/epics/epic-XXXX/plans/plan-story-0060-0001.md"],"missingArtifacts":[],"wallclockMs":47,"timestamp":"2026-04-24T10:35:00Z"}
Exit: 0.
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode post --skill x-implement-story --phase Phase-1 --expected-tasks 101,102 --expected-artifacts ai/epics/epic-XXXX/plans/arch-story-0060-0001.md,ai/epics/epic-XXXX/plans/missing.md")
Output:
{"passed":false,"mode":"post","skill":"x-implement-story","phase":"Phase-1","expectedTasks":[101,102],"completedTasks":[101,102],"missingTasks":[],"expectedArtifacts":["ai/epics/epic-XXXX/plans/arch-story-0060-0001.md","ai/epics/epic-XXXX/plans/missing.md"],"missingArtifacts":["ai/epics/epic-XXXX/plans/missing.md"],"wallclockMs":38,"timestamp":"2026-04-24T10:40:00Z"}
Exit: 12.
With --timeout-s 2 and task 203 in in_progress:
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode wave --skill x-review-codebase --phase Phase-2 --expected-tasks 201,202,203,204,205,206,207,208,209 --timeout-s 2")
Stderr:
PHASE_GATE_TIMEOUT — task 203 still in_progress after 2s
Exit: 14.
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode wave --skill x-review-codebase --phase Phase-2")
Stderr:
PHASE_GATE_MALFORMED — --mode wave requires --expected-tasks
Exit: 13.
When execution-state.json.taskTracking.enabled = false:
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode post --skill x-implement-story --phase Phase-1 --expected-tasks 101 --expected-artifacts foo.md")
Output:
{"passed":true,"mode":"post","skill":"x-implement-story","phase":"Phase-1","expectedTasks":[],"completedTasks":[],"missingTasks":[],"expectedArtifacts":[],"missingArtifacts":[],"wallclockMs":3,"timestamp":"2026-04-24T10:45:00Z","note":"taskTracking disabled (legacy mode)"}
Exit: 0.
| Scenario | Action |
| :--- | :--- |
| --mode value invalid | PHASE_GATE_MALFORMED; exit 13 |
| --phase fails regex | Exit 64 |
| Required flag missing per mode | PHASE_GATE_MALFORMED; exit 13 |
| State file absent AND no --state-file override | STATE_FILE_AMBIGUOUS; exit 13 |
| taskTracking.enabled=false | Short-circuit; passed=true; exit 0 |
| Expected task in_progress beyond timeout | PHASE_GATE_TIMEOUT; exit 14 |
| At least one expected task missing / non-completed | PHASE_GATE_FAILED; exit 12 |
| At least one expected artifact absent | PHASE_GATE_FAILED; exit 12 |
| x-internal-update-status delegation fails | Propagate stderr; exit 13 with STATE_UPDATE_FAILED |
The ia-dev-env generator excludes skills with visibility: internal from:
.claude/README.md inventory table./help menu.Still copied to .claude/skills/ (flat layout) so other skills can invoke via Skill(...).
Internal skills DO NOT emit phase.start / phase.end markers — the calling orchestrator owns telemetry wrapping. Passive hooks capture tool.call events for the underlying Bash/Read invocations.
| Skill | Relationship | Context |
| :--- | :--- | :--- |
| x-internal-update-status | delegate | Atomic flock-protected write of phaseGateResults[] entry. |
| x-implement-epic, x-implement-story, x-implement-task, x-release, x-orchestrate-epic, x-review-codebase, x-review-pr, x-manage-pr-merge-train | callers | Retrofit in stories 0055-0003 → 0055-0010. |
| x-internal-verify-epic-integrity | composition | --mode final of x-implement-epic Phase 4 composes with integrity gate (gate runs first; integrity-gate is subsequent). |
| x-internal-verify-story | composition | --mode final of x-implement-story Phase 3 composes with story-verify (verify runs first; gate confirms its outputs). |
| .claude/hooks/verify-phase-gates.sh | consumer (Stop hook) | Reads phaseGateResults[] this skill writes; emits WARNING + exit 2 on gate failure at end of LLM turn. |
| .claude/hooks/enforce-phase-sequence.sh | consumer (PreToolUse hook) | Reads phaseGateResults[] to block Skill(...) of an orchestrator whose predecessor phase has no passed=true entry. |
x-internal-*, visibility: internal).--mode final).Per-mode state-file schema, full flock + x-internal-update-status interaction pseudocode, polling algorithm for in_progress tasks, and the phaseGateResults[] append-only invariant live in references/full-protocol.md.
tools
Documentation automation v2: stack-aware generation from documentation.targets.
development
Generates or updates CI/CD pipelines per project stack with actionlint validation.
tools
Generates ADRs from architecture-plan mini-ADRs with sequential numbering and index update.
development
Formats source code; first step of the pre-commit chain (format -> lint -> compile).