Skip to content

feat(config): lint errors on reserved-schema usage#564

Merged
joshua-temple merged 1 commit into
mainfrom
feat/lint-reserved-fields
Jul 11, 2026
Merged

feat(config): lint errors on reserved-schema usage#564
joshua-temple merged 1 commit into
mainfrom
feat/lint-reserved-fields

Conversation

@joshua-temple

Copy link
Copy Markdown
Collaborator

Problem

cascade lint (step 1) rejects unknown top-level keys, but a manifest that
populates a field which parses but is not wired to generation still lints
clean and generates an inert result. Those reserved fields should be a hard
error, not a silent no-op.

Fix

A hand-verified reserved-field registry (internal/config/reserved.go), wired
into Validate() so it errors through cascade lint and the generate path.
It mirrors the globalOnlyComponentFields pattern and maps each field path to a
category with a distinct message:

  • not-yet-wired: ... is reserved and not implemented in this cascade version
  • wrong-place (callback timeout_minutes): ... timeout belongs in the called workflow, not the caller

The walk covers the top level, every component, and external repos.

Consumed-vs-reserved evidence (verified against internal/generate)

Field Verdict Evidence
telemetry reserved no reference under internal/generate
deploys[].rollout.type / .canary / .blue_green reserved not read by writeDeployStrategyOptions; no generate reference
deploys[].deploy_target reserved no generate reference
release.version_overrides reserved no generate reference
callback timeout_minutes reserved (wrong-place) populated into graph nodes (graph.go) but never emitted
rollout.max_parallel / rollout.fail_fast consumed emitted at promote.go:797-806, generator.go:1100-1104
job_timeout_minutes consumed emitted at generator.go:216
target_sha out of scope lives on EnvState (component state), not the manifest surface, so it never reaches manifest lint

Migration

Reserved usage now errors, so in-repo fixtures that populated reserved fields
were updated: the four reserved-shape e2e scenarios (their premise, that a
reserved field generates byte-identically, no longer holds) are removed and
replaced by 59-lint-reserved-rejection.yaml, which proves the merge-queue lint
lane reds on a reserved field. The reserved structural unit tests now assert the
rejection while retaining their direct structural-validator coverage. The
reference/manifest.md and reference/versioning.md reserved-field docs note
that cascade lint rejects their use.

Verification

go build ./..., go test ./..., go test -race ./internal/config/... ./internal/generate/...,
and golangci-lint run ./... all clean (root and e2e module). New reserved
tests fail before the registry and pass after.

Reject any manifest that populates a parses-but-inert reserved field. A
hand-verified registry (verified field-by-field against internal/generate,
since the schema comments are unreliable) maps each field path to a category:
not-yet-wired (telemetry, rollout.type/canary/blue_green, deploy_target,
release.version_overrides) errors as not implemented in this version;
callback timeout_minutes errors as belonging in the called workflow. The
consumed knobs rollout.max_parallel/fail_fast and job_timeout_minutes stay
valid. Walks components and external repos too. Migrates in-repo fixtures and
docs, and adds an e2e scenario proving the merge-queue lint lane reds on a
reserved field.

Signed-off-by: Joshua Temple <joshua.temple@stablekernel.com>
@joshua-temple joshua-temple merged commit ac6d0a5 into main Jul 11, 2026
20 checks passed
@joshua-temple joshua-temple deleted the feat/lint-reserved-fields branch July 11, 2026 22:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant