Skip to content

feat(overlay): prove new-object placement; author images/replicas overrides and $patch:delete#244

Merged
sunib merged 4 commits into
mainfrom
feat/overlay-new-object-placement
Jul 15, 2026
Merged

feat(overlay): prove new-object placement; author images/replicas overrides and $patch:delete#244
sunib merged 4 commits into
mainfrom
feat/overlay-new-object-placement

Conversation

@sunib

@sunib sunib commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Overlay write-path work on the render-root-scoping boundary. Three commits, each verified by the existing re-render oracle (a wrong proposal is refused, never committed) and by writer-integration tests.

1. Prove new-object placement (no production code)

§4 listed "new object → overlay file + resources: entry" as Planned, but #239 already wired it. TestPlacement_ExternalBaseOverlay_NewObject proves it end-to-end (file lands in the overlay, overlay's own kustomization gains the entry, base untouched, oracle accepts). Stale docs corrected.

2. Author a missing images:/replicas: entry — "edit a specific environment, get the override"

Changing a base-supplied image or replica count in one overlay used to try to write the read-only base and was refused. Now the projection, on a diverging component whose supplier is the source document and whose source is out of the write jail, emits a Create OverrideEdit into the overlay's own kustomization. New primitive manifestedit.AppendKustomizationOverride creates the entry (and the images:/replicas: section if absent); the base source form stays byte-identical so nothing is written to the base. Idempotent on resync.

  • TestOverlayAuthors_ImageEntry_ForBaseSuppliedImage, ..._ReplicaEntry_ForBaseSuppliedCount, ..._Idempotent_OnResync

3. $patch: delete for an inherited object

Deleting an object the overlay inherits from its base used to try to delete the base doc (refused). Now applyDelete authors a $patch: delete document under the overlay + a patches: entry (new primitive AppendKustomizationPatch), keeps the Removed intent so the oracle proves the object leaves the render, and never touches the base. The patch pins apiVersion/kind/namespace/name — a non-matching patch is refused by the oracle (proven: the namespace-less first cut was refused, the namespaced one commits).

  • TestOverlayAuthors_DeletePatch_ForInheritedObject

Note: $patch: delete is a deliberate, narrow widening of the patch-authoring boundary for the delete slice only (identified by name, no field attribution). Strategic-merge patch authoring for a base-owned field remains planned. Recorded in patch-authoring.md, render-root-scoping.md, support-contract.md.

Validation

  • task lint — doccheck OK, golangci-lint 0 issues
  • task test — all unit + writer-integration tests pass; coverage 76.3% → 76.5% (baseline bumped)
  • task test-e2e — running (both features touch the write path)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • External-base overlays can now create overlay-local resources and author image or replica overrides.
    • Overlays can delete inherited resources using supported delete patches.
    • Changes are routed to overlay files while keeping the base unchanged.
    • Repeated synchronization avoids duplicate entries and preserves existing content.
  • Documentation

    • Updated support-boundary and compatibility guidance to reflect these shipped capabilities.
  • Tests

    • Added coverage for resource creation, overrides, deletion patches, idempotency, and validation.

…ay; fix stale docs

render-root-scoping.md §4 labelled "new object -> overlay-local file plus
resources: entry" as Planned, but #239's render-root scoping already wired it:
WriteScope threads the overlay's write jail into placement, the base
kustomization is skipped as out-of-jail (writableCohort / resolveKustomizeRoot),
appendKustomizationResource adds the resources: entry in the same commit, and
the render oracle verifies the result. It simply had no test proving it and the
docs still read "Planned".

Add TestPlacement_ExternalBaseOverlay_NewObject: a GitTarget rooted at an overlay
that reads ../../base gains a new ConfigMap. It asserts the file lands inside the
overlay, the overlay's OWN kustomization gains the resources: entry (never the
base's), the read-only base is untouched byte-for-byte, and the render oracle
accepts the flush.

Correct the stale docs (render-root-scoping §1/§4/§5/top, README, support-contract,
kustomize-support-boundary) to mark new-object creation shipped. What remains for an
overlay: adding a MISSING images:/replicas: declaration (no source value to attribute
yet) and base-owned-field patch authoring.

No production code changed — this proves and documents existing behaviour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@sunib, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 981a79af-2c38-4f98-8411-7d7e3bfedf31

📥 Commits

Reviewing files that changed from the base of the PR and between 5ab5e53 and b51a33e.

📒 Files selected for processing (5)
  • docs/design/support-boundary/README.md
  • docs/design/support-boundary/kustomize-support-boundary.md
  • docs/design/support-boundary/render-root-scoping.md
  • internal/git/placement_test.go
  • internal/git/plan_flush.go
📝 Walkthrough

Walkthrough

External-base overlays now write overlay-local resources, images: and replicas: overrides, and $patch: delete documents. Projection, kustomization editing, flush routing, integration tests, and support-boundary documentation were updated accordingly.

Changes

External-base overlay authoring

Layer / File(s) Summary
Override projection and authoring contracts
internal/manifestanalyzer/overrides_projection.go, internal/manifestanalyzer/*_test.go
Projection now marks missing image and replica overrides for creation in an overlay kustomization.
Kustomization override and patch editing
internal/git/manifestedit/kustomization.go, internal/git/manifestedit/kustomization_test.go
Kustomization mutation adds idempotent images: and replicas: entries and supports patch-path registration with YAML type preservation.
Overlay routing and inherited deletion
internal/git/plan_flush.go
Flush routing targets overlay-owned kustomizations, separates created overrides from existing edits, and authors deterministic inherited-object delete patches.
Validation and support boundary
internal/git/placement_test.go, docs/design/support-boundary/*, .coverage-baseline
Tests verify overlay-local object placement, image/replica authoring, idempotent resync, and inherited deletion; support documentation records these behaviors as shipped.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant LiveEvent
  participant BranchWorker
  participant OverrideProjection
  participant KustomizationEditor
  participant Overlay
  LiveEvent->>BranchWorker: Submit object update or delete
  BranchWorker->>OverrideProjection: Project change for overlay authoring
  OverrideProjection->>KustomizationEditor: Create override edit
  BranchWorker->>KustomizationEditor: Append override or delete patch
  KustomizationEditor->>Overlay: Write overlay-local kustomization artifacts
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 68.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ⚠️ Warning The description is narrative but does not follow the required template sections or checklists. Rewrite it using the template headings: Description, Type of Change, Testing, Checklist, Related Issues, Screenshots, and Additional Notes.
✅ Passed checks (3 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.
Title check ✅ Passed The title clearly summarizes the overlay write-path changes for new objects, override authoring, and delete patches.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/overlay-new-object-placement

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.

@codecov

codecov Bot commented Jul 15, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 72.08122% with 55 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/manifestanalyzer/overrides_projection.go 34.8% 26 Missing and 2 partials ⚠️
internal/git/manifestedit/kustomization.go 71.2% 12 Missing and 11 partials ⚠️
internal/git/plan_flush.go 94.5% 4 Missing ⚠️

📢 Thoughts on this report? Let us know!

sunib and others added 2 commits July 15, 2026 19:59
…alue is changed per-environment

An external-base overlay could edit an EXISTING images:/replicas: entry, but when the
base itself supplied an image or replica count, changing it in one environment tried to
write the read-only base and was refused ("planned write path base/deployment.yaml
escapes the GitTarget write scope"). So "edit the image in dev" did not work unless the
overlay already declared an entry.

Author the entry instead. When the projection finds a diverging image component or replica
count whose supplier is the SOURCE document and that source is out of the write jail (a base
an overlay reads), it emits a Create OverrideEdit into the overlay's own kustomization rather
than writing the base. The writer applies it with a new
manifestedit.AppendKustomizationOverride primitive (creating the images:/replicas: section if
the overlay has none), and the base source form stays unchanged so nothing is written to the
base.

Every authored entry is put to the existing re-render oracle before it can commit, so a
proposal that over-reaches (an images: name shared by another object) is refused there, not
written. Idempotent on resync: once the entry exists the store sees it and the change routes
to it, no duplicate.

Tests: manifestedit primitive unit tests; overrides projection call-site updates; and writer
integration TestOverlayAuthors_{ImageEntry,ReplicaEntry,Idempotent}. Docs updated
(render-root-scoping, support-contract, README, kustomize-support-boundary).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Deleting an object an external-base overlay inherits from its base tried to delete the base
document (out of the write jail, shared by other environments) and was refused. Author a
`$patch: delete` in the overlay instead: when the delete target is out of the write jail and
the overlay has a supported render root, applyDelete writes a small `$patch: delete` document
inside spec.path, names it in the overlay's own patches: (new manifestedit.AppendKustomizationPatch
primitive, creating the patches: section if absent), and keeps the existing Removed intent so the
re-render oracle proves the object leaves the render. The base is never touched.

The patch pins the object by apiVersion/kind/namespace/name; a patch that does not match makes
kustomize fail to build and the oracle refuses the flush (proven: the namespace-less first cut
was correctly refused, the namespaced one commits). It needs no field attribution, so it does not
wait on strategic-merge field-patch authoring — which stays refused.

This is a deliberate, narrow widening of the patch-authoring boundary for the delete slice only;
noted in patch-authoring.md and the render-root-scoping/support-contract records.

Tests: AppendKustomizationPatch unit tests; integration TestOverlayAuthors_DeletePatch_ForInheritedObject.
Coverage baseline bumped 76.4% -> 76.5%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sunib sunib changed the title test(overlay): prove new-object placement into an external-base overlay works; fix stale docs feat(overlay): prove new-object placement; author images/replicas overrides and $patch:delete Jul 15, 2026

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🤖 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/README.md`:
- Around line 42-50: The patch-authoring documentation must distinguish
read-only field patches from the shipped deletion exception. Update the table
row in docs/design/support-boundary/README.md (lines 42-50) and the
taxonomy/accepted-layout description in
docs/design/support-boundary/kustomize-support-boundary.md (lines 36-41) to
state that field patches remain read-only while $patch: delete may be authored.

In `@internal/git/plan_flush.go`:
- Around line 1170-1175: Update the inherited-delete patch flow around
inheritedDeletePatchName and wb.buffer(patchPath) so an existing patch-path
collision is detected before assignment. Reuse the existing content when it
matches the generated patch, otherwise choose a conflict-free patch path or
return a clear error; never overwrite existing content, and propagate Git or
manifest errors gracefully.
🪄 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: b614dc3e-6910-4bd0-8832-3d6b3a98c776

📥 Commits

Reviewing files that changed from the base of the PR and between 49327fd and 5ab5e53.

📒 Files selected for processing (14)
  • .coverage-baseline
  • docs/design/support-boundary/README.md
  • docs/design/support-boundary/kustomize-support-boundary.md
  • docs/design/support-boundary/patch-authoring.md
  • docs/design/support-boundary/render-root-scoping.md
  • docs/design/support-boundary/support-contract.md
  • internal/git/manifestedit/kustomization.go
  • internal/git/manifestedit/kustomization_test.go
  • internal/git/placement_test.go
  • internal/git/plan_flush.go
  • internal/manifestanalyzer/kustomize_render_test.go
  • internal/manifestanalyzer/overrides_projection.go
  • internal/manifestanalyzer/overrides_projection_test.go
  • internal/manifestanalyzer/source_form_test.go

Comment thread docs/design/support-boundary/README.md Outdated
Comment thread internal/git/plan_flush.go
…ress CodeRabbit

- Resolve README / render-root-scoping conflicts between #243 (scan-repo adopts
  external-base overlays) and this branch (overlay write-path): keep both facts —
  scan-repo reports kustomize-overlay accepted, and the overlay authors new objects,
  images/replicas entries, and $patch: delete.
- CodeRabbit #1: distinguish read-only field patches from the authored $patch: delete
  in README and kustomize-support-boundary.
- CodeRabbit #2: authorInheritedDelete never overwrites an unrelated file at the
  deterministic patch path — it skips the delete on a content collision (idempotent
  resync still proceeds). Covered by TestOverlayAuthors_DeletePatch_SkipsOnPathCollision.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sunib
sunib merged commit 38590d1 into main Jul 15, 2026
18 checks passed
@sunib
sunib deleted the feat/overlay-new-object-placement branch July 15, 2026 21:24
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