Skip to content

fix(reconcile): reject unknown top-level fields in desired-state documents - #1888

Merged
rohilsurana merged 2 commits into
mainfrom
fix/reconcile-framework-known-fields
Aug 19, 2026
Merged

fix(reconcile): reject unknown top-level fields in desired-state documents#1888
rohilsurana merged 2 commits into
mainfrom
fix/reconcile-framework-known-fields

Conversation

@rohilsurana

@rohilsurana rohilsurana commented Aug 17, 2026

Copy link
Copy Markdown
Member

What

The reconcile document decoder ignored unknown top-level keys. A stray key (a typo of spec, a misplaced metadata, a misspelled apiVersion that silently falls back to v1) was dropped instead of failing the file.

This adds KnownFields(true) to the document decoder, so an unknown top-level key fails the whole file up front, the same way entry decoding already rejects unknown fields. The spec's own content is decoded separately, so only the outer apiVersion/kind/spec envelope is guarded.

Why

RFC 0001 Rule 3 ("validate before apply") says the whole file is checked first, including that unknown fields are rejected. The document level did not enforce this, so a typo could pass validation and hide a mistake.

Testing

  • New unit test: a document with an unknown top-level field is rejected before any reconciler runs.
  • Full internal/reconcile package passes.

Stack

Base of a four-PR stack fixing reconcile-kind findings from the RFC 0001 audit:

  1. fix/reconcile-framework-known-fields (this PR)
  2. fix/reconcile-permission-spicedb-grammar
  3. fix/reconcile-billingproduct-r2
  4. docs/reconcile-platformuser-disabled-out-of-scope

Note on casing

Top-level keys are now case-sensitive. apiversion: or KIND: fail instead of silently defaulting. That is the intended typo-catch, and export always writes the correct casing, so exported files are unaffected.

@vercel

vercel Bot commented Aug 17, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
frontier Ready Ready Preview Aug 18, 2026 6:16am

@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes

    • YAML documents now reject unknown top-level fields, including misspelled version fields and unexpected metadata.
    • Invalid documents are reported with a decoding error, and reconciliation does not proceed when they are detected.
  • Tests

    • Added coverage for invalid fields in both the first and subsequent documents.

Walkthrough

parseDocuments now rejects unknown top-level YAML fields. Tests verify rejection in the first and later documents, with no reconciliation dispatch.

Changes

Strict envelope validation

Layer / File(s) Summary
Top-level field validation
internal/reconcile/reconcile.go, internal/reconcile/reconcile_test.go
parseDocuments enables strict validation for top-level YAML fields. Tests verify errors for unknown fields in the first and later documents and confirm that reconciliation does not run.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 9ef21

The decoder now rejects unknown or mis-cased top-level fields before reconciliation, preventing typoed documents from being silently accepted. A trivial casing regression test remains a follow-up, but no actionable merge-blocking risk remains.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coveralls

coveralls commented Aug 17, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 32106205618

Coverage increased (+0.01%) to 48.747%

Details

  • Coverage increased (+0.01%) from the base build.
  • Patch coverage: 5 of 5 lines across 1 file are fully covered (100%).
  • No coverage regressions found.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

No coverage regressions found.


Coverage Stats

Coverage Status
Relevant Lines: 40093
Covered Lines: 19544
Line Coverage: 48.75%
Coverage Strength: 15.67 hits per line

💛 - Coveralls

@rohilsurana

Copy link
Copy Markdown
Member Author

Review: reject unknown top-level fields (RFC 0001 Rule 3)

Verified on this branch: go build ./..., go test ./internal/reconcile/..., and golangci-lint run ./internal/reconcile/... all pass.

This does what it claims. dec.KnownFields(true) is set on the shared decoder in parseDocuments (reconcile.go:67) and the decoder is reused across the document loop, so every document's envelope is checked, before the validate and reconcile loops and any server call. Spec-level unknown fields were already caught by decodeSpec, so Rule 3 now holds at both levels. Export emits only apiVersion/kind/spec, so an exported file never trips the new check (Rules 1 and 5 stay intact).

Verdict: approve with nits.

Nits:

  1. The rejection error leaks the internal Go type name to operators. kind: PlatformUser\nspec: []\nspce: oops yields ...field spce not found in type reconcile.document. It names the field and line, which is good, but reconcile.document is an implementation detail. Consider wrapping to drop the type name.
  2. reconcile_test.go only covers a single-document file. The whole point is "check the whole file," so add a multi-document case where the stray key is in the second document, to lock in that the check fires on every document, not just the first.
  3. Not a bug, worth a line in the PR body: top-level keys are now case-sensitive, so apiversion: or KIND: now fail instead of silently defaulting. That is the intended typo-catch, and export always writes correct casing.

@rohilsurana

Copy link
Copy Markdown
Member Author

Follow-up on the review nits.

  • Multi-document test: done. Added a subtest where the stray key sits in the second document, so the check is locked to fire on every document, not just the first.
  • Case-sensitivity: added a note to the PR description. It is the intended typo-catch, and export always writes correct casing.
  • Type name in the error: keeping it. The useful signal is the field name and the line, which are both there. Stripping the trailing in type reconcile.document means string-surgery on the yaml library's error text, which is brittle. The type name is stable and harmless, so it is not worth that.

@rohilsurana

Copy link
Copy Markdown
Member Author

Re-review (head 3e21c89)

Build, tests, and lint are all green on the branch. The multi-document test an unknown field in a later document is rejected correctly locks in the every-document property: it puts the stray key in the second document and asserts a parse-time failure with no reconciler dispatched (rec.called == 0). Rule 3 and the export round-trip both still hold.

One nit still open, cosmetic: the error still surfaces the internal Go type name, ...field spce not found in type reconcile.document. Not a blocker.

Verdict: approve with nits.

Comment thread internal/reconcile/reconcile.go
@rohilsurana
rohilsurana force-pushed the fix/reconcile-framework-known-fields branch from 3e21c89 to 9ef21ae Compare August 18, 2026 06:16
@rohilsurana
rohilsurana marked this pull request as ready for review August 18, 2026 06:55

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
internal/reconcile/reconcile_test.go (1)

116-125: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a case-sensitivity regression test.

The current test rejects spce, but it does not prove that known keys with different casing, such as Kind or Spec, are rejected. Add one case and assert that reconciliation is not dispatched.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 87f33dce-a6d7-4f4d-8719-2566124961a9

📥 Commits

Reviewing files that changed from the base of the PR and between 2b125a7 and 9ef21ae.

📒 Files selected for processing (2)
  • internal/reconcile/reconcile.go
  • internal/reconcile/reconcile_test.go

Included review availability: Your plan includes up to 2 reviews per rolling hour; 1 remains after this review.

@rohilsurana
rohilsurana merged commit eca7ba0 into main Aug 19, 2026
8 checks passed
@rohilsurana
rohilsurana deleted the fix/reconcile-framework-known-fields branch August 19, 2026 09:17
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.

3 participants