Skip to content

feat(api)!: adopt Kubernetes watch terms and remove rule operation filters - #398

Merged
sunib merged 10 commits into
mainfrom
docs-kubernetes-watch-facts
Sep 28, 2026
Merged

sunib merged 10 commits into
mainfrom
docs-kubernetes-watch-facts

Conversation

@sunib

@sunib sunib commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Moves the project's read-side vocabulary to Kubernetes' own terms, and makes a rule select whole resource collections.

Breaking API change: spec.rules[].operations is removed from WatchRule and ClusterWatchRule. Every selected collection is observed through creates, updates, and deletions, and GitTarget.spec.prune.mode alone decides whether an observed removal deletes a Git document. The filter never described the object set consistently: it only applied to live events, so a create-only rule still wrote every existing object during initialization, and excluding DELETE never stopped Always from sweeping. A rule that listed a subset now also mirrors updates and removals. Set prune.mode: Never before upgrading to keep documents on removal. UPGRADING.md covers the migration:

  • Under fieldValidation=Strict a stale manifest is refused. Under Warn/Ignore the field is pruned.
  • The chart's values schema refuses the key.
  • watch_events_total loses its operation_filtered outcome.
  • The cell/sourceCell/*Cells log keys become collection/sourceCollection/*Collections.

Cell → resource collection in code and docs, with no aliases:

  • CellKey is now CollectionKey, SourceCell is SourceCollection, and cellSpec is watchSpec. watchSpec now carries only the served version, so only a version change or a forced recheck restarts a collection.
  • The four cell files move to collection names.
  • Unchanged: the rendered CollectionKey.String() values, Redis cursor keys, status units, and prune behavior.
  • The operation event tags move to internal/types, keeping the same values. Audit verb mapping and attribution are unchanged.

Docs:

Tests:

  • New: every live event type reaches the writer. Envtest asserts Strict rejects the stale field and Warn/Ignore prune it, on both CRDs. A Helm test asserts the values schema refuses the key.
  • Filter-only tests are removed, and restart tests now edit the served version.
  • task lint, task test, and local task test-e2e (87/87) pass.

🤖 Generated with Claude Code

Add a facts page on Kubernetes watch options (selectors, membership events,
initial events, bookmarks, recovery) with upstream references, and move the
attribution-grace ordering explainer into spec/ with its overlapping-watch
limitation corrected. Definitions now use collection, watch, and initial
events in prose; identifiers keep their spelling until a code migration.
The watches-and-labels design records the plan for issue #146.

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

coderabbitai Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Too many files!

This PR contains 113 files, which is 13 over the limit of 100.

To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch.

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 6045cbad-ddba-4418-a757-c4f8a64997e9

📥 Commits

Reviewing files that changed from the base of the PR and between 974be81 and 9e1fd01.

📒 Files selected for processing (113)
  • .docs-lint-scope
  • README.md
  • api/v1alpha3/clusterwatchrule_types.go
  • api/v1alpha3/watchrule_types.go
  • api/v1alpha3/zz_generated.deepcopy.go
  • charts/gitops-reverser/values.schema.json
  • charts/gitops-reverser/values.yaml
  • cmd/main_chart_quickstart_rules_test.go
  • config/crd/bases/configbutler.ai_clusterwatchrules.yaml
  • config/crd/bases/configbutler.ai_watchrules.yaml
  • config/samples/clusterwatchrule.yaml
  • config/samples/quickstart-watchrule.yaml
  • docs/INDEX.md
  • docs/TODO.md
  • docs/UPGRADING.md
  • docs/architecture.md
  • docs/commit-messages.md
  • docs/components.md
  • docs/configuration.md
  • docs/definitions.md
  • docs/demo/commit-window.md
  • docs/design/collection-terminology-rename.md
  • docs/design/data-plane-triggering.md
  • docs/design/gittarget-configuration-freshness.md
  • docs/design/metrics-observability-plan.md
  • docs/design/source-scope-simplification.md
  • docs/design/target-watch-plan.md
  • docs/design/watch-and-catalog-architecture.md
  • docs/design/watch-manager-ownership.md
  • docs/design/watch-plane-status-convergence-failures.md
  • docs/design/watches-labels-simplification.md
  • docs/facts/kubernetes-watch-options.md
  • docs/interpreting-metrics.md
  • docs/spec/README.md
  • docs/spec/gittarget-isolation-on-rule-change.md
  • docs/spec/type-followability.md
  • docs/spec/typeset-owns-discovery-grace.md
  • docs/spec/watch-event-ordering-and-attribution-grace.md
  • hack/crdfields/fields.yaml
  • internal/auditutil/identity.go
  • internal/auditutil/identity_test.go
  • internal/auditutil/objectref.go
  • internal/auditutil/objectref_test.go
  • internal/authz/source_namespace.go
  • internal/authz/source_namespace_test.go
  • internal/controller/removed_rule_operations_test.go
  • internal/git/branch_worker.go
  • internal/git/commit.go
  • internal/git/git_path_refusal.go
  • internal/git/git_path_refusal_test.go
  • internal/git/refusal_observation_test.go
  • internal/git/refusal_touch.go
  • internal/git/refusal_touch_test.go
  • internal/git/render_fidelity_gate.go
  • internal/git/render_fidelity_gate_test.go
  • internal/git/resync_flush.go
  • internal/git/resync_push_test.go
  • internal/git/resync_scope_test.go
  • internal/git/source_cell_test.go
  • internal/git/source_collection.go
  • internal/git/source_collection_test.go
  • internal/git/types.go
  • internal/git/write_boundary_precondition_test.go
  • internal/rulestore/store.go
  • internal/rulestore/store_test.go
  • internal/telemetry/exporter.go
  • internal/types/collection.go
  • internal/types/collection_test.go
  • internal/types/operation.go
  • internal/typeset/lifecycle.go
  • internal/watch/config_plane_split_review_fixes_test.go
  • internal/watch/event_router.go
  • internal/watch/event_router_test.go
  • internal/watch/git_path_acceptance.go
  • internal/watch/git_path_acceptance_scan_test.go
  • internal/watch/git_path_acceptance_test.go
  • internal/watch/gitpath_events.go
  • internal/watch/helpers_test.go
  • internal/watch/live_content_dedup_test.go
  • internal/watch/manager.go
  • internal/watch/owner_observability.go
  • internal/watch/owner_test.go
  • internal/watch/prune_declaration_test.go
  • internal/watch/render_fidelity_gate.go
  • internal/watch/resync_scope_test.go
  • internal/watch/retention_rollup.go
  • internal/watch/retention_rollup_test.go
  • internal/watch/source_namespace_planning_test.go
  • internal/watch/source_namespace_stream_summary_test.go
  • internal/watch/source_namespace_test.go
  • internal/watch/stream_readiness.go
  • internal/watch/stream_readiness_test.go
  • internal/watch/target_watch.go
  • internal/watch/target_watch_plan.go
  • internal/watch/target_watch_plan_test.go
  • internal/watch/target_watch_session_end_test.go
  • internal/watch/target_watch_shutdown_test.go
  • internal/watch/target_watch_test.go
  • internal/watch/terminating_snapshot_test.go
  • internal/watch/watch_event_metrics.go
  • internal/watch/watch_event_metrics_test.go
  • internal/watch/watch_plane_state.go
  • internal/watch/watched_type_resolver.go
  • internal/watch/watched_type_table.go
  • internal/watch/watched_type_table_test.go
  • test/e2e/audit_route_attribution_e2e_test.go
  • test/e2e/refusal_empty_commit_e2e_test.go
  • test/e2e/setup/demo-only/podinfos-intent/2-reverse-gitops.yaml
  • test/e2e/setup/demo-only/voter-gitops/test/coffeeconfig-reverse-gitops.yaml
  • test/e2e/templates/demo/clusterwatchrule-demo.tmpl
  • test/e2e/templates/demo/watchrule-all.tmpl
  • test/e2e/templates/manager/clusterwatchrule-crd.tmpl
  • test/e2e/templates/restart/watchrule-wildcard.tmpl

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: a32b8a77-3a1e-4088-96c6-1692eef57399

📥 Commits

Reviewing files that changed from the base of the PR and between 02e4cd9 and 974be81.

📒 Files selected for processing (5)
  • docs/INDEX.md
  • docs/definitions.md
  • docs/design/collection-terminology-rename.md
  • docs/design/watches-labels-simplification.md
  • docs/spec/watch-event-ordering-and-attribution-grace.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/INDEX.md
  • docs/definitions.md
  • docs/spec/watch-event-ordering-and-attribution-grace.md

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

This PR updates documentation about Kubernetes watch selection, initialization, recovery, and event ordering. It adds watch references and design proposals, moves the attribution-ordering explainer into the specification directory, and ignores local-only documentation entries.

Changes

Watch documentation

Layer / File(s) Summary
Collection terminology and recovery
docs/definitions.md, docs/architecture.md, docs/INDEX.md, docs/design/collection-terminology-rename.md
The glossary and architecture describe resource collections, watch lifecycle, initialization, cursor resumption, and scoped snapshot reconciliation. A separate plan documents a future terminology rename and operation-filter removal; it states that no implementation changes occur in this PR.
Watch options and selector design
docs/facts/kubernetes-watch-options.md, docs/design/watches-labels-simplification.md, docs/INDEX.md
The reference covers Kubernetes watch selectors, authorization, membership events, initialization, and recovery. The pending design describes per-rule selectors, collection identity, pruning, attribution, and validation requirements.
Attribution and event ordering
docs/spec/watch-event-ordering-and-attribution-grace.md, docs/facts/watch-event-ordering-and-attribution-grace.md, docs/spec/README.md, docs/spec/attribution.md, docs/architecture.md, docs/design/commitrequest-save-wait-options.md, docs/design/data-plane-triggering.md, docs/finished/*, docs/INDEX.md
The ordering explainer now resides under docs/spec/, and related links point to it. The text describes per-watch ordering and attribution waits, while noting that overlapping watches have no shared mutation order.

Local documentation ignore rule

Layer / File(s) Summary
Ignore local-only documentation
.gitignore
Adds an ignore rule for entries directly under /docs/local-only/.

Priority: ⬇️ Low

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

Change: Other

Merge Risk: ⚪ Minimal · up to 974be

This documentation-only change introduces no identified merge-blocking risk. The reviewed lifecycle and ordering descriptions match current behavior, and the selector proposal is clearly marked as future work.

Architecture Summary

Architecture risk: 🔵 Low · up to 974be

The change affects 1 system.

Changed systems: docs

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — docs (service) was modified; 14 changed files map to changed impact.

Before / after behavior

  • observed — Modified behavior in docs/architecture.md: The Redis-optional description changes from cold replay on restart to watches initializing from current state at startup; configured Redis stores cursors for reconnecting running watches.
  • observed — Modified behavior in docs/architecture.md: The watch model now selects eligible collections, chooses one served version per collection, and keeps all-namespace and named-namespace collections separate. It distinguishes first initialization from later reconnect attempts: initialization requests current state, later attempts may resume a stored cursor, and expired history requires fresh initialization. Snapshot cleanup is limited to the observed collection and pruning policy; unsupported streaming initialization uses LIST with a buffered watch.
  • observed — Modified behavior in docs/architecture.md: Delete recovery is no longer described as unconditional mark-and-sweep on every reconnect. Retained deletion events can arrive from the resume RV; fresh snapshots infer absence only within the observed collection, and only Always permits cleanup, while Never and OnEvent retain snapshot orphans.
  • observed — Modified behavior in docs/architecture.md: The ingestion and recovery descriptions replace the blanket persisted-mutation and mark-and-sweep guarantee with scoped behavior. A watch orders its own live events, but independent handlers can interleave; initialization events establish current state, and the initial-events-end bookmark completes the snapshot. The first attempt after startup or replacement requests initial state even if Redis has a cursor; later attempts in that loop may resume, while absent or expired cursors require initialization. Snapshot reconciliation removes absent managed documents only under prune.mode: Always, excludes terminating objects, and cannot infer absence outside its collection or after a failed observation. Periodic object lists and drift sweeps remain abse
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description gives detailed scope and testing information, but it does not follow the required template. It omits explicit Type of Change selections, checklist confirmations, Related Issues, Screen… Add the required template headings. Select the applicable Type of Change and Testing checkboxes, complete the Checklist, provide the related issue number or state that none applies, and add Screenshots and Additional Notes entries where app…
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. Docstring coverage is scoped to functions touched by this diff. Analyzed 0 functions across 0…
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 is concise and identifies the PR's terminology and rule-filter themes. The rule-filter change is documented as part of the PR scope, although the provided file summary shows documentation an…
Full details: Description check

Explanation

The description gives detailed scope and testing information, but it does not follow the required template. It omits explicit Type of Change selections, checklist confirmations, Related Issues, Screenshots, and Additional Notes sections.

Resolution

Add the required template headings. Select the applicable Type of Change and Testing checkboxes, complete the Checklist, provide the related issue number or state that none applies, and add Screenshots and Additional Notes entries where applicable.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

Rewrite the watches-and-labels design around server-side selection: the
rule's objectSelector joins the collection key and is sent as the watch's
labelSelector, membership follows the Kubernetes event semantics, and the
Always sweep is bounded by the selector evaluated on Git document labels.
Overlapping collections of one type must share a selector.

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

codecov Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.79412% with 6 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/watch/render_fidelity_gate.go 77.7% 2 Missing ⚠️
internal/watch/target_watch.go 97.4% 2 Missing ⚠️
internal/git/source_collection.go 85.7% 1 Missing ⚠️
internal/watch/event_router.go 94.7% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

sunib and others added 7 commits September 28, 2026 08:29
Removing a label is a deletion from the watch's point of view, and
spec.prune.mode alone decides whether the document leaves Git; there is no
separate retention option. Overlapping collections with different selectors
are refused. Record what the user-facing docs must say about both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Rename the architecture heading that contradicted its section, drop the
cell vocabulary from the target-watch-plan index row, pin the RBAC link
to v1.37.0, and match the index row style. Add the four new watch docs
to the prose gate; the two step plans say to leave it once they ship.

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

Rules select whole resource collections. WatchRule and ClusterWatchRule
lose spec.rules[].operations: every selected collection observes creates,
updates and deletions, and GitTarget spec.prune.mode alone decides whether
an observed removal deletes a Git document. The operation event tags move
to internal/types; watch_events_total drops the operation_filtered outcome.

The project-specific "cell" becomes Kubernetes' resource collection in
code, logs and docs: CellKey is CollectionKey, cellSpec is watchSpec (served
version only), SourceCell is SourceCollection, and the cell/sourceCell/
*Cells log keys become collection/sourceCollection/*Collections. Redis keys
and CollectionKey.String() output are unchanged.

BREAKING CHANGE: rules[].operations is removed. A rule that listed a subset
now also mirrors updates and removals; set spec.prune.mode: Never on the
GitTarget before upgrading to keep documents on removal. See UPGRADING.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sunib sunib changed the title docs: use Kubernetes watch terms feat(api)!: adopt Kubernetes watch terms and remove rule operation filters Sep 28, 2026
TargetView's FollowedType carries NamespaceScopes, matching the
WatchedTypeTable after rule operations were removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@sunib
sunib merged commit cc32848 into main Sep 28, 2026
20 checks passed
@sunib
sunib deleted the docs-kubernetes-watch-facts branch September 28, 2026 13:14
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