Repository navigation
docs: rank deployment above probes in agent evidence hierarchy - #4867
Merged
kddejong merged 3 commits intoOct 24, 2026
Merged
Conversation
The evidence hierarchy in .kiro said 'a live API probe' without defining it, and an agent read 'aws cloudformation validate-template' as satisfying it. validate-template parses a template and checks template-level structure; it never invokes a resource provider, so it cannot establish a property-level constraint, and treating it as proof risks encoding a limit CloudFormation does not enforce. Rank a real deployment first, then real templates known to deploy, then a service-level API probe, and name validate-template explicitly as not evidence. Also record environment facts that produced wrong conclusions: the -m data sorted-keys requirement that only CI enforces, strict_types diverging between conftest and real rule paths, editable-install resolution from a worktree, E3001/E6001 on non-alphanumeric scratch names, and the two decoder entry-point shapes.
The evidence hierarchy ranks a real deployment first and names validate-template as not evidence, but it gave no technique for obtaining a deployment. That gap is what pushed a reviewing agent to validate-template: the file demanded deploy-grade evidence and offered no affordable route to it. A parameter's Default is validated in the CloudFormation control plane before any resource exists, so a create-stack probe is free. The template carries the parameter under test plus one AWS::CloudFormation::WaitConditionHandle. A rejection returns synchronously and creates no stack at all; an acceptance creates one free resource, deleted immediately. This covers the whole parameter-constraint family, E2015, E2003 and their neighbours. Record the measured coercion model: CloudFormation renders a boolean Default lowercase. Default: true deploys against AllowedValues ["true", "false"], and fails against ["True", "False"]. Seven cases, us-east-1, 2026-10-23. The account id stays in the PR description, since .kiro ships in the public repo. Point false-positives.md at the technique instead of repeating it. rule-development.md already routes command detail to verification.md, so it needs no pointer.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #4867 +/- ##
=======================================
Coverage 94.68% 94.68%
=======================================
Files 439 439
Lines 16004 16004
Branches 3108 3108
=======================================
Hits 15153 15153
Misses 468 468
Partials 383 383
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
kddejong
marked this pull request as ready for review
October 24, 2026 00:17
This was referenced Oct 24, 2026
kddejong
added a commit
that referenced
this pull request
Oct 26, 2026
…d constraints (#4932) validate-template invokes no resource provider, so it cannot establish a property value, limit, enum or cross-property rule. That reasoning does not extend to template-level structure, which the control plane itself parses and which validate-template is therefore testing directly. Recorded us-east-1, 2026-10-26: a parameter of Type: List<AWS::SSM::Parameter::Value<String>> draws the identical "Template format error: List of AWS::SSM::Parameter::Value is not supported." from validate-template and from create-stack, and no stack is created. Raised by issue #4854, where the reporter cited validate-template for a parameter type and our own context said that was not evidence. The deployment-first evidence ranking from #4867 is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The agent context files under
.kiro/rank evidence for a constraint as"real templates that deploy, then a live API probe, then AWS docs, then the
base CFN schema". The phrase "a live API probe" was never defined, and an agent
reading it treated
aws cloudformation validate-templateas satisfying tier 2 —running it 18 times and reporting the result as the strongest evidence in a
review.
validate-templateparses a template and checks template-level structure. Itnever invokes a resource provider, so for the overwhelming majority of cfn-lint
rules it proves nothing, and where it does respond it may be stricter or looser
than
CreateStack. Treating it as proof risks encoding a constraintCloudFormation does not actually enforce, which is exactly the false-positive
class
.kiro/context/false-positives.mdexists to prevent.What changed
Docs only — nothing under
src/ortest/. There is no code change here, sothere are no unit tests to add.
.kiro/context/false-positives.mdvalidate-templateexclusion; review checklist item 1 now asks for a deployment or the owning service's API.kiro/context/verification.mdvalidate-templateexclusion, an AWS-docs fetching note, and five verified environment traps (see below).kiro/steering/rule-development.mdThe corrected hierarchy, strongest first: a real deployment; real templates
known to deploy; a service-level API probe; AWS documentation; the base CFN
resource schema. Plus, stated as its own item:
aws cloudformation validate-templateis not evidence for a property-level constraint.Wording was kept in each file's existing voice and length — the ranked lists
stay ranked lists.
Secondary: environment facts added to
verification.mdEach was re-run in this clone before being written. Five of six went in; one was
corrected by testing and one claim in it was dropped.
Verified and added
-m datatrap.pyproject.tomlsetsaddopts = ["-m not data"], sotest/integration/test_data_file_format.pyis deselected locally, while.github/workflows/ci-pr-data.yamlrunstox -e py -- -m "data"on anychange under
src/cfnlint/data/**. Confirmed by replicating the test'sassertion on a tracked data file: the on-disk content passes, the same
content with reversed top-level keys fails.
format_json_stringisindent=1, sort_keys=True, separators=(",", ": ")plus a trailing newline.Also documented a trap within the trap: running
-m datalocally fails on agitignored downloaded schema under
src/cfnlint/data/schemas/resources/thatarrives without a trailing newline, so the red result is often about a file
you never touched.
strict_typesmakes negative unit tests lie.test/unit/conftest.pydefaults
strict_types=True;rules/resources/Metadata.py:71,rules/outputs/Value.py:55andrules/resources/properties/Properties.py:87evolve toFalse— linereferences confirmed. Demonstrated directly:
"123"against{"type": "integer"}yields'123' is not of type 'integer'understrict_types=Trueand zero errors underFalse.python -m cfnlintfails withNo module named cfnlint.__main__(there is no__main__.py; the entrypoint is
cfnlint.runner:main). Confirmed the editable-install behaviour byadding a marker line to a throwaway worktree's
src/cfnlint/__init__.py:run from the worktree, the venv interpreter resolved the main checkout and
the marker was absent; with
PYTHONPATH=<worktree>/srcit resolved theworktree and the marker was present. The
.pthholds the main checkout'sabsolute
srcpath. The throwaway worktree was reverted and removed.my_bucket-1and Outputmy_out-1reportsE3001andE6001(
does not match any of the regexes: '^[a-zA-Z0-9]+$'); the identicaltemplate with
MyBucket1/MyOut1exits 0.cfn_yaml.loaddoes return the template dict directly, but the stated failure signature is
only half the story: unpacking it raises
ValueError: too many values to unpackonly when the template has a number of top-level keys other thantwo. With exactly two keys it silently binds the two top-level key names,
which is the more dangerous outcome. Both confirmed, and the text says so.
Corrected before writing
web_fetchtruncatesdocs.aws.amazon.comat ~8000 characters and that appending.mdreturnsthe full source — did not reproduce. Fetching the HTML page in full-content
mode returned the complete page including every property's
Minimum/Maximum/Allowed values. The ~8000-character cut is thedefault extraction mode, and it truncated the
.mdURL as well, mid-waythrough the property list; the
.mdrendering additionally mangles inlineURLs. So the note that went in says: use full-content mode, and treat
.mdas an alternative source subject to the same default truncation. The original
framing was not written.
Commands run
pre-commitpasses on the changed files:check vcs permalinks,fix end of files,mixed line endingandtrim trailing whitespaceall pass;the Python-only hooks are skipped because nothing Python changed.
A bare local
ruff format --check .kiro/(ruff 0.16.3) reports one file itwould reformat,
.kiro/steering/rule-development.md. This is pre-existingand not what CI runs: extracting
.kiro/fromupstream/maininto ascratch directory produces the identical single finding, and the pinned hook is
ruff v0.12.4, which does not format markdown at all.
pre-commit run --all-filesis clean. It is a fenced Python block this change does not touch,and it was deliberately left alone.
Proposed agent-prompt changes (NOT in this PR)
.kiro/agents/*.jsoncarry the same flawed wording, but all three files haveuncommitted local edits, so nothing under
.kiro/agents/is staged, changedor reverted by this PR. The replacements below are for applying by hand.
.kiro/agents/pr-reviewer.jsonneeds no change — its prompt contains noevidence hierarchy and never mentions a probe.
.kiro/agents/issue-triage.jsonBefore, inside the
FALSE POSITIVEparagraph:After:
.kiro/agents/issue-worker.jsonBefore, step 2:
After:
Follow-up commit
6f3a809— a free deployment probe for parameter constraintsThe reranked hierarchy puts a real deployment first and names
validate-templateas not evidence, but it offered no technique for obtaininga deployment. That gap is what pushed a reviewing agent to
validate-templatein the first place: the file demanded deploy-grade evidence and gave no
affordable route to it.
.kiro/context/verification.mdgains a section immediately after the rankedlist and the
validate-templateparagraph, in the file's existing voice:Defaultis validated in the CloudFormation control planebefore any resource exists, so a
create-stackprobe is free and leavesnothing behind.
AWS::CloudFormation::WaitConditionHandle. A rejection returns synchronouslyand creates no stack at all; an acceptance creates one free resource, deleted
immediately.
E2015,E2003and theirneighbours.
Defaultlowercase.Default: truedeploys againstAllowedValues: ["true", "false"]; against["True", "False"]it failsParameter 'P' must be one of AllowedValues;against
AllowedPattern: ^(True|False)$it failsParameter 'P' must match pattern ^(True|False)$. ACommaDelimitedListfailsEach value of parameter 'P' must be one of AllowedValues.Seven cases, recorded in us-east-1 on 2026-10-23. That coercion model is
what three claims in the review of PR #4797 turned on. Per the ranked list's own
instruction the account id belongs in a PR description rather than a checked-in
file, since
.kiro/ships in this public repo, so the committed text carriesregion and date only.
Pointer, not duplication
.kiro/context/false-positives.mdgains one line pointing at the technique,because its §4 makes the strongest demand for deployment evidence and previously
offered no route to it.
.kiro/steering/rule-development.mdgets nothing: its "Before Submitting"section already routes command-level detail to
verification.md, so a secondpointer would be redundant. One home plus one pointer.
Still docs-only — nothing under
src/ortest/— so there are no unit teststo add.
The
unitlint (ubuntu-latest, 3.14)failure is pre-existingVerdict: not this PR's doing, and already fixed on
upstream/main, so themerge below resolves it.
The failing job's log names exactly one test:
on
test/fixtures/templates/integration/aws-ec2-subnet.yaml, withSecond list contains 4 additional elements— fourE3020matches(
'CidrBlock' should not be included with 'Ipv4NetmaskLength'and its converse,on
Subnet1andSubnet5) that a newer schema set produces and the recordedsnapshot did not list. Upstream commit
8f7e4857e("Update tests for schemachanges", #4872) adds precisely those four entries to
test/fixtures/results/integration/aws-ec2-subnet_yaml.json.Reproduced on both trees against one identical freshly downloaded schema set
(
schema_date2026-10-23T22:24:42Z) in an isolatedXDG_CACHE_HOME:c9be2d04d)E3020entries as CIupstream/main(a06f5b206)upstream/mainThe inverse holds too, which confirms the coupling: with an older local schema
set (
2026-10-22) the outcome flips, this branch passing andupstream/mainfailing. The snapshot is pinned to schema behaviour, not to any code here.
The alternative explanation —
pre-commit'sruff-formatreaching fenced codeblocks in markdown — is ruled out four ways:
tox-ghmaps Python 3.14 topy314only. Thestyleenv that runspre-commit run --all-filesis mapped to 3.13, so the failing job never ranit. The log agrees: the one tox env is
py314, and neitherruffnorpre-commitappears anywhere in it.unitlint (macos-latest, 3.13)and(windows-latest, 3.13)both passed, sothe
styleenv itself passes on this branch.Running the hooks against the changed files reports
ruff format ... (no files to check).pre-commit run --all-filesis clean on this branch, every hook passing.Nothing was changed to address the failure beyond the merge, which was required
anyway.
Bringing the branch current: merged, not rebased
The branch was
BEHIND. A rebase would require a force-push, which thisenvironment refuses, so
upstream/mainwas merged into the branch instead(
1634243). No history was rewritten and nothing was force-pushed. This PR issquash-merged, so the merge commit costs nothing in the final history; if a
linear history is preferred, a maintainer can rebase and force-push directly.
Commands run for this follow-up
pre-commit run --all-files: clean, all fourteen hooks pass.The full suite reports
19 failed, 3127 passed— identical on cleanupstream/main, caused bypydot,junit_xml,sarif_omandjschema_to_pythonbeing absent from this local venv while CI installs.[full].-m datapasses (4 tests) on both trees. No file undersrc/cfnlint/data/**changed, soci-pr-data.yamldoes not trigger..kiro/agents/remains untouched:git diff upstream/main...HEAD -- .kiro/agents/is empty, and the branch diff contains no credential block and no account id.