Skip to content

feat(cli): Add prune planner.#3411

Merged
c-r33d merged 3 commits intomainfrom
prune-migrated-policy
May 1, 2026
Merged

feat(cli): Add prune planner.#3411
c-r33d merged 3 commits intomainfrom
prune-migrated-policy

Conversation

@c-r33d
Copy link
Copy Markdown
Contributor

@c-r33d c-r33d commented Apr 29, 2026

Summary

Add a new prune planner for namespaced policy migration cleanup.

This branch introduces prune planning for:

  • actions
  • subject condition sets
  • subject mappings
  • registered resources
  • obligation triggers

It also exposes resolved migration state from the existing planner so prune logic can reuse it where appropriate.

Details

  • Added new prune plan models and structured prune status/reason output.
  • Added a new PrunePlanner with scope-specific behavior:
    • actions / subject condition sets use direct prune planning based on:
      • current legacy usage
      • canonical migrated target lookup
    • subject mappings / registered resources / obligation triggers use resolved migration state from the planner
  • Added RR-specific prune verification using both:
    • resolved source view
    • authoritative full source reloaded from the global namespace
  • Added comprehensive prune planner test coverage across statuses:
    • delete
    • blocked
    • unresolved

Supporting refactors

  • Split Planner.Plan() so resolved targets can be reused via resolve()
  • Moved shared helper logic into plan_utils.go
  • Deduplicated isStandardAction
  • Cleaned up prune naming around:
    • PruneStatusReason
    • PruneStatusReasonType
    • TargetRef

Summary by CodeRabbit

  • New Features

    • Added prune planning with detailed status classification (Delete/Blocked/Unresolved), reasons, and options for interactive review and page sizing to support policy migration workflows.
  • Refactor

    • Reorganized migration planning flow to separate resolution and finalization for clearer, more robust planning.
  • Tests

    • Added a comprehensive test suite covering prune classification, blocking/unresolved logic, and edge cases.

@c-r33d c-r33d requested a review from a team as a code owner April 29, 2026 15:26
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request adds a new prune planner to the CLI to facilitate the cleanup of legacy policy objects after they have been migrated to namespaced policies. It provides a structured way to determine if objects can be safely deleted, are blocked by existing usage, or require further manual review. The changes include new models for prune planning, a dedicated planner implementation, and comprehensive test coverage to ensure accurate classification of prune statuses.

Highlights

  • New Prune Planner: Introduced a PrunePlanner to identify and classify legacy policy objects for cleanup following a namespaced policy migration.
  • Scope-Specific Pruning: Implemented distinct pruning strategies: direct planning for actions and subject condition sets, and resolved-state planning for subject mappings, registered resources, and obligation triggers.
  • Refactored Migration Logic: Split the existing migration planner's Plan() method to expose resolved targets, enabling reuse in the new prune logic, and centralized shared utilities in plan_utils.go.
  • Prune Verification: Added authoritative verification for registered resources by reloading full source data from the global namespace to ensure safe pruning.

🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console.

Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.


The legacy code must fade away, To clear the path for a new day. With plans to prune and status set, We clean the mess, we have no regret.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 29, 2026

Warning

Rate limit exceeded

@c-r33d has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 18 minutes and 18 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2b109459-5fbe-49eb-99ed-2389bbe9298b

📥 Commits

Reviewing files that changed from the base of the PR and between f4502a6 and 0addc32.

📒 Files selected for processing (3)
  • otdfctl/migrations/namespacedpolicy/prune_plan.go
  • otdfctl/migrations/namespacedpolicy/prune_planner.go
  • otdfctl/migrations/namespacedpolicy/prune_planner_test.go
📝 Walkthrough

Walkthrough

Adds a prune-planning subsystem for namespaced policy migrations: new prune plan types, a PrunePlanner with scope-specific classification logic, refactors Planner.Plan to separate resolve/finalize, and moves/adds small utility helpers; includes extensive tests for prune classification and edge cases.

Changes

Cohort / File(s) Summary
Utilities
otdfctl/migrations/namespacedpolicy/plan_utils.go
Adds objectIDSet[T interface{ GetId() string }] and isStandardAction(*policy.Action) bool helpers (generic ID set builder and safe standard-action predicate).
Planner refactor
otdfctl/migrations/namespacedpolicy/planner.go, otdfctl/migrations/namespacedpolicy/resolved.go
Extracts resolution into (*Planner).resolve; Plan now finalizes separately. Moves/relocates isStandardAction usage (removes local strings-based implementation).
Prune model
otdfctl/migrations/namespacedpolicy/prune_plan.go
Introduces prune domain types: PruneStatus, PruneStatusReasonType, TargetRef, PrunePlan and per-target plan records with JSON tags and helper methods.
Prune planner implementation
otdfctl/migrations/namespacedpolicy/prune_planner.go
Implements PrunePlanner, NewPrunePlanner, Plan, options (page size, interactive reviewer), and core per-scope classification logic (Delete/Blocked/Unresolved), canonical matching, and in-use detection.
Tests
otdfctl/migrations/namespacedpolicy/prune_planner_test.go
Adds comprehensive tests covering planner construction, per-scope pruning logic, blocking/in-use detection, unresolved cases, registered-resource reload behavior, and related helpers.
Cleanup
otdfctl/migrations/namespacedpolicy/retrieve.go
Removes local objectIDSet duplicate now consolidated in plan_utils.go.

Sequence Diagram

sequenceDiagram
    participant Client
    participant PrunePlanner
    participant Planner
    participant PolicyClient
    participant DB as "Policy DB"

    Client->>PrunePlanner: Plan(ctx)
    PrunePlanner->>PrunePlanner: Validate scopes (single scope)

    alt Legacy-object scopes (Actions / ConditionSets)
        PrunePlanner->>PolicyClient: Retrieve legacy objects
        PolicyClient->>DB: Fetch objects
        DB-->>PolicyClient: Objects
        PolicyClient-->>PrunePlanner: Objects

        PrunePlanner->>PolicyClient: List subject mappings/resources/triggers
        PolicyClient->>DB: Scan usage relationships
        DB-->>PolicyClient: Usage data
        PolicyClient-->>PrunePlanner: Usage data

        PrunePlanner->>PrunePlanner: Determine in-use status
        PrunePlanner->>PrunePlanner: Enumerate migrated targets by namespace
        PrunePlanner->>PrunePlanner: Classify each source (Delete / Blocked / Unresolved)
    else Resolved-object scopes (Mappings / Resources / Triggers)
        PrunePlanner->>Planner: Request resolved targets
        Planner->>PolicyClient: Resolve + retrieve targets
        PolicyClient->>DB: Fetch canonical objects
        DB-->>PolicyClient: Objects
        PolicyClient-->>Planner: Resolved targets
        Planner-->>PrunePlanner: ResolvedTargets

        PrunePlanner->>PrunePlanner: Map resolved targets to prune status
        PrunePlanner->>PrunePlanner: Check migrated-from labels and canonical equality
        PrunePlanner->>PrunePlanner: Classify (Delete / Blocked / Unresolved)
    end

    PrunePlanner-->>Client: Return PrunePlan
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

comp:policy, size/xl

Suggested reviewers

  • elizabethhealy
  • alkalescent

Poem

🐰
Hop, hop — tidy rows of code,
I prune the old so new seeds grow.
Plans in hand and helpers neat,
Namespaces matched, no loose ends meet.
A gentle nibble — migration complete.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.89% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(cli): Add prune planner' accurately describes the main feature addition—a new prune planner for namespaced policy migration cleanup—and aligns with the primary changes introduced in the changeset.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch prune-migrated-policy

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
Review rate limit: 0/1 reviews remaining, refill in 18 minutes and 18 seconds.

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

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a pruning framework for legacy policy objects, adding the PrunePlanner and PrunePlan components to determine if migrated objects can be safely deleted. The implementation covers actions, subject condition sets, mappings, registered resources, and obligation triggers, and includes a refactor of the Planner to support these checks. Feedback indicates that defensive nil checks in the PrunePlanner.Plan method may be redundant and could be removed to simplify the code.

Comment thread otdfctl/migrations/namespacedpolicy/prune_planner.go
@github-actions
Copy link
Copy Markdown
Contributor

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 174.622426ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 88.090223ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 417.611624ms
Throughput 239.46 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 42.055296705s
Average Latency 418.797995ms
Throughput 118.89 requests/second

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@otdfctl/migrations/namespacedpolicy/prune_planner.go`:
- Around line 670-678: The function singleMigratedTarget constructs a TargetRef
using namespace.GetId() and namespace.GetFqn() without checking for nil; update
singleMigratedTarget to guard against a nil namespace: if namespace is nil
return an empty TargetRef (or a TargetRef containing only ID as appropriate) or
construct a TargetRef with empty NamespaceID/NamespaceFQN when namespace is nil,
otherwise use namespace.GetId() and namespace.GetFqn(); ensure you reference the
TargetRef return and the singleMigratedTarget function so callers still receive
a valid, non-panicking result.
- Around line 562-585: The current matchedActionTargets function (and the
analogous function around lines 597-620) sets labelsMatch true if any canonical
target has a matching migrated_from label and also breaks out of the namespace
loop on the first canonical match, which can mask other mismatched/missing
labels; change the logic so that you evaluate all canonical matches instead of
breaking early and compute labelsMatch as "all canonical matches have
migrated_from == source.GetId()" (e.g., initialize labelsMatch = true and flip
to false if any canonical target's migratedFromID != source.GetId(), or collect
canonical matches and compare counts), remove the premature break so every
canonical target in actionsByNamespace[namespace.GetId()] is examined, and apply
the same fix to the other function referenced (the block at ~597-620) to ensure
correct delete/classify behavior.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 417f5cee-9b5a-4d50-aa7e-abdb4b6b4b1c

📥 Commits

Reviewing files that changed from the base of the PR and between 14e7a25 and 6dd0562.

📒 Files selected for processing (7)
  • otdfctl/migrations/namespacedpolicy/plan_utils.go
  • otdfctl/migrations/namespacedpolicy/planner.go
  • otdfctl/migrations/namespacedpolicy/prune_plan.go
  • otdfctl/migrations/namespacedpolicy/prune_planner.go
  • otdfctl/migrations/namespacedpolicy/prune_planner_test.go
  • otdfctl/migrations/namespacedpolicy/resolved.go
  • otdfctl/migrations/namespacedpolicy/retrieve.go
💤 Files with no reviewable changes (1)
  • otdfctl/migrations/namespacedpolicy/retrieve.go

Comment thread otdfctl/migrations/namespacedpolicy/prune_planner.go
Comment thread otdfctl/migrations/namespacedpolicy/prune_planner.go
@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 182.204879ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 89.128456ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 441.117114ms
Throughput 226.70 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 44.083203336s
Average Latency 439.012691ms
Throughput 113.42 requests/second

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
otdfctl/migrations/namespacedpolicy/prune_planner.go (1)

562-580: ⚠️ Potential issue | 🟠 Major

Inspect every canonical match in the namespace before classifying.

These loops still stop at the first canonical match. If a namespace contains multiple canonical duplicates, a later target with the correct migrated_from label is never seen, so the source can be classified unresolved incorrectly and the returned target context is incomplete.

Suggested fix
 func matchedActionTargets(source *policy.Action, targetNamespaces []*policy.Namespace, actionsByNamespace map[string][]*policy.Action) ([]TargetRef, bool, bool, error) {
 	targets := make([]TargetRef, 0)
 	foundCanonical := false
 	labelsMatch := false

 	for _, namespace := range targetNamespaces {
 		for _, target := range actionsByNamespace[namespace.GetId()] {
 			if target == nil || !actionCanonicalEqual(source, target) {
 				continue
 			}
 			if target.GetId() == "" {
 				return nil, false, false, fmt.Errorf("%w: migrated target for source %q has empty id", ErrInvalidPruneResolvedTarget, source.GetId())
 			}
 			foundCanonical = true
 			targets = append(targets, singleMigratedTarget(target.GetId(), namespace))
 			if migratedFromID(target) == source.GetId() {
 				labelsMatch = true
 			}
-			break
 		}
 	}

 	return targets, foundCanonical, labelsMatch, nil
 }

 func matchedSubjectConditionSetTargets(source *policy.SubjectConditionSet, targetNamespaces []*policy.Namespace, scsByNamespace map[string][]*policy.SubjectConditionSet) ([]TargetRef, bool, bool, error) {
 	targets := make([]TargetRef, 0)
 	foundCanonical := false
 	labelsMatch := false

 	for _, namespace := range targetNamespaces {
 		for _, target := range scsByNamespace[namespace.GetId()] {
 			if target == nil || !subjectConditionSetCanonicalEqual(source, target) {
 				continue
 			}
 			if target.GetId() == "" {
 				return nil, false, false, fmt.Errorf("%w: migrated target for source %q has empty id", ErrInvalidPruneResolvedTarget, source.GetId())
 			}
 			foundCanonical = true
 			targets = append(targets, singleMigratedTarget(target.GetId(), namespace))
 			if migratedFromID(target) == source.GetId() {
 				labelsMatch = true
 			}
-			break
 		}
 	}

 	return targets, foundCanonical, labelsMatch, nil
 }

Also applies to: 597-615

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@otdfctl/migrations/namespacedpolicy/prune_planner.go` around lines 562 - 580,
The loops in matchedActionTargets stop at the first canonical match, which
misses other canonical targets (and any with the migrated_from label); remove
the early break so you inspect every target in a namespace: for each target in
actionsByNamespace[namespace.GetId()] if actionCanonicalEqual(source, target)
then validate id, set foundCanonical=true, append
singleMigratedTarget(target.GetId(), namespace) and set labelsMatch=true if
migratedFromID(target)==source.GetId(); do not break so later canonical matches
are collected. Apply the identical change to the second, similar loop block (the
duplicate logic later in the file) so both places collect all canonical
duplicates instead of exiting after the first match.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@otdfctl/migrations/namespacedpolicy/prune_planner.go`:
- Line 16: The prune reason string
pruneStatusReasonMessageMigrationLabelNotFound currently covers both missing and
mismatched migrated_from labels; update the code to distinguish these cases by
adding a new constant (e.g., pruneStatusReasonMessageMigrationLabelMismatch =
"migrated target has wrong migration label") and use migratedFromID(target) !=
"" vs migratedFromID(target) == "" checks around the compare with sourceID to
emit either the existing "missing migration label" message or the new "wrong
migration label" message; update all usages (including where
pruneStatusReasonMessageMigrationLabelNotFound is currently used at the other
occurrences you noted) so operators see correct structured prune output.

---

Duplicate comments:
In `@otdfctl/migrations/namespacedpolicy/prune_planner.go`:
- Around line 562-580: The loops in matchedActionTargets stop at the first
canonical match, which misses other canonical targets (and any with the
migrated_from label); remove the early break so you inspect every target in a
namespace: for each target in actionsByNamespace[namespace.GetId()] if
actionCanonicalEqual(source, target) then validate id, set foundCanonical=true,
append singleMigratedTarget(target.GetId(), namespace) and set labelsMatch=true
if migratedFromID(target)==source.GetId(); do not break so later canonical
matches are collected. Apply the identical change to the second, similar loop
block (the duplicate logic later in the file) so both places collect all
canonical duplicates instead of exiting after the first match.
🪄 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: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ce06b228-8753-43ed-b1cc-6c37ad8dadea

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd0562 and f4502a6.

📒 Files selected for processing (1)
  • otdfctl/migrations/namespacedpolicy/prune_planner.go

Comment thread otdfctl/migrations/namespacedpolicy/prune_planner.go Outdated
@github-actions
Copy link
Copy Markdown
Contributor

@github-actions
Copy link
Copy Markdown
Contributor

Benchmark results, click to expand

Benchmark authorization.GetDecisions Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 175.041456ms

Benchmark authorization.v2.GetMultiResourceDecision Results:

Metric Value
Approved Decision Requests 1000
Denied Decision Requests 0
Total Time 90.72636ms

Benchmark Statistics

Name № Requests Avg Duration Min Duration Max Duration

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 426.606994ms
Throughput 234.41 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 42.123397952s
Average Latency 419.490532ms
Throughput 118.70 requests/second

@github-actions
Copy link
Copy Markdown
Contributor

⚠️ Govulncheck found vulnerabilities ⚠️

The following modules have known vulnerabilities:

  • tests-bdd

See the workflow run for details.

@github-actions
Copy link
Copy Markdown
Contributor

Comment thread otdfctl/migrations/namespacedpolicy/prune_planner.go
Comment thread otdfctl/migrations/namespacedpolicy/prune_planner.go
@c-r33d c-r33d added this pull request to the merge queue May 1, 2026
Merged via the queue into main with commit 3e294e6 May 1, 2026
39 checks passed
@c-r33d c-r33d deleted the prune-migrated-policy branch May 1, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants