Plan-only CI with policy gates
The expensive AgentCore mistakes are visible in a plan: a memory that would be replaced (every conversation gone), a gateway that would be replaced (a new URL for every client), a role that grants everything. A pull request is the cheapest place to stop them, and a plan is the right artefact to check — it is what would actually happen.
${CLAUDE_SKILL_DIR} is set by the skill runtime to this skill's own
directory. Use it verbatim in every command below; a sibling skill's scripts
are at ${CLAUDE_SKILL_DIR}/../<skill>/scripts/.
Flow
-
Plan the workflow, read-only:
python3 ${CLAUDE_SKILL_DIR}/scripts/gen_ci.py <project> --dry-run--tf-rootfor the stack to plan (default: the root that reaches the AgentCore resources),--role-varfor the GitHub variable holding the plan role (defaultAWS_PLAN_ROLE_ARN),--region,--terraform. -
Report the root, Terraform or Terragrunt, the pins found (or
CI010if the fallbacks are used), and the role note (CI011). -
Options: write the workflow and vendored checker (free); run the policy on an existing saved plan first (free). Confirm with
AskUserQuestion, then write. -
Try the gate locally on a plan the user produced:
python3 ${CLAUDE_SKILL_DIR}/scripts/policy_check.py agentcore.plan.json--allow-replace <address>only for a replacement a human has reviewed. -
Verify the vendored runner works from the repository root:
python3 ci/agentcore/run.py review ..
The CI0xx band
| Id | Severity | Trigger |
|---|---|---|
CI001 | error | the plan deletes or replaces a memory, gateway, credential provider, workload identity, Cognito user pool or DynamoDB table |
CI002 | error | a planned IAM policy allows * on * |
CI003 | error | a gateway planned with authorizer_type = "NONE" |
CI004 | warn | a gateway planned without supported_versions |
CI005 | info | the plan has no changes |
CI006 | info | the AgentCore resources the plan creates (the billing surface) |
CI010 | info | no SHA-pinned actions in the repository; fallback pins used |
CI011 | info | the plan job's role must be read-only |
Decision rules
- CI plans; humans apply. The workflow has no apply job, and the plan role should not be able to write.
- Allowances are per address.
--allow-replacenames one resource; there is no "allow all". - Pin actions to SHAs. Reuse the repository's pins; the fallbacks are the ones the first user's repository carried on 2026-10-05, labelled with tags.
- Vendored, not installed.
ci/agentcore/is plain Python 3.10+; updating the plugin means re-runninggen_ci.py --forceand reviewing the diff.
References
| Question | File |
|---|---|
| The workflow, step by step; roles; Terragrunt | plan-only-ci.md |
| Each policy rule, why, and how to allow a reviewed exception | policy-gates.md |
Scripts
| Command | Does | Exit codes |
|---|---|---|
scripts/gen_ci.py <project> [--tf-root D] [--role-var V] [--dry-run] [--force] [--json] | the workflow plus ci/agentcore/ | 0 written/planned, 1 nothing, 2 bad arguments |
scripts/policy_check.py <plan.json> [--allow-replace ADDR ...] [--json] | the CI0xx gate on a saved plan | 0 pass, 1 policy error, 2 not a plan |
Completion checklist
- The workflow was shown as a write plan and confirmed before writing.
- It has no apply step, and the plan role was described as read-only.
-
policy_check.pywas run on a real plan, or the user was told how. - The vendored runner was exercised from the repository root.