Skip to content

docs: spec a deferred diamond-cleanup queue (park-and-drain facet removals)#2049

Closed
0xDEnYO wants to merge 2 commits into
mainfrom
docs/deferred-diamond-cleanup-queue-spec
Closed

docs: spec a deferred diamond-cleanup queue (park-and-drain facet removals)#2049
0xDEnYO wants to merge 2 commits into
mainfrom
docs/deferred-diamond-cleanup-queue-spec

Conversation

@0xDEnYO

@0xDEnYO 0xDEnYO commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Which Linear task belongs to this PR?

Part of the deferred diamond-cleanup effort (child of EXP-350, alongside the
EXSC-193 removal work). The store layer this spec describes has since shipped in
#2051. A dedicated SmartContract (EXSC) ticket for the spec still needs to be
created/linked — flagged for follow-up.

Why did I implement it this way?

Follow-up to PR #2047 (docs/FacetRemovalReconciliation.md). Today /deprecate-contract
step 6 proposes the on-chain facet removal immediately — across ~71 mainnet
diamonds that's a mass Safe-signing event of irreversible diamondCuts, i.e. signer
fatigue, for a cleanup that isn't time-critical.

This spec parks each removal in a durable queue at deprecation time (carrying a link
to the deprecation PR) and drains it opportunistically — the next time a facet cut
happens on that network, the cleanup rides along in the same signing session. The
originating-PR link is surfaced to the multisig reviewer at signing time (first-class
requirement). No governance change — removals stay Safe- + timelock-gated.

Doc: docs/DeferredDiamondCleanupQueue.md.

What changed in this revision

This revision encodes the decisions agreed in the #dev-sc-review thread (Goran +
Daniel) and reflects the merged store layer (#2051). The two questions previously
flagged for a read are now resolved:

  1. Store — RESOLVED. A new Mongo collection on the non-sensitive MONGODB_URI
    cluster
    (DB deferred-cleanup, collection parkedTasks), mirroring the
    timelock-operations/queue sibling — not the sc_private/tunnel-gated signing
    store the earlier draft named. Nothing parked is secret; the security boundary is
    on-chain; un-gated so CI / reconcile / agent-driven /deprecate-contract reach it
    without a tunnel. Chosen over a git file because the state is mutable cross-session.
    Shipped in feat(safe): parked-tasks store layer for deferred diamond-cleanup queue #2051.
  2. Drain chokepoint — RESOLVED. Hook runPropose (propose-to-safe.ts:58) —
    not sendOrPropose, not main(), not the multisig-rollout skill. runPropose is
    the true funnel: main() and proposeDiamondCut (EVM + Tron) both route through it,
    and the agentic facet-cut path never touches main(). Public runPropose drains in
    a try/catch so the primary proposal and exit code are unaffected.

Also encoded: claimForProposal() as the atomic dedup gate (§6/§7), the
DRAIN_PARKED_TASKS flag semantics (ON for rollouts, OFF for emergencies, §6/§11), and
the loud-log + PR-link-to-signer visibility requirement (§6). §13 marks the store +
CLIs done (#2051); §14 keeps Q4 (batching) and Q8 (enqueue timing) as clearly-labelled
open recommendations rather than pretending they're settled.

Checklist before requesting a review

  • I have performed a self-review of my code
  • This pull request is as small as possible and only tackles one problem
  • I have added tests that cover the functionality / test the bug — n/a (docs only, no code)
  • For new facets — n/a
  • I have updated any required documentation (this PR is the doc)

Checklist for reviewer (DO NOT DEPLOY and contracts BEFORE CHECKING THIS!!!)

  • n/a — documentation only, no contract or script changes

…-and-drain facet removals)

Design spec (no code) for parking deprecation-driven facet removals in a durable
queue and draining them opportunistically on the next multisig action per network,
instead of an eager fleet-wide removal at deprecation time. Builds on PR #2047 /
docs/FacetRemovalReconciliation.md. Compares store options (recommends a new
sc_private.parkedTasks Mongo collection), identifies the sendOrPropose/runPropose
drain chokepoint, and makes the originating-PR link visible to the multisig
reviewer at signing time.

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

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

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

Next review available in: 59 seconds

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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: a197f60b-307b-4b7f-b000-e33a37323cff

📥 Commits

Reviewing files that changed from the base of the PR and between c37da94 and f3be062.

📒 Files selected for processing (1)
  • docs/DeferredDiamondCleanupQueue.md
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch docs/deferred-diamond-cleanup-queue-spec

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.

0xDEnYO added a commit that referenced this pull request Jul 15, 2026
…ue (#2051)

* feat(safe): parked-tasks store layer for deferred diamond-cleanup queue

Persistence layer for the deferred diamond-cleanup queue (design: PR #2049,
docs/DeferredDiamondCleanupQueue.md). New sc_private.parkedTasks collection
modelled on the timelock-operations/queue sibling and reusing the Safe/Mongo
plumbing:

- IParkedTask schema (spec §4); selectors resolved from the loupe at drain,
  not stored, so a parked list can never go stale
- computeTaskKey + partial unique index on taskKey over open statuses
  {queued, proposed} (dedup independent of the non-deterministic timelock salt)
- enqueue (E11000 -> null dedup; refuses a missing/blank prUrl), list, atomic
  claimForProposal flip, and the executed/superseded/cancelled/revert transitions
- injectable Collection I/O; 100% unit coverage except the thin live-Mongo adapter

Store layer only — no drain chokepoint, no removal engine; independent of PR #2047.

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

* feat(safe): parked-tasks read + enqueue CLIs

- list-parked-tasks.ts: read-only queue view grouped by network (facet | status
  | age | origin PR | safeTxHash) with per-network queued/proposed counts;
  --network/--pr/--status/--json, exit 0/1/2. Mirrors list-pending-proposals.ts.
- enqueue-parked-task.ts: parks one facet-removal task; refuses without prUrl,
  validates addresses via viem, production-only in v1, resolves enqueuer from
  git user.email. Delegates dedup/insert to the tested enqueueParkedTask helper.

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

* refactor(safe): tighten parked-tasks after adversarial review

- markCancelled: restrict to queued only — cancelling a proposed task would
  orphan its live Safe removal proposal from the origin-PR linkage (spec §6);
  the revert-to-queued -> cancel path handles that case explicitly
- test: in-memory fake now honors findOneAndUpdate's returnDocument option, so a
  regression dropping returnDocument:'after' would fail the post-update assertions
- list-parked-tasks: de-duplicate --network values (was double-listing/counting)
- doc: note the safeTxHash setter is intentionally deferred to the drain PR

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

* fix(safe): address CodeRabbit review on parked-tasks

- enqueueParkedTask now normalizes at the store chokepoint (trims network/
  facetName/prUrl, rejects a blank facetName) so every caller dedups
  consistently — a stray space no longer mints a distinct undeduplicated task;
  enqueue CLI adds a matching fast-fail before connecting (CR: enqueue-parked-task.ts)
- enqueue/list CLIs: set process.exitCode=1 instead of process.exit(1) in the
  inner catch so the finally block still closes the Mongo client (CR major)
- getParkedTasksCollection: use fetchWithTimeout for the VPN probe so a slow
  ipify response can't hang the CLI (CR + reuse-helpers rule)
- tests: cover facetName blank-check + network/facetName/prUrl trimming

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

* fix(safe): move parked-tasks queue to non-sensitive MONGODB_URI cluster

Addresses review (gvladika): the store's cluster choice rested on a false
premise. The cited precedent (timelock-queue.ts) runs on the non-sensitive
MONGODB_URI cluster, not sc_private. Nothing parked is secret (public facet
names, on-chain addresses, PR URLs) and the security boundary is on-chain, so
un-gated is correct and unblocks non-interactive consumers (CI backlog reports,
reconcile/TTL jobs, agent-driven /deprecate-contract) that cannot open a tunnel.

- getParkedTasksCollection now mirrors getTimelockQueueCollection: MONGODB_URI,
  DB deferred-cleanup, no VPN/ipify gate. That gate was retired code copied from
  the pre-EXSC-615 getSafeMongoCollection (removed from main in f5b88ca); under
  lifi-connect port-forwarding it could never pass, so both CLIs always exited 2.
- CLIs: recoverable-misconfig (exit 2) detection now keys on MONGODB_URI.
- Corrected the false timelock-queue precedent claim in the module header.

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

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…merged store #2051

Update the design spec to the decisions agreed in #dev-sc-review (Goran + Daniel):

- Store (§5, §9, Q1): the queue lives on the NON-sensitive MONGODB_URI cluster
  (DB deferred-cleanup, collection parkedTasks), mirroring timelock-queue.ts — not
  the sc_private/VPN-gated signing store. Nothing parked is secret; the security
  boundary is on-chain; un-gated so CI/reconcile/agent consumers reach it without a
  tunnel. Chosen over a git file because the state is mutable cross-session. Shipped
  in #2051, so store-layer facts are now verified against origin/main.
- Drain chokepoint (§6, §10, Q2): hook runPropose (propose-to-safe.ts:58), NOT
  sendOrPropose / main() / the multisig-rollout skill. runPropose is the true funnel
  — main() and proposeDiamondCut (EVM+Tron) both route through it; the agentic
  facet-cut path never touches main(). Extract a pure _runPropose; public runPropose
  drains in a try/catch so the primary proposal and exit code are unaffected. Honest
  gap: sendOrPropose-only paths fall to the cold-network backstop.
- Concurrency (§6, §7): claimForProposal() atomic queued->proposed flip is THE dedup
  gate, independent of the salt-nondeterministic intentHash.
- Opt-out flag (§6, §11): DRAIN_PARKED_TASKS ON for rollouts, OFF for emergencies.
- Visibility (§6): drained removal logged loudly + PR link into confirm-safe-tx
  detailLines + list-pending-proposals + Slack.
- §13 marks store + CLIs DONE (#2051); §14 marks Q1/Q2 resolved and keeps Q4
  (batching) / Q8 (enqueue timing) as clearly-labelled open recommendations.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@0xDEnYO

0xDEnYO commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

Folding this into #2047. The deferred-cleanup-queue spec (docs/DeferredDiamondCleanupQueue.md, both commits) has been cherry-picked onto #2047's branch so the spec ships alongside the removal engine + CLI + /deprecate-contract park wiring it documents — one self-contained, review-ready PR instead of a spec that #2047 dangling-links to. No content lost (this PR had no reviews yet). Closing in favour of #2047.

@0xDEnYO 0xDEnYO closed this Jul 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants