feat: ship a Kiro power that drives this accelerator - #41
Conversation
|
Commit: Security Scan Results
|
Add kiro/agentcore-enterprise-platform/, a Kiro power carrying the operational knowledge this repository does not: which profile fits a described situation, what each module deploys versus what its title suggests, which command proves a layer works rather than that its stack reached CREATE_COMPLETE, why module 6 goes silent for seven minutes, which flags are sharp, and which security controls are enforced rather than merely declared. POWER.md routes; 14 steering files hold the detail and load on demand. Six of those steering files are runbooks - deploy the platform, deploy one module, verify, recover a failed deploy, audit cost, tear down. They are ordered procedures with a verify gate after each step and explicit halt conditions, for driving a deployment rather than explaining one. Everything that creates, changes, deletes or bills is proposed one command at a time with its cost stated, and waits for approval. Why it ships here instead of in a repository of its own: a hallucinated --flag reads exactly like a real one and only fails in front of a user, and a renamed flag is indistinguishable from a hallucinated one a month later. scripts/check-kiro-power.sh resolves every file:line citation against this tree, checks every restated profile sequence against PROFILE_MODULES in scripts/deploy.sh, and checks every cited --flag against the script it is used with, so a rename breaks the build instead of quietly breaking someone's session. It also asserts installability: Kiro's power validator rejects a directory containing anything outside POWER.md, mcp.json and steering/*.md - including a hidden file at any depth - with an error that does not name the offending file, so the gate ports that allowlist rather than trusting it. That is also why the six runbooks are steering/runbook-*.md rather than skills/<name>/SKILL.md: skills/ is not on the installer's allowlist, so on an installed power every runbook read would fail. The gate is stdlib-only with no AWS and no network calls, and runs in the existing shell-checks job, which is already this repo's no-AWS self-checks job and already sets up Python 3.13. The power was written against bbc1a0b and re-verified against this base. That surfaced seven stale claims. Six are the argument for the CI gate but also its limit - the gate caught none of them, because they are semantic rather than textual. The seventh it caught outright: - --profile no longer "exports feature flags"; it materializes presets/X.yaml as platform.yaml. The scope trap survives (still cdk deploy --all) but the durability problem it used to imply is fixed. - ls --profile <p> was documented as a free read-only way to measure the blast radius. The argv pre-scan runs on every action but config and is suppressed only by --dry-run, so it now rewrites platform.yaml. Verified directly. The power now points at expected_stacks() instead. - The parser fails closed. --dryrun, --modul 6, --stack=identity, --team platfrom and --profile greenfeld all exit 1 now; four of the power's documented traps were fixed upstream and are removed. What survives is deploy --dry-run, accepted and ignored, so that one is promoted to the headline. - confirm_footprint prints cdk ls and blocks before --all, so the trap is interactive-visible and silent only under --yes / NON_INTERACTIVE=1. - Bare deploy --team viability is manifest-dependent now: a2a defaults false in the schema, so --team agent fails under greenfield, migration and security-focused. The old table claimed it always worked. - 3LO providers need a *_client_secret_name; plaintext keys are rejected and a client id without a secret name fails at synth. - scripts/test.py is gone (74db5ae), replaced by scripts/verify.py and a deploy.sh verify action. The gate failed on the citation, which is exactly what it is for. The power documented test.py's inability to fail in detail; that analysis is now history, so verify.md keeps it as a pre-upgrade symptom - a red AccessDeniedException on step 2 identifies an older checkout - and documents verify.py's own two limits instead: it verifies what the configuration promises rather than what the account contains, and it has no check for -auth, -identity, -security or uc-*. deploy.sh verify is added to the actions table, along with the fact that the script's one-line Usage: string omits it. Naming a retired path is a category the gate now handles explicitly rather than by rewording around it: RETIRED maps the path to why it went, and the check asserts the path is still *absent*, so a resurrected scripts/test.py fails instead of sitting silently in an allowlist. Negative-tested. Also added, because a user will ask and the power was silent: the use-cases/ extension point and its four guardrails, --yes, and the post-destroy sweep - including that NON_INTERACTIVE=1 without --yes reports leftovers and leaves them, which can exit 0 with a NAT still billing. README.md gets a "Drive It with Kiro" section plus a pointer from the five-step path, and states plainly that the power is blunt about this repository's posture: controls are opt-in and default off, a -security stack at CREATE_COMPLETE means the resources exist and not that anything is enforced, enable_networking=true is not an air-gapped VPC, and Cedar ships LOG_ONLY. Verification: - bash scripts/check-kiro-power.sh: all checks passed. 16 power files; 71 source citations resolve with line ranges in bounds; 25 profile sequences across 5 profiles match PROFILE_MODULES; 134 cited flag usages exist in the script they are used with; 57 anchors resolve; 214 bash blocks parse; 19 files scanned for account-identifying values. - Negative-tested the gate against 8 planted defects - a .DS_Store and a notes.txt under steering/, a .py at the power root, a skills/ tree, a citation to a file that does not exist, a line number past EOF, a wrong greenfield sequence, and a --fast-path flag that does not exist. All 8 caught; baseline green. - Behaviour above verified by running it, not by reading: materialization on ls --profile, the --dry-run suppression, the hand-edit refusal and its --yes override, and all five fail-closed parser paths. - Footprint table recomputed from expected_stacks() across all five presets; greenfield 6 and platform-team 10 match what the power already claimed, and the three missing rows are now filled in. - shellcheck --severity=warning over git ls-files '*.sh' (6 files): clean on 0.11.0. - bash scripts/check-deploy-config.sh, check-workshop-flow.sh and check-contract.sh: all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
567b758 to
898614b
Compare
|
CI caught real drift, which is the outcome the gate exists for — so worth writing down rather than just fixing.
On the gate itself: naming a retired path is a legitimate thing for this content to do, and it will recur. Rewording around the check would have been the wrong fix — that is a test driving the content instead of checking it. So it is now a declared category: Re-verified on Unrelated, and pre-existing on |
.github/workflows/label.yml runs actions/labeler@v7 on pull_request_target but .github/labeler.yml was never added, so the action 404s fetching its own config and the job has failed on every pull request since the workflow landed. The file has to be on the default branch. actions/labeler resolves its config with `ref: github.context.sha` (src/api/get-content.ts), and under pull_request_target that is the base branch commit, not the pull request head — so no contributor branch can supply it, and the workflow itself runs from the base copy. This is why the failure looks unfixable from a fork. Labels are limited to four that already exist in the repository (documentation, python, github_actions, dependencies). actions/labeler creates missing labels implicitly with an arbitrary colour, so introducing a new name would have an effect outside this file. Verification: config parsed as YAML; every key checked against the accepted set in actions/labeler src/changedFiles.ts at the pinned v7 SHA (bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13); every glob confirmed to match at least one tracked file, against all 195 files on main at b5e72f3 — one non-matching pattern (**/package-lock.json, no lockfile in this repo) was dropped rather than left as dead config; all four label names confirmed present via `gh label list`. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
What this adds
A Kiro power at
kiro/agentcore-enterprise-platform/that lets Kiro drive thisaccelerator: pick a profile from a described situation, deploy and verify a module at a
time, diagnose what failed, audit what it costs, and tear it down cleanly.
POWER.mdroutes. Fourteensteering/*.mdfiles hold the detail and load on demand, sonothing beyond the router enters context until it is needed.
Six of the steering files are runbooks — deploy the platform, deploy one module,
verify, recover a failed deploy, audit cost, tear down. They are ordered procedures with a
verify gate after each step and explicit halt conditions. Everything that creates,
changes, deletes or bills is proposed one command at a time, with its cost stated, and
waits for approval. Read-only checks are grouped, because teaching someone to click
through five prompts without reading is worse than not prompting.
The content is the operational knowledge this repository does not currently write down:
what each module deploys versus what its title suggests, which command proves a layer works
rather than that its stack reached
CREATE_COMPLETE, why module 6 goes silent for sevenminutes, which flags are sharp, and which security controls are enforced rather than merely
declared.
README.mdgets a## Drive It with Kirosection and a pointer from the five-step path.Why it ships in this repository rather than in one of its own
A hallucinated
--flagreads exactly like a real one and only fails in front of a user.Worse, a renamed flag is indistinguishable from a hallucinated one a month later. Prose
about a CLI rots silently, and the failure lands on whoever is mid-session.
So
scripts/check-kiro-power.sh(new, ~558 lines, stdlib Python + bash, no AWS calls andno network) makes drift a build failure:
file:linecitation in the power against this tree and bounds-checks theline range — 66 of them.
PROFILE_MODULESinscripts/deploy.sh— 25 sequences across 5 profiles.--flagagainst the action it is used with — 133 of them.steering/x.md#anchorlink resolves — 57.anything outside
POWER.md,mcp.jsonandsteering/*.md— including a hidden file atany depth — with an error that does not name the offending file. The gate ports that
allowlist rather than trusting it, so a stray
.DS_Storefails CI here instead offailing install for a user.
It runs in the existing
shell-checksjob, which is already this repo's no-AWSself-checks job and already sets up Python 3.13. One added step.
The installer allowlist is also why the six runbooks are
steering/runbook-*.mdratherthan
skills/<name>/SKILL.md:skills/is not on the allowlist, so on an installed powerevery runbook read would fail.
The power lives at
kiro/, not.kiro/, because.gitignoredeliberately excludes.kiro/("Kiro session steering (local-only, must never ship)"). That looked like amaintainer decision worth respecting rather than overriding with a negation.
The gate's value, and its limit
The power was written against
bbc1a0band then rebased onto750e283. The gate passedclean on the new tree — and six claims were still wrong, because they were semantic
rather than textual. Worth stating plainly, since it bounds what CI here can promise:
--profileno longer "exports feature flags"; it materializespresets/<p>.yamlasplatform.yaml. The scope trap survives (stillcdk deploy --all) but the durabilityproblem it used to imply is fixed.
ls --profile <p>was documented as a free read-only way to measure the blast radius.The argv pre-scan runs on every action but
configand is suppressed only by--dry-run, so it now rewritesplatform.yaml. The power points atexpected_stacks()instead.--dryrun,--modul 6,--stack=identity,--team platfromand--profile greenfeldall exit 1 now. Four documented traps werefixed upstream and are removed from the power. What survives is
deploy --dry-run,accepted and ignored, so that one is promoted to the headline.
confirm_footprintprintscdk lsand blocks before--all, so the scope trap isinteractive-visible and silent only under
--yes/NON_INTERACTIVE=1.deploy --teamviability is manifest-dependent now:a2adefaults false in theschema, so
--team agentfails undergreenfield,migrationandsecurity-focused.The old table claimed it always worked; there is now a per-manifest matrix computed from
expected_stacks().*_client_secret_name; plaintext keys are rejected and a client idwithout a secret name fails at synth.
Also added, because a user will ask and the power was silent: the
use-cases/extensionpoint and its four guardrails,
--yes, and the post-destroy sweep — including thatNON_INTERACTIVE=1without--yesreports leftovers and leaves them, which can exit 0with a NAT gateway still billing.
On posture
The power is blunt about this repository on purpose, in the same terms as
docs/SECURITY_CONTROLS.md: security controls are opt-in and default off; a-securitystack at
CREATE_COMPLETEmeans the resources exist, not that anything is enforced;enable_networking=trueis not an air-gapped VPC; Cedar ships inLOG_ONLYwith anunconstrained permit. A facilitator who overstates the posture loses the room in the first
security question, so the power says so first.
Verification
bash scripts/check-kiro-power.sh— all checks passed. 16 power files, 19 scanned; 66citations resolve with line ranges in bounds; 25 profile sequences match
PROFILE_MODULES; 133 flags valid for their action; 57 anchors resolve; 211 bash blocksparse.
wrong profile sequence, dead anchor, broken bash, disallowed file, hidden file, path that
no longer exists).
shellcheck --severity=warningovergit ls-files '*.sh'(5 files): clean on 0.11.0.bash scripts/check-deploy-config.sh,check-workshop-flow.sh,check-contract.sh: allpass.
reading: materialization on
ls --profile, the--dry-runsuppression, the hand-editedmanifest refusal and its
--yesoverride, and all five fail-closed parser paths.expected_stacks()across all five presets.greenfield= 6 andplatform-team= 10 match what the power already claimed from anearlier measurement; the three missing rows are now filled in.
A2A, module C networking +
check_network.py, module E, alanggraph-agentswap, a fulldestroy), plus deliberate failure injection. Timings in the power are measured, not
estimated — including the destroy that exited non-zero after 11.6 minutes having deleted
4 of 10 stacks with the NAT still
available, which is why the teardown runbook checksthe exit code and then checks the NAT anyway.
Second commit:
.github/labeler.ymlThe
labeljob fails on this PR. It is not caused by this PR, and it cannot be fixed byone:
.github/labeler.ymlhas never existed onmain, soactions/labeler@v7404sfetching its own config. 15 of the last 15
labelruns failed, across 10 branches —including
feat/verify-command(#40, merged red) and a dependabot branch.actions/labelerresolves its config withref: github.context.sha(
src/api/get-content.ts),which under
pull_request_targetis the base branch commit — and the workflow has nocheckout step, so the log reads "not found locally, fetching via the api". The second
commit here adds that file.
It will not turn this PR's own
labeljob green, and the run on this head confirms it:the error is byte-identical to the run before the commit existed. The config is read from
the base, so it only takes effect for pull requests opened after this merges. I have left
the red X rather than pretend otherwise.
Because CI 404s before it parses, this repository's CI cannot exercise the config until
it is on
main. Verified locally instead: YAML parses; every key checked against theaccepted set in
src/changedFiles.tsat the pinned v7 SHA; every glob confirmed to matchat least one of the 195 tracked files; one pattern that matched nothing
(
**/package-lock.json— no lockfile here) dropped rather than left as dead config. Labelsare limited to the four that already exist (
documentation,python,github_actions,dependencies), becauseactions/labelercreates missing labels implicitly with anarbitrary colour, which would be an effect outside the file.
This is a second scoped task in one merge request, which
CONTRIBUTING.mdasks me not todo. Splitting it out is a one-command change if you would rather review it separately — say
so and I will.
One recommendation, not part of this PR
This content also exists as a standalone power repository, which means two copies of the
same ~40k words. Vendoring here is the right call for the reason above — CI can only verify
the copy that sits next to the source — but the better long-term shape is for this
repository to be the authoring home and the standalone repo to be generated from
kiro/agentcore-enterprise-platform/. Happy to wire that up if you'd like it; it needs adecision from you about where the power is published from, so it is deliberately not in
this diff.
Two further things I noticed and left alone. The labeler config above is the only place I
stepped outside the "one merge request addresses one scoped task" rule, for the reason given
there; these two I did not:
README.mdshows a barepython scripts/check_network.py, which fails outside anactivated venv where every other documented invocation uses
.venv/bin/python.--teamsplit, which a facilitator runningthree parallel teams would want.
Both are separate PRs if wanted.
🤖 Generated with Claude Code