一键导入
run-integ
Run integration tests (deploy + destroy) against real AWS. Use when you need to verify cdkd works end-to-end with actual AWS resources.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Run integration tests (deploy + destroy) against real AWS. Use when you need to verify cdkd works end-to-end with actual AWS resources.
用 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.
Recommend the right reviewer count for a PR based on size + bias factors. Outputs a concrete plan (inline spot-check / 1 reviewer / 3-axis parallel) plus ready-to-paste Agent dispatch prompts when reviewers are warranted.
| name | run-integ |
| description | Run integration tests (deploy + destroy) against real AWS. Use when you need to verify cdkd works end-to-end with actual AWS resources. |
| argument-hint | <test-name|all> [--synth-only] [--no-destroy] |
Run integration tests against a real AWS account. These tests deploy actual AWS resources, verify them, and clean up.
test-name: Which test to run. Run ls tests/integration/ to see all available tests. If not specified, use the AskUserQuestion tool to ask which test to run, showing the available options.all: Run all tests--synth-only: Only run synthesis, skip deploy/destroy--no-destroy: Deploy but don't destroy (for debugging)--deploy-args "<args>": Forward extra arguments to the cdkd deploy invocation (e.g. --deploy-args "--aggressive-vpc-parallel"). Use when validating an opt-in deploy flag end-to-end against bench-cdk-sample etc. — the destroy step is unaffected (opt-in flags so far are deploy-only).Build first: Run vp run build to ensure dist/ is up to date.
List available tests: Run ls tests/integration/ to discover all test directories dynamically. Do NOT rely on a hardcoded list.
Determine state bucket: Resolve dynamically via aws sts get-caller-identity --query Account --output text to get the account ID, then construct cdkd-state-{accountId} (region-free, the current default since PR #62 / v0.11.0). If that bucket doesn't exist, fall back to the legacy cdkd-state-{accountId}-us-east-1 and note the deprecation in the report.
Pre-flight orphan scan (mandatory — fail fast on prior-run leftovers instead of going through CREATE + rollback):
Before invoking deploy, scan AWS for resources matching the stack name from this test that have no business existing yet. The scenario this catches: a previous integration run was killed mid-deploy, leaving orphan Event Source Mappings / Lambda functions / ENIs / IAM roles whose names match the stack about to be deployed. cdkd's diff calculation does NOT see these (they're not in state), so the deploy attempts CREATE — which collides with the orphans, fails immediately with ResourceAlreadyExists, and forces a CREATE-then-rollback cycle. Failing at the start is much cheaper than partway through.
Synth first (without deploy) to learn the stack name and the resource types in the template, then for each scenario in scope run a targeted scan:
# Always run these (cheap, broadly applicable):
aws s3 ls s3://<bucket>/cdkd/<StackName>/ --region us-east-1
aws iam list-roles --query 'Roles[?contains(RoleName, `<StackName>`)].RoleName' --output text
aws lambda list-functions --region us-east-1 \
--query 'Functions[?contains(FunctionName, `<StackName>`)].FunctionName' --output text
# Run when the template uses Lambda EventSourceMapping (the orphan ESM
# case bit cdkd in 2026-05-02: AlreadyExists + rollback cycle on a fresh deploy):
aws lambda list-event-source-mappings --region us-east-1 \
--query 'EventSourceMappings[?contains(FunctionArn, `<StackName>`)].[UUID,FunctionArn]' --output text
# Run when the template uses VPC + Lambda VpcConfig (hyperplane ENIs
# outlive their function):
aws ec2 describe-network-interfaces --region us-east-1 \
--filters "Name=description,Values=AWS Lambda VPC ENI-<StackName>*" \
--query 'NetworkInterfaces[].[NetworkInterfaceId,Status]' --output text
If anything is found, abort the test run with a clear report listing the orphans and the cleanup commands the user should run (aws lambda delete-event-source-mapping --uuid …, cdkd state destroy <StackName> --yes, etc.) — do NOT proceed with deploy. Resuming on top of orphans is the failure mode this step exists to prevent.
If nothing is found, the deploy can proceed cleanly.
Run the test(s):
Dispatch: a verify.sh in tests/integration/<test-name>/ is for tests with non-standard flows (drift-injection, multi-stage validation, etc.) — the script owns its own deploy + verify + destroy cycle. The standard flow below is for plain "deploy this stack and destroy it" smoke tests. Pre-flight (step 4) and the post-run verification (steps 6 + 7) apply to BOTH paths — they are the safety net that catches a buggy verify.sh leaking resources.
tests/integration/<test-name>/npm install if node_modules doesn't existtests/integration/<test-name>/verify.sh exists, run it instead of the standard flow:
AWS_REGION=us-east-1 STATE_BUCKET=<bucket> bash verify.shverify.sh failures.verify.sh), run the standard flow (synth → deploy → destroy):
node ../../../dist/cli.js synth --region us-east-1multi-stack-deps, composite-stack,
cross-stack-references), pass --all to deploy and destroy.
Without --all, deploy/destroy will fail with Multiple stacks found: ... Specify stack name(s) or use --all.node ../../../dist/cli.js deploy [--all] [<extra-deploy-args>] --region us-east-1 --state-bucket <bucket> --verbose
--deploy-args "<args>" was passed to the skill, splice those args into the deploy invocation verbatim. Don't apply them to destroy.node ../../../dist/cli.js destroy [--all] --region us-east-1 --state-bucket <bucket> --forceVerify cleanup:
aws s3 ls s3://<bucket>/cdkd/ --region us-east-1 to confirm no leftover stateaws iam list-roles --query 'Roles[?contains(RoleName, \{StackName}`)].RoleName'`aws lambda list-functions --region us-east-1 --query 'Functions[?contains(FunctionName, \{StackName}`)].FunctionName'`aws s3api list-buckets --query 'Buckets[?contains(Name, \{stackName-lowercase}`)].Name'`aws ecr describe-repositories --region us-east-1 --query 'repositories[?contains(repositoryName, \{stackName-lowercase}`)].repositoryName'`aws dynamodb list-tables --region us-east-1 --query 'TableNames[?contains(@, \{StackName}`)]'`aws ec2 describe-vpcs --filters "Name=tag:Name,Values={StackName}/Vpc" ...Auto-cleanup orphans (mandatory when destroy didn't fully succeed):
Trigger this step whenever any of the following is true:
destroy step in step 5 reported a non-zero error count (e.g. "X failed to delete")What to do:
aws ec2 describe-network-interfaces --filters "Name=vpc-id,Values=<vpc>" → aws ec2 delete-network-interface). Some may be in-use initially — re-poll until they go available, then delete.aws ec2 delete-security-group) — must come after the ENIs that reference them are gone.aws ec2 delete-subnet) — must come after every ENI in them is gone.aws ec2 delete-vpc) — last.aws s3 rm s3://<bucket>/cdkd/<StackName>/ --recursive. (cdkd state orphan <StackName> is the cdkd-native equivalent and also handles the lock key.)--region).Never end the run with orphan resources still present in AWS. Cost (NAT GW alone is ~$1/hr) and account hygiene make this non-negotiable. If a resource genuinely cannot be deleted after reasonable retries, surface it to the user with the exact ID, region, and what was tried — but only after the auto-cleanup pass.
Report results: Show pass/fail for each test, including resource counts and timing. Always state explicitly "destroy completed: 0 errors, 0 orphans" or itemize what remained / what was force-cleaned.
Set the integ-destroy markgate marker (only on full clean success):
When — and ONLY when — all of the following hold:
record the gate so subsequent gh pr merge calls are unblocked:
mise exec -- markgate set integ-destroy
If any of the above failed, do NOT set the marker — that is the
whole point of the gate. The hook
.claude/hooks/integ-destroy-gate.sh will block gh pr merge for
any PR that touches deletion-related code (see .markgate.yml
integ-destroy.include) until this marker is fresh, so a
destroy-untested change physically cannot reach main.
Set the integ-local markgate marker (only for local-* tests, on full clean success):
When the integ test name starts with local- (i.e. local-invoke,
local-start-api, local-run-task, local-invoke-container,
local-invoke-from-state, local-invoke-layers,
local-invoke-python / -ruby / -java / -dotnet / -provided,
local-start-api-cors, or any future local-* test), ALSO set
the integ-local marker after a clean Docker run.
Required cleanup verification BEFORE setting the marker (in
addition to the conditions above for integ-destroy):
# Both queries MUST return empty. If either lists any IDs, the
# marker is NOT set and the user is shown the orphan container /
# network IDs to clean up via `docker rm -f` / `docker network rm`.
docker ps --filter name=cdkd-local- --format '{{.ID}}'
docker network ls --filter name=cdkd-local-task- --format '{{.ID}}'
When BOTH return empty AND the integ test exited cleanly:
mise exec -- markgate set integ-local
The hook .claude/hooks/integ-local-gate.sh blocks gh pr merge
(and git merge) for any PR that touches cdkd local * code
(see .markgate.yml integ-local.include) until this marker is
fresh, so an unverified local-execution change physically cannot
reach main. Same TTL (14d) and same "do NOT call markgate set
directly to bypass" rule as integ-destroy.
Note: Non-local-* tests (e.g. bench-cdk-sample, lambda)
leave the integ-local marker untouched. The two markers are
independent — a lambda integ run does not refresh integ-local,
and a local-invoke run does not refresh integ-destroy unless
its verify.sh also exercises a real-AWS deploy + destroy (the
local-invoke-from-state test does, so it can set BOTH).
Set the integ-broad markgate marker (only for BROAD integ tests, on full clean success):
The broad-integ set covers tests that exercise multi-resource deploy/destroy paths (VPC + NAT + Lambda hyperplane ENI, Custom Resource, DAG with 5+ types across 2+ levels). A test is "broad" iff its name is one of:
bench-cdk-sample
lambda
microservices
drift-revert
drift-revert-vpc
multi-stack-deps
multi-resource
remove-protection
export
(Keep this list in sync with .claude/hooks/integ-broad-gate.sh's
error message and the matching memory rule
feedback_cross_cutting_needs_broad_integ.md.)
When the integ test name is in the broad set AND the destroy step
finished cleanly with 0 errors / 0 orphans (= the same conditions
that flip integ-destroy), ALSO record the broad-integ sentinel
and flip the marker:
# Sentinel content is informational (human-readable, helps the
# next /verify-pr run know which integ was last run). The
# `integ-broad` markgate gate's include scope is just this file,
# so writing the test name flips its digest naturally.
printf '%s ran at %s\n' "<test-name>" "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
> .markgate-broad-integ-test
mise exec -- markgate set integ-broad
The hook .claude/hooks/integ-broad-gate.sh blocks gh pr merge
for any PR that touches cross-cutting deploy/destroy code (see the
hook's CROSS_CUTTING_REGEX for the canonical list) until this
marker is fresh. Same 14d TTL and same "do NOT call markgate set
directly to bypass" rule as the other AWS-coupled gates.
Narrow feature integs do NOT set this marker — e.g.
import-value-strong-ref flips integ-destroy but leaves
integ-broad alone. This is the structural fix for the PR #348
incident: a 2-stack S3+SSM feature fixture is sufficient for the
feature's correctness, but a cross-cutting change to
src/deployment/deploy-engine.ts etc. needs the broader VPC /
Lambda / multi-resource coverage that only the broad set provides.
Set the integ-schema-migration markgate marker (only for
schema-v*-to-v*-migration tests, on full clean success):
cdkd's S3 state schema is the actual user contract. A schema version bump (e.g. v5 -> v6) MUST be transparently auto-migrated by the new binary AND verified by a real-AWS integ test that proves the round-trip: deploy under vN -> swap binary -> read works against the vN state without re-deploying -> next write upgrades to vN+1 silently -> destroy clean / 0 orphans.
A test is "schema-migration" iff its name matches the pattern
schema-v<N>-to-v<N+1>-migration (e.g.
schema-v5-to-v6-migration). Test fixtures live under
tests/integration/schema-v<N>-to-v<N+1>-migration/.
When the test name matches AND the destroy step finished cleanly
with 0 errors / 0 orphans (= the same conditions that flip
integ-destroy), ALSO record the schema-migration sentinel and
flip the marker. Unlike integ-broad, this gate's marker uses
src/types/state.ts as its include scope directly, so the
sentinel is informational only (no separate sentinel file is
needed for markgate's digest):
mise exec -- markgate set integ-schema-migration
The hook .claude/hooks/integ-schema-migration-gate.sh blocks
gh pr merge for any PR that bumps the StackState.version
literal type in src/types/state.ts (detected via precise gh pr diff grep — non-bump edits to state.ts pass through with no
false positive) until this marker is fresh. Same 14d TTL and
same "do NOT call markgate set directly to bypass" rule as the
other AWS-coupled gates.
Non-schema-migration tests do NOT set this marker — e.g.
lambda flips integ-destroy + integ-broad but leaves
integ-schema-migration alone. A schema-bump PR must run a
test named exactly schema-v<N>-to-v<N+1>-migration to clear
this gate. See memory rule
feedback_schema_version_migration_integ_required.md for the
full migration-test checklist + the absolute requirement that
auto-migration must be transparent (no user action required
on upgrade).
Record the run in the integ ledger (MANDATORY — every run, pass OR fail):
docs/_generated/integ-last-run.tsv is a COMMITTED (NOT gitignored), update-type
ledger — one row per test — so anyone can see when each integ last ran and whether
it passed. This answers "has this been run recently?" / "this hasn't run in months,
it's risky to trust" without trawling CI history, and feeds /pick-integ. Write it
on EVERY /run-integ invocation, pass or fail, right after the marker steps above
(or right after a failure is recorded — never skip it on failure).
Columns (TAB-separated): test last_run_iso result duration_s flow note
result: PASS only when the run finished cleanly (destroy 0 errors AND 0 orphans;
verify.sh exited 0) — the SAME bar as the markgate markers. Otherwise FAIL.last_run_iso: date -u +%Y-%m-%dT%H:%M:%SZ (UTC). flow: verify.sh or standard.duration_s: optional wall-clock seconds. note: short reason / finding one-liner.Portable update (do NOT use grep -P — unavailable on macOS BSD grep; use awk to drop
the test's old row, then append the new one):
LEDGER="docs/_generated/integ-last-run.tsv"
[ -f "$LEDGER" ] || printf '# integ-last-run ledger (update-type: one row per test). cols: test\tlast_run_iso\tresult\tduration_s\tflow\tnote\n' > "$LEDGER"
TEST="<test-name>"; TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
RESULT="PASS"; DUR="<seconds>"; FLOW="verify.sh"; NOTE="rc ok, orph clean"
tmp="$(mktemp)"; awk -F'\t' -v t="$TEST" '$1!=t' "$LEDGER" > "$tmp" && mv "$tmp" "$LEDGER"
printf '%s\t%s\t%s\t%s\t%s\t%s\n' "$TEST" "$TS" "$RESULT" "$DUR" "$FLOW" "$NOTE" >> "$LEDGER"
Commit the ledger update with the branch's changes (it is part of the integ run record; the file is intentionally committed so the last-run history is shared across sessions).
--region us-east-1 for integration testscdkd deploy / cdkd destroy directly from a shell — the orphan-cleanup contract above is part of the integration test, not optional