Imported from ilvar/stricthelm (
AGENTS.md). Install upstream withnpx skills add ilvar/stricthelm. Copyright stays with the author.
AGENTS.md
Repository-specific rules for coding agents working on stricthelm.
Project intent
stricthelm is not a new chart format. It is:
- a strict subset of Helm;
- a tooling layer around Helm's existing capabilities;
- a deterministic feedback contract for an agent-driven check → patch → re-check loop.
Do not introduce a chart parser, a template-engine fork, a policy language, a Kubernetes admission controller, or any cluster-connected check.
Layer discipline
The check pipeline has four layers and they are ordered by cost:
- chart structure;
- template source;
- helm (
lintandtemplate); - rendered manifest.
Rules belong to the earliest layer that can decide them. A rule that can be decided from template text must not wait for a render; a rule that needs a rendered object must not be guessed at from text. Every layer reports everything it finds — never stop at the first defect. Layers 3 and 4 are skipped only when an earlier layer proves the chart cannot render.
Fleet discipline
A chart with a fleet.yaml is checked with the values Rancher Fleet
actually deploys. The merge order is Fleet's, not Helm's, and it is
verified against Fleet's own source rather than its documentation:
- within a Helm block,
valuesFilesmerge over inlinevalues, in declaration order (Fleet'sgenerateValues); - a target customization's block merges over the base's (Fleet's
options.Merge); - mappings recurse, everything else is replaced (wrangler's
MergeMaps).
Do not "fix" the valuesFiles-over-values ordering to match the Helm
CLI. It is deliberate, it is what Fleet does, and tests/fleet_layer.rs
pins it.
Every configuration the bundle deploys is checked, not just the base:
Fleet's default targetCustomizationMode is FirstMatch, so the variants
are the base alone plus the base with each customization. A finding common
to every variant is reported once, untagged; anything else carries the
variant that produced it. Never collapse variants by checking only the
base — that hides defects that appear solely in production.
Anything needing a cluster — valuesFrom, templateValues — is reported
as a warning, never skipped in silence.
Diagnostic contract
The JSON shape is the public API between the tool and coding agents. Treat incompatible changes as breaking changes.
Required properties:
- output exactly one JSON document on stdout;
sourceisstricthelmorhelm;codeis stable; message text is not, and rules must be matched by code;- include
objectonly for rendered-manifest findings, andvariantonly for findings specific to one Fleet target, omitting both entirely rather than serialisingnull; - include
fixesonly when a replacement is span-exact, unambiguous, and idempotent; - never invent a replacement or an applicability;
- include start and end positions for every span and fix;
- order deterministically by
(file, line, col, code, object.path, variant); - exit
0only whenerror_count == 0.
Internal metadata such as byte offsets may be retained with #[serde(skip)],
but must not alter the public shape.
Capability discipline
Every filesystem and process effect lives in src/capability.rs, behind the
marked effects module. Nothing else in the crate may call std::fs,
std::net, or std::process; main.rs confines the process exit status the
same way. This is what makes the rule set testable without a disk, a Helm
binary, or a cluster — do not weaken it for convenience.
stricthelm is written in the strictrs strict subset and CI enforces it:
no unsafe, no panic APIs outside tests, no numeric as casts, no glob
imports, no mutable globals, explicit return types on public functions.
Rule rules
A new rule needs, in the same change:
- a stable
stricthelm::code, listed insrc/help.txt; - a positive test proving it fires;
- a negative test proving the nearest legitimate construct does not;
- a fixture entry when it belongs to the kitchen-sink chart;
- an entry in
README.md.
tests/help.rs enforces the manual in both directions: every code the crate
emits must be documented, and every code the manual lists must be emittable.
A rule cannot ship undocumented and a code cannot be documented into
existence.
Prefer a rule that is precise over one that is broad. A false positive costs an agent a wasted edit and teaches it to distrust the report; a false negative costs one missed defect. When a construct cannot be decided from the available evidence, do not report it.
Fix rules
Fixes are deliberately conservative:
- attach a fix only when the correct replacement is unambiguous — quoting a
scalar qualifies, choosing between
quoteandintdoes not; - retain and use byte offsets rather than reconstructing edits from columns;
- never edit a path outside the requested chart root;
- validate byte ranges and UTF-8 boundaries before modifying content;
- group edits by file and apply them back to front;
- deduplicate identical edits and keep the first of overlapping alternatives;
- re-run the full check after every pass;
- stop when clean, when no applicable edit remains, when a pass makes no progress, or at the iteration cap;
- keep the final stdout value the ordinary report, not a fix-result schema.
Generated-chart rules
stricthelm new output must:
- pass
stricthelm checkwith zero errors and zero warnings; - render deterministically, and be proven so by rendering twice in CI;
- declare exactly the values its templates read, and read exactly the values it declares;
- keep
values.schema.jsonin step withvalues.yamlin the same change; - install and pass
helm teston a kind cluster in CI; - be written through a staging directory and renamed only after every file succeeds;
- refuse invalid chart names and existing destinations;
- name every skipped stage in
scripts/check.shrather than passing quietly.
When adding a generated file, add it to FILES in src/template.rs; the
generator tests compare the produced tree against that list in both
directions.
Test rules
Every behaviour change needs a fixture or a focused test.
- Parsers over external text (Helm output, rendered manifests) are golden- tested against captured fixtures, so the contract is exercised without Helm.
- When a golden file changes, explain why the contract changed. Do not refresh a snapshot to make a test pass.
- Keep
tests/helm_live.rsthe only suite that needs a Helm binary, and keep its skip loud: CI setsSTRICTHELM_REQUIRE_HELM=1so a skip becomes a failure. - Preserve the kitchen-sink fixture proving many simultaneous defects across several files are all reported, not just the first.
- Preserve the compliant fixture proving the rules do not fire on a correct chart. A rule set that only has positive tests is a rule set nobody has measured for false positives.
- Property tests cover what cases cannot: the scanners must never panic on arbitrary input, ordering must be total, and a check must be a pure function of its input.
- CI enforces a line-coverage floor. Raise it when coverage improves; do not lower it to accommodate untested code.
Validation and commit discipline
Run the full set with Rust 1.97.1 before committing or pushing:
cargo fmt --check
cargo clippy --all-targets --all-features --locked -- -D warnings
cargo test --all-targets --all-features --locked
strictrs check .
With Helm installed, also run STRICTHELM_REQUIRE_HELM=1 cargo test and the
generated chart's own scripts/check.sh.
If no Helm release is reachable from your environment, build one — a Go
toolchain and a git clone are enough, and the result is a real binary that
runs the whole tests/helm_live.rs suite:
git clone --depth 1 --branch v3.21.4 https://github.com/helm/helm /tmp/helmsrc
(cd /tmp/helmsrc && go build -o /tmp/gobin/helm ./cmd/helm)
PATH="/tmp/gobin:$PATH" STRICTHELM_REQUIRE_HELM=1 cargo test
Do this rather than pushing a Helm-dependent change unverified and reading the result off CI. The Helm layers are where the assumptions are, and a guess about which layer catches a defect is exactly the kind of error that looks right until CI disagrees.
- Assemble a complete logical change before committing.
- Inspect the full diff and the staged file list before the commit.
- Do not commit or push known formatting, compilation, lint, or test failures.
- Do not create one commit per file, or one commit to discover a CI error.
- Batch related mechanical corrections and push them together after validating, rather than triggering a torrent of CI runs.
- Remove temporary renders, logs, and scratch files before the final push.
- Use CI to verify a validated change, not as a substitute for validation available locally.
Scope discipline
Keep diagnostic-contract, rule, fix, generated-chart, help, and installation changes separable enough to review directly. Avoid unrelated refactors.