Skip to content

feat(client)!: native editing through the host proxy (slice 2) - #61

Merged
sunib merged 7 commits into
feat/native-watch-viewerfrom
feat/native-editing
Oct 5, 2026
Merged

sunib merged 7 commits into
feat/native-watch-viewerfrom
feat/native-editing

Conversation

@sunib

@sunib sunib commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

Slice 2 of the native work: editing objects read with connectNativeWatch, written back through the same host proxy. Stacked on #60 (feat/native-watch-viewer); retarget to main once #59 and #60 merge.

What it adds

  • Machinery is read-only in the store under every policy. metadata.managedFields and the kubectl.kubernetes.io/last-applied-configuration annotation are protected the way redacted paths are. These are the same two paths ValidateMergePatch already refuses for every projection. No new policy or store is needed: new LiveResourceStore() edits native objects.
  • Maps that hold a protected path are merged and edited key by key, not replaced whole. This also fixes an existing bug: a new Secret data key added beside redacted values was accepted but never reached changes() or patch().
  • nativeObjectURL(proxy, scope) addresses one member under the same proxy and collection the watch reads.
  • gateway.ValidateNativeMergePatch(patch) is for a host proxy to run before forwarding a native PATCH. It refuses a patch without both metadata.uid and metadata.resourceVersion, or one that touches machinery, including through a parent such as annotations: null.
  • examples/native-editor/editor.ts is a copyable conditional editor:
    • It sends the captured intent as application/merge-patch+json with the preconditions inside the patch.
    • On a 409, or a 422 on metadata.uid, it reconciles a guarded native GET of the same object.
    • It never adopts the write response, and it refuses a projected envelope or a different UID.
    • Its outcomes match the projected conditional editor's.
  • Docs:
    • saving.md has a native editing section comparing the projected and native write contracts.
    • The state model, the READMEs and the guides that called native access read-only are updated.
    • migrating.md lists the edit-policy change.
    • Proposal 0006 and the request mark slice 2 complete and record its acceptance evidence.

No editor method changed, so proposal 0009's cleanup is unaffected. Gateway v1 wire and protocol version are unchanged.

Breaking change

setValue and removeKey on metadata.annotations as a whole are refused; edit each annotation key instead. isEditable(uid, ["metadata", "annotations"]) is now false, while isDirty on it still reports edits underneath.

Validation

Check Result
task fixtures-check Passed, no changes
task test Passed: 213 client tests (12 new), 2 Vue tests, and the Go gateway, kube and example suites
task lint Passed (one pre-existing biome schema-version note)
task build-client / task pack-client Passed; nativeObjectURL is in the bundle and the declarations
task e2e-wire Passed
task e2e-browser 24/24 passed
task test-real-api 8/8 passed on k3s v1.36.4+k3s1, including the new TestRealAPINativeEditThroughHostProxy

The new real-API case drives the real connector and editor through a credential-holding /k8s proxy that validates each PATCH. It checks:

  • a save and its watch echo, with the last-applied annotation untouched;
  • a competing write landed between capture and PATCH: 409, guarded read, then a second deliberate save, with neither write lost;
  • the proxy refusing an unconditional patch and a machinery patch before either reaches the API server;
  • an object replaced under the same name, reported unavailable with the replacement untouched.

I repeated it 8 times with -race; it passed every time, recovering with version-stale then saved.

Not run: task release-rehearsal, task test-cluster and task cluster-facts, which this change doesn't need. There is no browser page for native editing yet; the comparative frontend (order 6) is where one belongs.

🤖 Generated with Claude Code

sunib and others added 5 commits October 5, 2026 11:56
The store now treats metadata.managedFields and the last-applied-configuration annotation like redacted paths, whatever the policy: the same two paths ValidateMergePatch refuses for every projection. A native object carries both, so this is what lets the default policy edit it.

A map that holds a protected path is merged and edited key by key rather than replaced whole. That also fixes a new Secret data key beside redacted values, which was accepted but never reached changes() or patch().

BREAKING CHANGE: setValue and removeKey on metadata.annotations as a whole are refused; edit each annotation key instead. isEditable on that map is now false.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds the object URL under the same proxy and collection a native watch reads, for a native editor's conditional PATCH and guarded recovery read. Shares segment validation with nativeCollectionURL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ValidateNativeMergePatch refuses a patch without metadata.uid and metadata.resourceVersion preconditions, or one touching managedFields or the last-applied annotation, including through a parent. A host proxy runs it before forwarding a native write.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eal-API checks

examples/native-editor/editor.ts sends the captured intent as a JSON merge PATCH with its UID and resourceVersion through the watched proxy, and recovers a 409 through a guarded native GET. The real-API case runs it and the connector through a /k8s proxy that validates each PATCH: a save and its echo, a competing write between capture and PATCH, refused unconditional and machinery patches, and a same-name replacement.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 54 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 2f0a15dd-ab4f-41e0-bb50-c21ae87b95e2
📥 Commits

Reviewing files that changed from the base of the PR and between 68c9869 and 1aa4cb0.

📒 Files selected for processing (30)
  • CONTRIBUTING.md
  • README.md
  • Taskfile.yml
  • docs/adopting.md
  • docs/alternatives.md
  • docs/auth.md
  • docs/client-state-model.md
  • docs/field-reports/third-our-identity.md
  • docs/migrating.md
  • docs/proposals/0006-stream-and-save-implementation-plan.md
  • docs/saving.md
  • docs/why-a-gateway.md
  • examples/README.md
  • examples/native-editor/README.md
  • examples/native-editor/editor.ts
  • examples/native-viewer/README.md
  • gateway/README.md
  • gateway/kube/native_e2e_test.go
  • gateway/patch.go
  • gateway/patch_test.go
  • packages/krm-stream/README.md
  • packages/krm-stream/e2e/native-driver.ts
  • packages/krm-stream/src/index.ts
  • packages/krm-stream/src/policy.ts
  • packages/krm-stream/src/store.ts
  • packages/krm-stream/src/url.ts
  • packages/krm-stream/test/invariants.test.ts
  • packages/krm-stream/test/native-editing.test.ts
  • packages/krm-stream/test/native.test.ts
  • packages/krm-stream/tsconfig.test.json
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

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.

…ed read

A network failure or 5xx left the native editor free to send the same PATCH again, and an accepted write whose echo never arrived let a second Save write again too. The editor now owes a guarded read in both cases: the next Save reads instead of writing, and confirm() performs that read when the host stops waiting for an echo. A 4xx is a definite refusal and owes none. The write response is still never adopted.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sunib
sunib added this pull request to stack #56 October 5, 2026 12:20
# Conflicts:
#	packages/krm-stream/src/url.ts
#	packages/krm-stream/test/native.test.ts
@sunib
sunib merged commit 66f306b into main Oct 5, 2026
13 checks passed
@github-actions github-actions Bot mentioned this pull request Oct 5, 2026
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