원클릭으로
verify-pr
Comprehensive PR readiness check before merge. Run quality checks, tests, CI, documentation, AWS resource cleanup, and code review.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Comprehensive PR readiness check before merge. Run quality checks, tests, CI, documentation, AWS resource cleanup, and code review.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
SOC 직업 분류 기준
Detect and delete leftover AWS resources from cdkd integration tests. Only targets resources matching known cdkd stack name patterns.
Proactively hunt for cdkd bugs by deploying real CDK apps that exercise common-but-untested AWS resources, configs, and CloudFormation notations against real AWS, then fix what breaks. Use for a periodic "find latent bugs" sweep, not for verifying a specific change.
Work through already-filed GitHub issues (typically the bug-hunt's output) end to end — triage safely, pick a few FILE-DISJOINT issues to fix in parallel, claim each on the issue before starting (collision-safe with other agents), verify against real AWS, then carry each through merge → pull → release → rebuild the linked binary → worktree cleanup. Use when asked to "handle/address filed issues", not to hunt for new bugs (that is /hunt-bugs).
Recommend which integration tests to run, based on the integ ledger (staleness / last result) plus the code areas touched by recent commits. Outputs a prioritized list of `/run-integ <name>` commands. Use before a release, after a batch of merges, or when unsure what integ coverage a change needs.
Scaffold a new integration test for cdkd. Creates a minimal CDK app with the specified AWS resources for deploy/destroy E2E testing.
Run integration tests (deploy + destroy) against real AWS. Use when you need to verify cdkd works end-to-end with actual AWS resources.
| name | verify-pr |
| description | Comprehensive PR readiness check before merge. Run quality checks, tests, CI, documentation, AWS resource cleanup, and code review. |
| argument-hint | [PR-number] |
Heavy pre-merge gate. Run this before creating or merging a pull request — NOT before every commit. Per-commit verification is handled by /check (enforced by a PreToolUse hook that blocks git commit without a fresh marker).
Run each check and report pass/fail:
Worktree pre-flight: confirm node_modules/ exists in the cwd:
[ -d node_modules ] || pnpm install
git worktree add does NOT copy node_modules, so a fresh worktree's
vp run typecheck / lint / build and vp run test all fail
with tsgo: command not found / Cannot find package 'vitest' etc. —
but the failure is easy to miss when the output is piped to tail (the
exit code reflects tail, not vp, and the failure line gets
buried). If the pre-flight skips by way of an existing node_modules,
confirm it is not stale by spot-checking pnpm-lock.yaml mtime ≤
node_modules/.modules.yaml mtime. Do not start step 1 until this
passes, or every quality check below silently no-ops while looking
green.
Code quality
vp run typecheck passesvp run lint passes (run lint:fix first if needed)vp run build succeedstail / head / grep for log
truncation, check the actual output content for Error /
Command failed markers — $? after a pipeline reflects
the LAST stage (usually 0), NOT the build tool's exit. The same
applies to background-task completion notifications: the
framework's exit code 0 is the chained command's exit, not the
pipeline head. When in doubt, capture the result without piping:
vp run X > /tmp/out 2>&1; rc=$?; tail -3 /tmp/out; echo "[rc=$rc]".Tests
vp run test - all unit tests passgit diff main...HEAD for src/ changes vs tests/ changes. If new logic was added or modified in src/ but no corresponding test files were added or updated, flag as fail and add the missing tests before proceedingCI status
gh pr view --json number -q .numberAskUserQuestion tool to ask for the PR numbergh pr view <PR> --json mergeStateStatus,mergeable -q '"mergeable=\(.mergeable) state=\(.mergeStateStatus)"' — when this returns mergeable=CONFLICTING state=DIRTY, the CI workflow will NEVER fire on the PR no matter how long you wait (empirically observed PR #404 — wasted ~70 min thinking GitHub Actions was broken). Close+reopen, empty commits, and git push --force-with-lease of unchanged content all fail to re-trigger. Resolution: git fetch origin main && git rebase origin/main, resolve conflicts, git push --force-with-lease — CI fires within ~30s of the push. See memory feedback_pr_conflict_blocks_ci.md for the full diagnostic checklist.mergeStateStatus is CLEAN / UNSTABLE / BLOCKED / BEHIND: gh pr checks <PR-number> - all checks passWorking tree
git status - clean (no uncommitted changes)Documentation consistency
/check-docs skill logic: verify docs match code changesinteg-coverage, scenario-coverage, audit:coverage:check) and hard-fails on staleness. PR #548 hit two of these in succession because /verify-pr only regenerated integ-coverage locally — the contributor pushed, CI failed on audit:coverage:check, the contributor regenerated provider-coverage and pushed again, CI failed on scenario-coverage matrix is up-to-date. The block below covers all three so the same round-trip can't happen again:
fixtures_changed=$(git diff main...HEAD --name-only | grep -qE '^src/provisioning/register-providers\.ts$|^tests/integration/[^/]+/(lib|bin)/.+\.ts$|^tests/integration/[^/]+/\.scenarios\.json$' && echo yes)
providers_changed=$(git diff main...HEAD --name-only | grep -qE '^src/provisioning/register-providers\.ts$' && echo yes)
if [ "$fixtures_changed" = "yes" ]; then
vp run integ-coverage
vp run scenario-coverage
fi
if [ "$providers_changed" = "yes" ]; then
# `--check` is offline (~0.5s) and verifies the cached
# docs/_generated/provider-coverage.json matches register-providers.ts
# under the current Tier classification. If it fails, the contributor
# must run `vp run audit:coverage:regenerate` (heavy: ~15 min, requires
# AWS creds with cloudformation:ListTypes + DescribeType) and commit
# the regenerated cache. /verify-pr does NOT auto-run :regenerate
# because the cost is too high and AWS creds may not be present in
# the local dev environment.
vp run audit:coverage:check
fi
git status --short docs/integ-coverage.md docs/_generated/integ-coverage.json \
docs/scenario-coverage.md docs/_generated/scenario-coverage.json \
docs/_generated/provider-coverage.json docs/_generated/provider-coverage.md
If git status reports any of these files as dirty, the contributor forgot to regenerate after their code change. Stage the regenerated output, amend / new-commit it onto the PR, and re-run /check-docs to refresh the docs marker. If vp run audit:coverage:check exits non-zero, run vp run audit:coverage:regenerate (heavy — see above) and commit the regenerated docs/_generated/provider-coverage.{json,md}.
The provider-integ-gate.sh PreToolUse hook blocks git commit when a new registry.register('AWS::Foo::Bar', ...) is added without integ coverage (literal type id, Cfn<Type>( L1 class, or // allow-no-integ: <rationale> carve-out) — but it does not enforce that the matrix snapshots themselves are regenerated. This step closes that gap.Leftover resources
Resolve account ID via aws sts get-caller-identity --query Account --output text
aws s3 ls s3://cdkd-state-{accountId}-us-east-1/stacks/ --region us-east-1 — no leftover state
For deletion-touching PRs (any change under src/provisioning/providers/**, src/cli/commands/destroy.ts, src/analyzer/dag-builder.ts, IMPLICIT_DELETE_DEPENDENCIES, etc.): the integ-destroy markgate gate physically blocks gh pr merge when its marker is stale (see .claude/hooks/integ-destroy-gate.sh). This step verifies the gate state explicitly so failures surface here rather than at merge time:
mise exec -- markgate verify integ-destroy
If this exits non-zero, run /run-integ <relevant-test> (e.g. bench-cdk-sample) and confirm it reports 0 errors / 0 orphans — the skill itself will then call markgate set integ-destroy.
CI is necessary but not sufficient — it does not exercise real-AWS destroy. The gate is the structural enforcement of that fact.
CROSS-CUTTING CHECK (load-bearing): the integ-destroy marker accepts ANY clean real-AWS destroy. A narrow feature-specific integ (e.g. import-value-strong-ref's 2-stack S3+SSM fixture) IS sufficient to flip the marker, but it does NOT exercise the broad deploy / destroy code paths a cross-cutting change touches. When the PR diff touches ANY of:
src/deployment/deploy-engine.tssrc/deployment/intrinsic-function-resolver.tssrc/cli/commands/destroy-runner.tssrc/cli/commands/destroy.tssrc/cli/commands/deploy.tssrc/analyzer/dag-builder.tssrc/analyzer/template-parser.tssrc/provisioning/register-providers.ts...you MUST run a broad integ in addition to whatever feature-specific integ the change came with. The canonical broad set (keep in sync with .claude/hooks/integ-broad-gate.sh, .claude/skills/run-integ/SKILL.md step 11, .markgate.yml integ-broad gate, CLAUDE.md "integ-broad" entry):
bench-cdk-sample (39-resource VPC+NAT+CF+Lambda+SQS)lambdamicroservicesdrift-revertdrift-revert-vpcmulti-stack-depsmulti-resourceremove-protectionexportThese exercise multi-resource VPC / Lambda / IAM / CFn-Custom paths that narrow integs leave uncovered. Cross-cutting code paths affect EVERY user's deploy/destroy, not just the feature you added — broad integs are the only structural defense against shipping a regression that only surfaces in production on stacks unlike your fixture. Bypassing this is the PR #348 trap from 2026-05-13 (Issue #343 shipped without bench-cdk-sample validation; surfaced post-merge as an incident).
# Detection: only fires when the diff actually touches cross-cutting code.
if git diff main...HEAD --name-only | grep -qE '^src/deployment/(deploy-engine|intrinsic-function-resolver)\.ts$|^src/cli/commands/(destroy-runner|destroy|deploy)\.ts$|^src/analyzer/(dag-builder|template-parser)\.ts$|^src/provisioning/register-providers\.ts$'; then
echo "Cross-cutting code touched — broad integ required (bench-cdk-sample / lambda / microservices / drift-revert)."
# Then run the broad integ via /run-integ and confirm 0 errors / 0 orphans.
fi
The narrow feature integ stays valuable for testing the FEATURE; the broad integ is the regression backstop. Both must pass; both refresh the same integ-destroy marker.
For local-execution-touching PRs (any change under src/local/**, src/cli/commands/local-*.ts, tests/integration/local-*/**): the integ-local markgate gate physically blocks gh pr merge when its marker is stale (see .claude/hooks/integ-local-gate.sh). The merge-time gate has a known blind spot: it reads the local working tree digest, and when gh pr merge runs from a parent worktree still on pre-PR main, the digest matches the old content and the gate passes silently — so an unverified local-execution change can reach main via the merge-from-parent path. /verify-pr runs in the PR's own worktree (post-PR content), so verifying the marker here closes that gap structurally:
# Only check when the PR diff actually touches the gate scope.
# Mirrors how `gh pr merge` is checked, but in the worktree that has the PR's content.
if git diff main...HEAD --name-only | grep -qE '^src/local/|^src/cli/commands/local-|^tests/integration/local-'; then
mise exec -- markgate verify integ-local
fi
If this exits non-zero (digest differs OR expired by TTL), run /run-integ local-<test> against a test that exercises the changed surface — local-start-api for HTTP-server / route-discovery / authorizer / container-pool changes, local-invoke for Lambda-runtime / ZIP-asset changes, local-run-task for ECS changes, local-invoke-container for container-Lambda changes, local-invoke-layers for Lambda Layers changes. The integ skill calls markgate set integ-local itself when the Docker-side check passes. As with integ-destroy, CI is necessary but not sufficient — it does not exercise Docker-based local execution.
For each region this PR may have created resources in (typically us-east-1), spot-check the most failure-prone resource types — VPCs (describe-vpcs --filters "Name=tag:Name,Values=Cdkd*/Vpc"), Lambda hyperplane ENIs (describe-network-interfaces --filters "Name=description,Values=AWS Lambda VPC ENI-*"), CloudFront Distributions, NAT Gateways. Any match against a stack name in this PR's diff = orphan, must be cleaned up before merge.
No stale references
src/index.ts exports are consistentCode review
First, run /review-pr <N> to get a size-appropriate review plan. The skill outputs one of:
pr-code-reviewer agent (the skill emits a ready-to-paste Agent call).pr-spec-reviewer / pr-code-reviewer / pr-test-reviewer in parallel (single message, three Agent tool calls).The skill applies bias factors (security surfaces bump up; pure-infra / docs / tests-only bump down). Trust the recommendation; override only when you have a concrete reason (note the reason here).
Synthesize the reviewer reports (or your inline read) into a pass / issues-found verdict. Any blocker → fix-back loop before continuing.
git diff main...HEAD — confirm the diff is what you reviewed (no last-minute commits slipped through).
For each change: is it correct? complete? necessary?
Check for:
Verify all callers of changed functions handle the new behavior
Verify type definitions are consistent with implementation
Shared-utility regression check: if any file under src/utils/** (or another widely-imported module) changed, list every importer (grep -rl "from '\.\./.*utils/<file>'" src tests) and walk through each one to confirm the new behavior is correct for them. A change to a shared helper is only "done" when every caller has been considered.
Internal-interface contract change check: if the diff changes the semantics of arguments an interface receives — even if the type signature is unchanged — list every implementer and walk through each one. Examples that count as a contract change: provider.update's newProperties shifting from "full desired state" to "partial / overlay / etc."; an intrinsic resolver's input format changing; a state schema field's invariant changing. The risk is implementers that silently treat the old shape's invariants as load-bearing (e.g. SNSTopicProvider.update treating newProps[K] === undefined as "remove K from AWS"). PR #161 hit this — the first-pass design ("drifted-only partial newProperties") had to be reworked after audit found SNSTopicProvider and IAMRoleProvider.updateManagedPolicies would silently clear non-drifted attrs. Audit BEFORE writing tests against the new design, not after — discovering the breaks via tests-after-design forces a design rework and invalidates the tests already written.
# For provider interface changes:
grep -rln "implements ResourceProvider" src/provisioning/providers/
# For each implementer, read the body of the affected method and write
# down what it assumes about the argument's shape — truthy gates,
# diff-based "absent = remove" semantics, JSON.parse on stringly-typed
# input, etc. The new contract must preserve every assumption that
# is load-bearing, OR every implementer must be updated in the same PR.
See feedback_internal_contract_audit_first.md for the full pattern.
Live-test changed behavior
vp run buildnode dist/cli.js <subcommand> <args> against tests/integration/<example>/cdk.out or a real state bucket; verify each output mode (--long / --json / patterns / etc.).cdkd-state-test).Retrospective + rules update
/check+check-gate / /check-docs+check-gate / /verify-pr+verify-pr-gate / /run-integ+integ-destroy-gate template.~/.claude/projects/.../memory/ so they land regardless of PR boundaries.verify-pr marker covers — skipping this step means the marker is set on incomplete work.Residual review-nit sweep (mandatory — added 2026-05-22 after a multi-PR session left ~9 reviewer-flagged nits unfiled when the parent declared "session complete")
/review-pr reviewer agent output during this session (including re-reviews after fix-back), walk the reviewer's "Minor / Nit / Informational" section.verify-pr marker:
verify-pr marker until every reviewer-flagged item is on one of those three paths.~/.claude/projects/-Users-goto-pc-github-cdkd/memory/ (with a MEMORY.md index entry) OR explicitly de-prioritized in the chat / PR body.feedback_pr_body_no_hash_for_item_numbers.md):
gh pr view <PR> --json body -q .body). For every (#N) parens-form reference, check whether it's adjacent to a close keyword (closes / fixes / resolves, case-insensitive).Closes #N (auto-close fires), OR add a manual gh issue close <N> step to the merge sequence and note it in the PR body.closes-paren-form-gate.sh hook ALREADY blocks gh pr merge for the Closes (#N) pattern — this skill step is the human-readable backup that catches the issue BEFORE the merge attempt.feedback_session_completion_audit_required.md — claiming "session complete" / "残作業なし" without running this sweep is the exact violation that surfaced this rule.PR title + body freshness (skip if no PR exists yet — /create-pr will write them from scratch)
gh pr view <PR> --json title -q .title and confirm it still describes the union of commits on the branch. If a later commit added a separate concern (e.g. an unrelated fix, an opportunistic refactor), broaden the title. Update via gh api -X PATCH repos/{owner}/{repo}/pulls/{number} -f title="..." (NOT gh pr edit --title, which currently fails silently due to GraphQL Projects-classic deprecation — see hook gh-pr-edit-deprecation-gate.sh).gh pr view <PR> --json commits -q '.commits | length' — commit count on the PRgit log main..HEAD --oneline | wc -l — commit count locallygh pr view <PR> --json body -q .body) and compare against the actual final diff (git diff main...HEAD). Flag any of:
# Write desired body to a file (avoids shell escaping issues with backticks)
cat > /tmp/pr-body.md <<'EOF'
## Summary
...
## Test plan
...
EOF
gh api repos/{owner}/{repo}/pulls/{number} -X PATCH --field "body=@/tmp/pr-body.md" -q '.html_url'
Note: gh pr edit --body may fail with "Projects (classic) is being deprecated" — fall back to the gh api PATCH form above.
gh pr view <PR> --json body -q .body | head -5 that backticks and special chars rendered correctly.Present results as a table:
| Check | Result |
|---|---|
| typecheck | pass/fail |
| lint | pass/fail |
| build | pass/fail |
| tests (N files, M tests) | pass/fail |
| test coverage for changes | pass/fail |
| CI | pass/fail |
| working tree | clean/dirty |
| docs consistency | pass/fail |
| leftover resources | none/found |
| integ-destroy marker (deletion-touching PRs only) | fresh/stale/n-a |
| integ-broad marker (cross-cutting deploy/destroy PRs only) | fresh/stale/n-a |
| integ-local marker (local-execution-touching PRs only) | fresh/stale/n-a |
| code review (incl. shared-utility callers) | pass/issues found |
| live-test changed behavior | pass/skipped/issues found |
| retrospective + rule proposals | done/skipped |
| residual review-nit sweep (filed / addressed / accepted) | N items / 0 unhandled |
auto-close audit (no Closes (#N) in body) | clean / N traps fixed |
| PR title + body freshness | up-to-date/stale (updated)/n-a (no PR yet) |
If all pass, confirm "PR is ready to merge." If any fail, list the issues to fix.
After all checks pass, record THREE markers via markgate so the gate hooks allow the next git commit, gh pr create, and gh pr merge. /verify-pr is a superset of /check (code correctness) and /check-docs (docs consistency), and adds live-test + retrospective + scope-match on top — so its success implies all three. cdkd pins markgate via mise, so use mise exec to avoid PATH issues when shims aren't active:
mise exec -- markgate set check
mise exec -- markgate set docs
mise exec -- markgate set verify-pr
The verify-pr marker is the one consulted by .claude/hooks/verify-pr-gate.sh to allow gh pr create and gh pr merge. It is intentionally settable ONLY by this skill — running it by hand from a shell to bypass the gate defeats the whole point. If a check legitimately cannot pass right now (e.g. the live-test cannot run because the user lacks AWS credentials), say so explicitly in the report and DO NOT set the marker — the gate exits non-zero so the human can decide whether to override.
Then, if there are uncommitted changes (e.g., lint fixes, doc updates made during this run), commit them and push to the remote. This ensures the remote branch is always up to date when reporting "PR is ready to merge."
Skip the marker + commit step if any check failed.