docs(support-boundary): render fidelity + consent + reconcile-trigger + kpt (consolidated design docs)#237
docs(support-boundary): render fidelity + consent + reconcile-trigger + kpt (consolidated design docs)#237sunib wants to merge 4 commits into
Conversation
…strator's Design for the context-skew fence raised in #234's review: we run `kustomize build` on the folder, but Flux/Argo run kustomize plus context that is NOT in the folder — Flux postBuild substitute (build.go:631), Argo spec.source.kustomize overrides including images/replicas/patches/version (types.go:723), their kustomize version and flags. So the live object is not what our render produces, and sourceForm reads the difference as a user edit and corrupts the source; the oracle shares the blind spot. Records the failed approach and WHY, because it is the reason the right fence looks as it does: a structural "refuse any managed doc with ${...}" acceptance check is wrong. ${...} is ambiguous — literal and safe in CRD descriptions / KRO templates / nginx ConfigMaps (live == our render there), dangerous only when actually substituted (live != our render). The discriminator is not in the repo; it is in the live object. Measured: the structural check broke CRD mirroring outright (the Flux CRD documents postBuild with ${var:=default} in its own schema, poisoning the folder). Proposes the correct fence: measure our render against the live object and refuse where they diverge. Precise (same tokens, opposite verdicts, each read off live), per-object (no folder poisoning), on the write path (needs the live object, so not the structure-only acceptance gate). Two shapes: 5a the precise token instance (no false positives, build first) and 5b the general render-vs-live baseline (catches Argo overrides and version skew too, but must first solve the runtime-drift discriminator — managedFields is the lead). Two mermaid diagrams (the two render paths; the fence decision). Direction-setting; ships no code. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds a design document describing render fidelity between repository output and Flux/Argo-applied objects, failure modes in source-form projection, and proposed live-object write fences. Updates the support-boundary index formatting. ChangesRender fidelity design
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design/support-boundary/render-fidelity.md`:
- Around line 204-217: Clarify the “per field, per object” wording in the
section describing the write-path fence: a single diverged token triggers one
target-level WriteBoundaryRefused, aborts the entire flush, and commits no
writes. State that the per-field check identifies the refusal trigger only and
does not introduce partial-write behavior or change per-edit accounting.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 509e9b6f-c034-4e87-bffb-fff51cd6786c
📒 Files selected for processing (2)
docs/design/support-boundary/README.mddocs/design/support-boundary/render-fidelity.md
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
The render-vs-live measurement needs the live object, so it runs at reconcile — which rules out only the STRUCTURE-ONLY gate, not the operator, which holds live state on every reconcile. So the same measurement surfaces two ways: a per-write refusal (6a), and a standing GitTarget condition (6b, RenderFaithful) that answers 'do we even have a chance of tracking this folder?' up front, before an edit is wasted. It is the folder-level 'can we track this?' verdict the reverted structural check was reaching for and could not have — it failed answering from the disk; the same question answered from the live object is correct. Sibling of FullyReflected; open question added on whether it blocks adoption or only informs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…oaches
Records the decision: RenderFaithful is BLOCKING. A GitTarget is the claim that a
folder can be reverse-GitOps'd, which is void if our render is not equal to what
the cluster runs — so RenderFaithful=False gates adoption (not tracked, Ready=False),
strict first, loosen later if demand proves out.
Adds §7 Implementation — the design work on how/where to run it. One per-document
predicate (render has a ${...} token where live resolved it) computed once and read
by both surfaces (per-write refusal + folder condition). Three ways to run the
folder pass: (A) fold into the reconcile/resync that runs on acceptance — the moment
both git and live are in hand and nothing is written yet, added as a resync
precondition that aborts and sets the condition (recommended, and the shape the
user described); (B) a distinct live-aware acceptance layer (A with a cleaner seam);
(C) derive the folder verdict from accumulated per-write refusals (loses the
up-front property). We block on the TOKEN form (5a), not general render!=live (5b),
because blocking demands no runtime-drift false positive. Composes second to
GitPathAccepted; recomputable and self-healing.
Open questions renumbered §8; the block-vs-inform question is resolved.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…nd kpt notes here Fold the design docs from the design/consent-and-reconcile-trigger branch (PR #236) onto this PR so the support-boundary design work lives in one PR rather than several: admission-consent.md, orchestrator-reconcile-trigger.md, and the kpt-and-krm-functions orientation note, plus their README index rows. render-fidelity joins the "Renderers & provenance" row beside the other render docs. No content change to any doc; PR #236 is superseded and closed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lementation Bring the support-boundary design docs onto this PR — they are the design that sits next to this real implementation, so they belong together rather than in a separate PR: render-fidelity.md (our render is not the orchestrator's; the render-vs-live fence, the blocking RenderFaithful condition, and where to implement it), admission-consent.md, orchestrator-reconcile-trigger.md, and the kpt-and-krm-functions orientation note, plus their README index rows. Consolidated from PR #237, now closed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Consolidated into #234 — the design work now sits alongside the implementation it describes. Closing. |
…input (#234) * fix(kustomize): stop writing the build's own output into the build's input The writer mirrors a live object into the file that produced it. Under kustomize that file is not what the cluster runs, and mirroring the live object straight back writes the BUILD'S OUTPUT into the build's INPUT. Measured, on a folder we accept today, with nothing changed in the cluster and nothing changed in the render: a kustomization declaring `labels:` + `commonAnnotations:` and nothing else commits the overlay's `env: prod` and `owner: platform` into the base manifest, on the first reconcile of an unchanged folder. Every reconcile of an in-sync folder produced a commit, and the file was left wrong. In a base shared by two overlays, the value baked in is one environment's. The fix is one rule, and it models no transformer: WHERE THE LIVE OBJECT AND THE RENDER AGREE, THE SOURCE KEEPS ITS BYTES. WHERE THEY DISAGREE, THE USER CHANGED SOMETHING, AND THAT IS WHAT WE WRITE. Agreement means the build already produces exactly what the cluster runs, so the source is — by construction — what produced it, and there is nothing to write. Disagreement is the user's edit: it is routed to an images:/replicas: entry when the dye says one supplies the field, and written through to the source otherwise — where, if the build owns the field, the re-render refuses the flush. Because it needs no model of labels, of namespace, or of a patch, it closes all of them at once. It is also the gate on tolerating `patches:` at all: a patched base would otherwise absorb one environment's values, and no re-render can catch that (the patch re-imposes its value, so the render comes out identical). Two behavior changes go with it: - the re-render now runs for ANY document a render root produces, not only one an override chain governs. A change to a build-supplied field in a folder with no images:/replicas: entries used to be committed and silently never converge; it is now a reported refusal. - a live change the projection cannot place is refused (`unplaceable-edit`): the build and the user both rewrote one list whose elements carry no unique `name:` to pair them by. Pairing by position is not a conservative guess, it is measurably wrong — kustomize PREPENDS a container a patch adds. The corpus no-op invariant now compares the WHOLE document, not just its images, which is how a projection that quietly rewrote every field we had not modelled passed it for as long as it did: 8 documents checked before, 53 now. * docs(kustomize): address review on #234 -- finish the refusal sentence, fix the vars contradiction The SourceFormRefusedError message (surfaced to the user via WriteBoundaryRefused) and its IssueUnplaceableEdit doc comment both cut off mid-clause; complete them. And §2's vars bullet claimed the folder is accepted today while a parenthetical said it now refuses -- rewrite the passage in past tense (both leaks it cites are now closed, by #229 and by sourceForm). * docs(support-boundary): design work alongside the render-fidelity implementation Bring the support-boundary design docs onto this PR — they are the design that sits next to this real implementation, so they belong together rather than in a separate PR: render-fidelity.md (our render is not the orchestrator's; the render-vs-live fence, the blocking RenderFaithful condition, and where to implement it), admission-consent.md, orchestrator-reconcile-trigger.md, and the kpt-and-krm-functions orientation note, plus their README index rows. Consolidated from PR #237, now closed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(render-fidelity): rename the condition RenderFaithful -> RenderMatchesLive RenderMatchesLive names what it measures directly — does our render match the live object — better than the 'faithful' framing. The negative reason follows: RenderNotFaithful -> RenderDoesNotMatchLive, and the gate AcceptRenderFaithful -> AcceptRenderMatchesLive. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(render-fidelity): conform to the decisions, and add the implementation prompt Align the prose to the RenderMatchesLive framing (the "faithful/unfaithful" language follows the rename to "matches / diverges"), and mark §8's 5a-first as decided rather than open. Add next-prompt-render-matches-live-gate.md: a handoff for a fresh session to implement the token gate — the design, the code entry points (patchExisting + the resync path), the CRD lesson (measure render-vs-live, not the disk; the reverted structural check broke CRD mirroring), the test net, and the e2e-tail gotcha. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(render-fidelity): the predicate is render-vs-live, not git-vs-live Incorporate the review findings: - The predicate must compare the RENDER to live, not the Git source to live. kustomize does not RESOLVE ${...}, but it does not PRESERVE every token-bearing source field either: a supported labels/commonLabels transform overwrites metadata.labels[...] via SetEntry, so `env: ${ENV}` under `labels: {env: prod}` renders to `prod` == live. Git-vs-live would falsely refuse that faithful folder. The render is dm.Rendered.Object for a kustomize doc, the Git doc for a plain one, and reading the render also catches a token a patches: block injects. Fixed the prompt (which had the git-vs-live shortcut + "does not need dm.Rendered") and §5a. - Narrow the causal claim in the fact doc and the design: a rendered token that differs from live proves only that our render did not produce the value — could be Flux substitution, a live edit, admission, another controller. Refusal is safe regardless; RenderDoesNotMatchLive is a fact, "must have been substituted" a guess. - State the two integration requirements for the blocking gate: aggregate fidelity across ALL scoped resyncs (a last-successful-GVR status masks a diverging type), and stop opening write windows while failed (status-only refusal isn't a gate). - Record the bias: blocking a shade too soon is fine; failing to block is not. The simple token gate is the right first cut. Also fixes the fact doc's external-sources markdown links (doccheck treats upstream checkouts as untracked) to code spans, matching the convention in the sibling docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs: define render fidelity gate * feat: gate writes on render fidelity * docs: clarify reconcile trigger barrier * fix: getting the bidirectional e2e test to work again * test: last extra checks to see if all behaves as expected * docs: last edits * docs: feedback of codex --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Consolidated support-boundary design docs — direction-setting, ships no code. Combines what were PRs #236 and #237 into one, since the design work belongs together and there were too many open PRs. Indexed in the folder README.
The docs
kustomize build; Flux/Argo run kustomize plus context that isn't in the folder (Flux postBuild, Argospec.source.kustomizeoverrides, their version). Records the failed structural${...}check and why (it broke CRD mirroring —${}is ambiguous, the discriminator isn't in the repo), and proposes the right fence: measure our render against the live object, refuse where they diverge. Two surfaces — a per-write refusal and a blockingRenderFaithfulGitTarget condition you read before you edit — and a §7 on how/where to implement it (fold into the reconcile-on-acceptance).WriteIntent(never a bypass), a consequence-hash token, the RBAC edge.selfHeal: false), and an origin-drift ordering barrier. Diagrams built frombi-directional.md.Was #236 (consent + reconcile-trigger + kpt) and #237 (render-fidelity); #236 is closed in favour of this one.
🤖 Generated with Claude Code