Skip to content

feat: --drop-unresolvable-principals flag + drop-aware connection scaffolding (1/4)#629

Draft
michael-richey wants to merge 1 commit into
mainfrom
drop-unresolvable-principals-scaffolding
Draft

feat: --drop-unresolvable-principals flag + drop-aware connection scaffolding (1/4)#629
michael-richey wants to merge 1 commit into
mainfrom
drop-unresolvable-principals-scaffolding

Conversation

@michael-richey

Copy link
Copy Markdown
Collaborator

Context

Stacked PR 1 of 4. Base: main.

In cross-datacenter managed-sync, some orgs reference roles/principals deleted from the source org before the org's first-ever import — so they have no source-state file and can never resolve. Today one such dead reference makes connect_resources() raise ResourceConnectionError and skip the entire resource's access-control sync (confirmed via a dashboard whose whole restriction policy silently never applied because of one deleted role).

This series adds an opt-in --drop-unresolvable-principals flag: drop a principal absent from both destination and source (permanently gone), keep syncing the rest, but still hard-fail loudly if dropping would empty a binding/list (access-elevation guard). Default-off = byte-for-byte unchanged behavior.

This PR (scaffolding — inert when the flag is off)

  • --drop-unresolvable-principals CLI option (in _diffs_options → sync/diffs/migrate), threaded through Configuration.
  • BaseResource._resolve_or_drop: three-way resolver (destination-present / source-present "not yet synced" → hard-fail as today / absent-from-both → droppable only under the flag).
  • Shared _filter_stale_binding_principals, _filter_stale_flat_roles, _raise_connection_error_if_any helpers. Lists are rebuilt (never index-mutated) to avoid the enumerate/index-shift skip bug.
  • ResourceConnectionError.empty_binding_risk; new Counter buckets + reset; config.counter back-reference; end-of-run summary lines; risk:empty_restriction_policy metric tag in _apply_resource_cb.

No model opts into this yet — pure no-op at runtime until PRs 2–4.

Testing

New unit tests for the helper (all three branches + exception propagation), Counter fields/reset, the exception attribute, summary emission, the metric tag, and CLI flag round-trip. Full unit suite green; ruff clean.

🤖 Generated with Claude Code

…on scaffolding

Adds the opt-in `--drop-unresolvable-principals` flag plus the shared, inert-when-off
machinery that later PRs wire into individual resource models. No behavior changes when
the flag is absent (the default).

- New `--drop-unresolvable-principals` CLI option (in `_diffs_options`, so it applies to
  sync/diffs/migrate) threaded through the `Configuration` dataclass.
- `BaseResource._resolve_or_drop`: three-way resolver distinguishing destination-present,
  source-present ("not yet synced", hard-fail as today), and permanently-gone
  (absent from both — droppable only under the flag).
- Shared `_filter_stale_binding_principals` / `_filter_stale_flat_roles` /
  `_raise_connection_error_if_any` helpers (rebuild lists rather than mutating in place,
  avoiding the enumerate/index-shift bug).
- `ResourceConnectionError.empty_binding_risk` flag for the access-elevation case.
- New `Counter` buckets (stale drops, empty-binding risks) + reset, surfaced by
  `_emit_apply_summary`; `config.counter` back-reference so models can record drops.
- `_apply_resource_cb` adds a `risk:empty_restriction_policy` metric tag when a binding
  emptied out.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@michael-richey michael-richey force-pushed the drop-unresolvable-principals-scaffolding branch from 7db6366 to a7514ad Compare July 14, 2026 21:32
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