Skip to content

feat: add promise check command for structural validation - #260

Open
shano wants to merge 20 commits into
mainfrom
promise-check-level1-gates
Open

feat: add promise check command for structural validation#260
shano wants to merge 20 commits into
mainfrom
promise-check-level1-gates

Conversation

@shano

@shano shano commented Aug 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Stacked on #259 (adds promise check) — extends it with the Level-1 deterministic gates from ADR0010: checks the generated Promise/Resource Request files directly, no model involved.

New optional flags on kratix promise check:

  • --promise-dir (default promises) — for each Promise file: valid CRD (spec.api is a real CustomResourceDefinition), scope: Namespaced (Kratix only supports this), every workflow pipeline container has an image set, and the delete workflow has at most one pipeline (Kratix only supports one).
  • --example-dir (default resource-requests) — matches each example/Resource Request to its Promise's CRD by apiVersion+kind, then validates required fields are present, no unknown fields, and any declared pattern matches.

Both flags are optional and backward compatible — if the directories don't exist (the common case for existing --review-file-only usage), that part of the gate is silently skipped, same behavior as #259 today.

Ran the built binary against a real generated output (ske-lift's fresh-baseline-2 scenario, 10 Promises / 11 Resource Requests) — passes clean. Also verified negatively: deliberately broke one Promise's scope to Cluster in a scratch copy and confirmed the gate catches it correctly.

Test plan

  • go test ./cmd/... — all existing + 9 new tests pass.
  • gofmt -l clean, go vet ./... clean.
  • Real end-to-end run against ske-lift's fresh-baseline-2 output — exit 0.
  • Negative test — corrupted scope: Cluster, confirmed gate fails with the right message.
  • Backward-compat check — --review-file only, no promise/example dirs present, exit 0 same as before.

shano added 3 commits August 14, 2026 10:23
kratix promise check --review-file <path> validates a review-findings
YAML/JSON artifact and exits non-zero if any unresolved critical or high
finding remains. Self-registers under a new `promise` parent command via
init(), no changes to root.go. No new dependencies (cobra, yaml.v3 already
present).

Built and tested against infrastructure-discovery tooling that needed a
hard pass/fail gate rather than a prose "review carefully" instruction
before a generated Promise gets applied.
@jeduardo

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6a91da963a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread cmd/promise_check_level1.go Outdated
Comment on lines +95 to +96
if crd.APIVersion != "apiextensions.k8s.io/v1" || crd.Kind != "CustomResourceDefinition" {
errs = append(errs, fmt.Sprintf("%s: spec.api is not a valid CustomResourceDefinition (apiVersion=%q kind=%q)", name, crd.APIVersion, crd.Kind))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate all required CRD structure

When spec.api has the expected apiVersion/kind, Namespaced scope, and any versions entry, this gate accepts it even if required CRD fields such as metadata.name, spec.group, names.plural, or version served/storage flags are absent. Such a Promise cannot be installed, so the advertised valid-CRD gate can return success for a structurally invalid generated artifact; decode and validate the complete Kubernetes CRD rather than treating these few fields as sufficient.

Useful? React with 👍 / 👎.

Comment thread cmd/promise_check_level1.go Outdated
Comment on lines +105 to +106
errs = append(errs, checkPipelines(name, "configure", doc.Spec.Workflows.Resource.Configure)...)
errs = append(errs, checkPipelines(name, "delete", doc.Spec.Workflows.Resource.Delete)...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Check images in promise workflows too

Only spec.workflows.resource.configure and .delete are inspected, so a container without an image under spec.workflows.promise.configure passes the gate. Promise-level configure pipelines are present in generated outputs such as the Terraform fixtures, and an empty image there still produces a broken workflow despite the command promising to check pipeline images; represent and inspect the promise workflow pipelines as well.

Useful? React with 👍 / 👎.

Comment thread cmd/promise_check_level1.go Outdated
Comment on lines +174 to +177
if fieldSchema.Pattern != "" {
str, isString := value.(string)
if !isString {
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Apply the complete schema when validating examples

When a property has no pattern, any value is accepted regardless of its declared type; even with a pattern, a non-string value explicitly skips validation here. For example, a CRD property declared as integer accepts a YAML string, and nested required fields are never visited, allowing promise check to succeed for requests that Kubernetes rejects. Use a complete OpenAPI schema validator, including recursive and type validation, instead of limiting validation to top-level presence and regex checks.

Useful? React with 👍 / 👎.

Comment thread cmd/promise_check_level1.go Outdated
return []string{fmt.Sprintf("%s: does not parse as YAML: %v", name, err)}
}

for promiseName, doc := range promises {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject duplicate GVKs before matching examples

When two loaded Promises declare the same group/version/kind but different schemas, this map iteration selects an arbitrary Promise and immediately returns its validation result. The same example can therefore pass or fail across runs depending on Go's map order; detect the duplicate GVK as a gate failure or build a deterministic unique-GVK index before validating examples.

AGENTS.md reference: AGENTS.md:L13-L13

Useful? React with 👍 / 👎.

Comment thread cmd/promise_check_level1.go Outdated
} `yaml:"spec"`
}

type promiseCRD struct {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't we reuse the kratix definitions for these kratix CRDs?

Comment thread cmd/promise_check_level1.go Outdated
// exampleDir and runs all Level-1 checks. Either directory may be absent
// (returns no errors, gate is skipped) so this stays backward compatible
// with a review-file-only check.
func runLevel1Gates(promiseDir, exampleDir string, out io.Writer) []string {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be one instead of 1

shano added 5 commits August 18, 2026 11:44
Decode Promises with v1alpha1.Promise and CRDs with the real
apiextensionsv1.CustomResourceDefinition instead of hand-rolled
promiseDoc/promiseCRD structs, and validate CRDs with the same
apiextensions-apiserver validator Kubernetes itself uses. This
catches incomplete CRDs (missing names.plural, no storage version,
etc.) that the old field-by-field check let through.
The image gate only inspected spec.workflows.resource.configure/.delete,
so a missing image under spec.workflows.promise.configure (seen in real
Terraform-fixture output) silently passed. Switch to Kratix's own
NewPipelinesMap, which covers all four workflow slots, and also flag a
pipeline declared with zero containers.
The old check only looked at top-level presence and a regex pattern:
a property with no pattern accepted any value regardless of declared
type, a non-string value with a pattern skipped validation entirely,
and nested required fields were never recursed into. Replace it with
apiextensions-apiserver's real CRD schema validator, validating the
whole example document against the whole schema so root-level rules
and unserved CRD versions are caught too.
Two Promises sharing a group/version/kind used to be resolved by Go's
undefined map iteration order, so an example could pass or fail
depending on the run. buildGVKIndex now walks Promise files in sorted
order and reports a duplicate GVK as a gate failure instead of picking
one arbitrarily.
Trivial wording fix from review: "one", not "1".
@shano

shano commented Aug 18, 2026

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

shano added 8 commits August 18, 2026 14:25
The review-findings/AI-review concept has been ported to a standalone
kratix-review plugin. kratix-cli core keeps only the Level-1
structural checks against the Promise's CRD, workflows, and examples.
Rename runLevelOneGates to runStructuralChecks and reword help text
and comments to describe checks in plain terms, since "Level-1 gate"
is another project's AI-grading vocabulary, not kratix-cli's.
@shano
shano changed the base branch from promise-review-check-gate to main August 18, 2026 15:17
@shano shano changed the title feat: add Level-1 structural gates to promise check feat: add promise check command for structural validation Aug 18, 2026
Comment thread cmd/promise_check.go
Comment on lines +45 to +49
Long: "Validate the generated Promise/example files in --promise-dir and --example-dir: the CRD is a " +
"valid, Namespaced-scope CustomResourceDefinition, every workflow pipeline container has an image " +
"set, the delete workflow is well-formed, and every example/Resource Request validates against its " +
"Promise's CRD schema (including CEL rules and schema defaults). Either directory may be absent, in " +
"which case its checks are skipped.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The long comment is maybe too long? I think it could finish at ...example-dir.

Comment thread cmd/promise_check.go
// gvkPromise pairs a group/version/kind with the name of the Promise file
// that declared it and its CRD, so checkExampleFile can validate against a
// single, deterministically-chosen Promise (see buildGVKIndex).
type gvkPromise struct {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any way to pull this from the Kratix code through a library call?

govulncheck now finds several new stdlib vulnerabilities (fixed in
Go 1.25.13/1.26.6) alongside the previously-allowlisted, unfixable
ones (x/crypto/openpgp, containerd v1, crossplane), causing the
security-scan job to fail again. Pinning the toolchain closes the
stdlib gap; the remaining findings are still covered by the existing
KNOWN_NA allowlist in tests.yaml.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
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.

4 participants