src/skills/x-internal-verify-phase-gates/SKILL.md
Validates phase transitions for orchestrators (Rule 25): pre/post/wave/final modes.
npx skillsauth add edercnj/ia-dev-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-story,x-implement-task,x-release-version,x-review-specialist,x-review-pr. Primeira skill da família de gates de fase.
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-implement-story Phase 3.1) owns those — this gate composes with it via --mode final.| Aspect | Value | Rationale |
| :--- | :--- | :--- |
| Path | internal/plan/x-internal-verify-phase-gates/ | Read-and-compute carve-out, co-located with other story/feature 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, Glob | Read for artifact stat; Bash for jq + state-file I/O via x-internal-update-status; Glob for the inlined policy-discovery (src/policies/*/policy.md) in --mode final. |
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 .aikittools/features/feature-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 .aikittools/features/feature-XXXX/plans/arch-story-0060-0001.md,.aikittools/features/feature-XXXX/plans/plan-story-0060-0001.md --state-file .aikittools/features/feature-XXXX/execution-state.json")
Skill(skill: "x-internal-verify-phase-gates",
args: "--mode wave --skill x-review-specialist --phase Phase-2 --expected-tasks 201,202,203,204,205,206,207,208,209 --state-file .aikittools/features/feature-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/.aikittools/features/feature-XXXX/execution-state.json when feature 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 .aikittools/features/feature-*/execution-state.json. Exactly one match → proceed. Zero or multiple → exit 13 with STATE_FILE_AMBIGUOUS.
--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 ~/src/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.
Policy enforcement (inlined). In final mode the gate is itself the single policy
enforcement point — it runs the full policy check inline, including the conformance
policies (require-task-completeness, require-ac-test-coverage, …) that prove the
execution matched the plan. Execute o Policy Validation Procedure (VP1–VP6) descrito em
references/full-protocol.md
com --phase gate-final --capabilities <story-caps> --story-id <XXXX-YYYY> --feature-id <XXXX> --profile <profile>;
capture o envelope como POLICY_RESULT:
passed: false envelope (exit 15 do procedimento) → the gate passed = false, exit 12; surface
the violations[] in missingArtifacts/findings so the caller can remediate.warnings[] are attached to the envelope but do NOT fail the gate.--profile is forwarded from the caller (default balanced from
policy-set.yaml), so the same gate can run strict for releases and lenient for spikes.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 feature with ~30 gates: < 5 seconds, i.e., well below the 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 .aikittools/features/feature-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 .aikittools/features/feature-XXXX/plans/arch-story-0060-0001.md,.aikittools/features/feature-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":[".aikittools/features/feature-XXXX/plans/arch-story-0060-0001.md",".aikittools/features/feature-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 .aikittools/features/feature-XXXX/plans/arch-story-0060-0001.md,.aikittools/features/feature-XXXX/plans/missing.md")
Output:
{"passed":false,"mode":"post","skill":"x-implement-story","phase":"Phase-1","expectedTasks":[101,102],"completedTasks":[101,102],"missingTasks":[],"expectedArtifacts":[".aikittools/features/feature-XXXX/plans/arch-story-0060-0001.md",".aikittools/features/feature-XXXX/plans/missing.md"],"missingArtifacts":[".aikittools/features/feature-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-specialist --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-specialist --phase Phase-2")
Stderr:
PHASE_GATE_MALFORMED — --mode wave requires --expected-tasks
Exit: 13.
| 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 |
| 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:
src/README.md inventory table./help menu.Still copied to src/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. |
| Policy Validation (Step 3d / --mode final) | inlined | Single policy enforcement point; runs artifact + rule + conformance policies under the active --profile (procedimento VP1–VP6 em references/full-protocol.md). A blocking violation fails the gate. |
| x-implement-story, x-implement-task, x-release-version, x-review-specialist, x-review-pr | callers | Retrofit in stories 0055-0003 → 0055-0010. |
| Story Verification Procedure (inline em x-internal-implement-story) | composition | --mode final of x-implement-story Phase 3 composes with story-verify (verify runs first; gate confirms its outputs). |
| src/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. |
| src/hooks/enforce-phase-sequence.sh (planned — not yet implemented) | consumer (PreToolUse hook) | Reads phaseGateResults[] to block Skill(...) of an orchestrator whose predecessor phase has no passed=true entry. |
Read src/knowledge/shared/architecture-decisions/task-hierarchy/knowledge.md Read src/knowledge/shared/governance-baselines/audit-gate-lifecycle/knowledge.md
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.
development
Documentation freshness gate: validates 6 dimensions (readme, api, adr, etc.) per PR.
testing
Conditional dep-policy gate: CVEs, licenses, versions, freshness; SARIF + report.
documentation
Incrementally updates the service or system architecture document; never regenerative.
development
Scans code and git history for leaked credentials, API keys, and tokens; SARIF output.