Deploy Gate v0.1
A thin checklist-runner that verifies deployment readiness before shipping. This skill checks preconditions — it does NOT execute deployments, manage rollbacks, or monitor rollouts.
When This Activates
- Part of the SHIP phase composition chain
- Runs after
verification-before-completion confirms code correctness
- Runs before
openspec-ship generates documentation
- Can also be triggered explicitly: "check deployment readiness", "run deploy gate"
Checklist
Run each check. Report pass/fail with evidence. Do not block on warnings — report them and let the human decide.
Required Checks (must pass)
CI Status (fail-closed: absent ≠ green)
_concl="$(gh pr checks --fail-fast >/dev/null 2>&1 && echo PASS \
|| gh run list --limit 1 --json conclusion -q '.[0].conclusion')"
if [ -z "$_concl" ]; then
echo "GATE FAIL: no CI checks reported — absent ≠ green. Treating as FAIL."
else
echo "CI conclusion: $_concl"
fi
Gate: distinguish three states — green ($_concl = PASS/success), red (any failure conclusion), absent-or-broken (empty $_concl, or a run that concluded with zero completed steps). Absent-or-broken is a FAIL, never a pass. Do not read an empty statusCheckRollup as "nothing blocking → ship". If gh pr checks itself reports failing checks, that is red regardless of what gh run list returns — do not let a stale prior run's success conclusion mask currently-red PR checks.
Local verification of record: when hosted CI is absent, you MAY accept a fresh ~/.claude/.skill-project-verified-<token> evidence file as verification performed on substrate local ONLY when its failed list is empty, its could_not_verify list is empty, gate_gaming_status is exactly clean, AND coverage_adequacy_status is not suspect. Any other value for gate_gaming_status — suspect (a possibly-gamed gate), unverified (the gate-gaming check could not run), or the field absent — MUST NOT be accepted: require the positive clean assertion, because absence is not proof (just as a non-empty could_not_verify, a gate that could not run, is not).
coverage_adequacy_status has a deliberately different acceptance rule than gate_gaming_status, in parity with project-verification's own tri-state contract for the field. A suspect value (new code shipping below the changed-line coverage floor) BLOCKS acceptance the same way gate_gaming_status: suspect does — both are positive evidence of a problem, not mere absence of proof. But unverified or an absent coverage_adequacy_status does NOT block acceptance, unlike an absent gate_gaming_status. This asymmetry is intentional: project-verification's adequacy check is itself fail-open by contract — unverified (no coverage tooling present, or an unparseable artifact) MUST NOT block there — so treating an absent or unverified field as a rejection here would silently re-impose a block the upstream check explicitly disclaims, penalizing repos that simply lack coverage tooling rather than repos with a demonstrated coverage gap.
Since absence-of-failure is not proof-of-pass, when rejecting evidence surface the specific reason (which gates could not be verified, that gate-gaming was detected/unverified in the diff, or that coverage_adequacy_status was suspect) rather than only reporting that hosted CI was absent. Still surface that hosted CI was absent rather than claiming hosted-CI green. This evidence is advisory provenance, not a non-bypassable gate.
No WIP Commits
git log --oneline origin/main..HEAD | grep -iE '(wip|fixup|squash|todo|hack|tmp)'
Gate: No WIP-pattern commits on the branch.
Version/Changelog Updated (if applicable)
Check if CHANGELOG.md, package.json, or version file was modified in this branch.
Gate: Soft — warn if no version change detected.
Design Intent Exists (if DESIGN phase was executed)
Check session state for design_path. If set, verify the file exists at docs/plans/.
Gate: Soft — warn if design_path is set but file is missing.
Advisory Checks (warn only)
Branch Protection
gh api repos/{owner}/{repo}/branches/main/protection 2>/dev/null | jq '.required_status_checks'
Report whether branch protection is configured.
Dependent Services Notified
Prompt: "Does this change affect any downstream services or consumers? If yes, have they been notified?"
This is a human checkpoint, not an automated check.
Feature Flags
Prompt: "Is this change behind a feature flag? If yes, confirm the flag default is OFF."
Project-Local Override
If .deploy-checklist.yml exists in the repo root, read it and use its checklist instead of the defaults above. Format:
required:
- name: CI green
command: gh pr checks --fail-fast
gate: exit_code_zero
- name: No WIP commits
command: git log --oneline origin/main..HEAD | grep -iE '(wip|fixup|squash)'
gate: exit_code_nonzero # grep returns 1 when no match = good
advisory:
- name: Migration reviewed
prompt: "Have database migrations been reviewed by the DBA?"
Output
Report as a structured checklist:
## Deploy Gate Results
| # | Check | Status | Evidence |
|---|-------|--------|----------|
| 1 | CI Status | PASS | Last run: success (run 12345) |
| 2 | No WIP commits | PASS | 0 WIP-pattern commits found |
| 3 | Version updated | WARN | No version change detected |
| 4 | Design intent | PASS | docs/plans/2026-04-15-feature-design.md exists |
| 5 | Branch protection | INFO | Required status checks: [ci/build] |
| 6 | Dependent services | HUMAN | User confirmed: no downstream impact |
| 7 | Feature flags | SKIP | No feature flags in this change |
**Result: PASS** (2 warnings, 0 failures)
Staged Rollout & Rollback Plan (advisory)
This gate checks readiness; it does not execute the rollout. But a "go" is only
meaningful against a pre-agreed rollout and rollback plan. Before promoting,
confirm the human has these defined (surface them; do not enforce):
Progressive canary stages. Ship to a small slice first, bake, then widen.
A typical ladder — adjust to traffic and risk:
| Stage |
Traffic |
Bake (target soak) |
Advance when |
| Canary |
~5% |
long enough to cover peak + one full request mix |
no trigger tripped |
| Early |
~25% |
shorter than canary — request mix already characterized |
no trigger tripped |
| Half |
~50% |
shorter still |
no trigger tripped |
| Full |
100% |
monitor |
stable |
Rollback triggers (define BEFORE rollout, measured against the pre-deploy
baseline — not against zero):
- Error rate — e.g. roll back if 5xx / error-event rate exceeds baseline by a
pre-set margin (a relative bump, e.g. +0.5pp absolute or +50% relative, whichever
the team picks) sustained over the bake window.
- Latency — e.g. roll back if p95 (and/or p99) exceeds the baseline by a pre-set
margin sustained over the bake window. Use the same percentile the SLO is written in.
- Saturation — CPU/memory/connection-pool/queue-depth nearing limits at the
current canary share (it will scale ~linearly as traffic widens).
Rollback is the default, not the exception. If a trigger trips, revert first and
diagnose after — do not "wait and see" while widening. Prefer a fast revert path
(redeploy previous image / flip the flag) over a forward-fix under load.
If any required check FAILS, report the failure clearly and stop. Do not proceed to openspec-ship.
Verification
Before reporting GO, confirm -- with evidence, not assumption:
- Every Required Check ran and its result was observed this session (CI status fetched, not presumed green).
- No required check sits in an unknown or skipped state being treated as a pass.
- Version and design-artifact checks reference the actual files, not memory.
- On any required-check FAIL, the verdict is STOP -- openspec-ship is not invoked.
1---2name: deploy-gate3description: Use when preparing to ship or release — before pushing to production, promoting a build, or finalizing a branch — to confirm CI is green, no WIP commits remain, and version and design artifacts are in order4---56# Deploy Gate v0.178A thin checklist-runner that verifies deployment readiness before shipping. This skill checks preconditions — it does NOT execute deployments, manage rollbacks, or monitor rollouts.910## When This Activates1112- Part of the SHIP phase composition chain13- Runs after `verification-before-completion` confirms code correctness14- Runs before `openspec-ship` generates documentation15- Can also be triggered explicitly: "check deployment readiness", "run deploy gate"1617## Checklist1819Run each check. Report pass/fail with evidence. Do not block on warnings — report them and let the human decide.2021### Required Checks (must pass)22231. **CI Status** (fail-closed: absent ≠ green)24 ```bash25 _concl="$(gh pr checks --fail-fast >/dev/null 2>&1 && echo PASS \26 || gh run list --limit 1 --json conclusion -q '.[0].conclusion')"27 if [ -z "$_concl" ]; then28 echo "GATE FAIL: no CI checks reported — absent ≠ green. Treating as FAIL."29 else30 echo "CI conclusion: $_concl"31 fi32 ```33 Gate: distinguish three states — **green** (`$_concl` = PASS/success), **red** (any failure conclusion), **absent-or-broken** (empty `$_concl`, or a run that concluded with zero completed steps). Absent-or-broken is a **FAIL**, never a pass. Do not read an empty `statusCheckRollup` as "nothing blocking → ship". If `gh pr checks` itself reports failing checks, that is **red** regardless of what `gh run list` returns — do not let a stale prior run's `success` conclusion mask currently-red PR checks.3435 **Local verification of record:** when hosted CI is absent, you MAY accept a fresh `~/.claude/.skill-project-verified-<token>` evidence file as verification performed on substrate `local` ONLY when its `failed` list is empty, its `could_not_verify` list is empty, `gate_gaming_status` is **exactly `clean`**, AND `coverage_adequacy_status` is **not `suspect`**. Any other value for `gate_gaming_status` — `suspect` (a possibly-gamed gate), `unverified` (the gate-gaming check could not run), or the field **absent** — MUST NOT be accepted: require the positive `clean` assertion, because absence is not proof (just as a non-empty `could_not_verify`, a gate that could not run, is not).3637 `coverage_adequacy_status` has a deliberately different acceptance rule than `gate_gaming_status`, in parity with `project-verification`'s own tri-state contract for the field. A `suspect` value (new code shipping below the changed-line coverage floor) BLOCKS acceptance the same way `gate_gaming_status: suspect` does — both are positive evidence of a problem, not mere absence of proof. But `unverified` or an absent `coverage_adequacy_status` does NOT block acceptance, unlike an absent `gate_gaming_status`. This asymmetry is intentional: `project-verification`'s adequacy check is itself fail-open by contract — `unverified` (no coverage tooling present, or an unparseable artifact) MUST NOT block there — so treating an absent or `unverified` field as a rejection here would silently re-impose a block the upstream check explicitly disclaims, penalizing repos that simply lack coverage tooling rather than repos with a demonstrated coverage gap.3839 Since absence-of-failure is not proof-of-pass, when rejecting evidence surface the specific reason (which gates could not be verified, that gate-gaming was detected/unverified in the diff, or that `coverage_adequacy_status` was `suspect`) rather than only reporting that hosted CI was absent. Still surface that hosted CI was absent rather than claiming hosted-CI green. This evidence is advisory provenance, not a non-bypassable gate.40412. **No WIP Commits**42 ```bash43 git log --oneline origin/main..HEAD | grep -iE '(wip|fixup|squash|todo|hack|tmp)'44 ```45 Gate: No WIP-pattern commits on the branch.46473. **Version/Changelog Updated** (if applicable)48 Check if `CHANGELOG.md`, `package.json`, or version file was modified in this branch.49 Gate: Soft — warn if no version change detected.50514. **Design Intent Exists** (if DESIGN phase was executed)52 Check session state for `design_path`. If set, verify the file exists at `docs/plans/`.53 Gate: Soft — warn if design_path is set but file is missing.5455### Advisory Checks (warn only)56575. **Branch Protection**58 ```bash59 gh api repos/{owner}/{repo}/branches/main/protection 2>/dev/null | jq '.required_status_checks'60 ```61 Report whether branch protection is configured.62636. **Dependent Services Notified**64 Prompt: "Does this change affect any downstream services or consumers? If yes, have they been notified?"65 This is a human checkpoint, not an automated check.66677. **Feature Flags**68 Prompt: "Is this change behind a feature flag? If yes, confirm the flag default is OFF."6970## Project-Local Override7172If `.deploy-checklist.yml` exists in the repo root, read it and use its checklist instead of the defaults above. Format:7374```yaml75required:76 - name: CI green77 command: gh pr checks --fail-fast78 gate: exit_code_zero79 - name: No WIP commits80 command: git log --oneline origin/main..HEAD | grep -iE '(wip|fixup|squash)'81 gate: exit_code_nonzero # grep returns 1 when no match = good82advisory:83 - name: Migration reviewed84 prompt: "Have database migrations been reviewed by the DBA?"85```8687## Output8889Report as a structured checklist:9091```markdown92## Deploy Gate Results9394| # | Check | Status | Evidence |95|---|-------|--------|----------|96| 1 | CI Status | PASS | Last run: success (run 12345) |97| 2 | No WIP commits | PASS | 0 WIP-pattern commits found |98| 3 | Version updated | WARN | No version change detected |99| 4 | Design intent | PASS | docs/plans/2026-04-15-feature-design.md exists |100| 5 | Branch protection | INFO | Required status checks: [ci/build] |101| 6 | Dependent services | HUMAN | User confirmed: no downstream impact |102| 7 | Feature flags | SKIP | No feature flags in this change |103104**Result: PASS** (2 warnings, 0 failures)105```106107## Staged Rollout & Rollback Plan (advisory)108109This gate checks readiness; it does not execute the rollout. But a "go" is only110meaningful against a **pre-agreed** rollout and rollback plan. Before promoting,111confirm the human has these defined (surface them; do not enforce):112113**Progressive canary stages.** Ship to a small slice first, bake, then widen.114A typical ladder — adjust to traffic and risk:115116| Stage | Traffic | Bake (target soak) | Advance when |117|-------|---------|-----------------|--------------|118| Canary | ~5% | long enough to cover peak + one full request mix | no trigger tripped |119| Early | ~25% | shorter than canary — request mix already characterized | no trigger tripped |120| Half | ~50% | shorter still | no trigger tripped |121| Full | 100% | monitor | stable |122123**Rollback triggers (define BEFORE rollout, measured against the pre-deploy124baseline — not against zero):**125- **Error rate** — e.g. roll back if 5xx / error-event rate exceeds baseline by a126 pre-set margin (a relative bump, e.g. +0.5pp absolute or +50% relative, whichever127 the team picks) sustained over the bake window.128- **Latency** — e.g. roll back if p95 (and/or p99) exceeds the baseline by a pre-set129 margin sustained over the bake window. Use the same percentile the SLO is written in.130- **Saturation** — CPU/memory/connection-pool/queue-depth nearing limits at the131 current canary share (it will scale ~linearly as traffic widens).132133**Rollback is the default, not the exception.** If a trigger trips, revert first and134diagnose after — do not "wait and see" while widening. Prefer a fast revert path135(redeploy previous image / flip the flag) over a forward-fix under load.136137If any required check FAILS, report the failure clearly and stop. Do not proceed to openspec-ship.138139## Verification140141Before reporting GO, confirm -- with evidence, not assumption:142143- Every Required Check ran and its result was observed this session (CI status fetched, not presumed green).144- No required check sits in an unknown or skipped state being treated as a pass.145- Version and design-artifact checks reference the actual files, not memory.146- On any required-check FAIL, the verdict is STOP -- openspec-ship is not invoked.