Exact-review queue Worker split runbook
- Status: proposed; not approved for production execution
- Owner: ClawSweeper maintainers and the designated Cloudflare operator
- Source of truth:
dashboard/wrangler.toml, - Last verified:
- Update when: queue bindings, migrations, runtime variables, routes,
dashboard/exact-review-queue.ts, and the deployed Worker settings
openclaw/clawsweeper@2cc2c0df8d533677c5ff82cf3b86a148dd869554
publication/state writing, verification, rollback, or approval changes
This runbook moves the existing SQLite-backed ExactReviewQueue Durable Object namespace from the clawsweeper-status Worker script to a dedicated Worker script. It is a future operational plan only. Do not change either production binding until the pre-checks pass and a maintenance owner has approved the exact script names, migration tags, and rollback operator.
Cloudflare's Durable Object migration documentation is authoritative. A cross-script move uses a transferred_classes migration on the destination Worker. The transfer creates the destination class, moves the stored objects, and automatically forwards existing source-class bindings to the destination. Do not run new_sqlite_classes for the destination class first. Migrations are atomic, cannot use gradual deployment, and each tag is applied once per environment. External Durable Object bindings use Wrangler's script_name setting.
#Proposed topology
- Source script:
clawsweeper-status. - Source class and binding:
ExactReviewQueue/EXACT_REVIEW_QUEUE. - Destination script: choose and freeze a name such as
- Destination class and binding: keep
ExactReviewQueue/ - Public API during the first cut: keep
clawsweeper-exact-review-queue before staging.
EXACT_REVIEW_QUEUE; the script name makes the class namespace unambiguous.
https://clawsweeper.openclaw.ai/internal/exact-review/* and /api/exact-review-queue on clawsweeper-status. Its router uses an external binding to the destination script. Moving routes or changing CLAWSWEEPER_EXACT_REVIEW_QUEUE_URL is a separate follow-up.
#Configuration inventory
Copy these queue-runtime values to the destination without changing them:
WORKER_BUDGETEXACT_REVIEW_ACTIONS_BUDGETEXACT_REVIEW_QUEUE_MAX_CONCURRENTEXACT_REVIEW_TARGET_MAX_CONCURRENTEXACT_REVIEW_PUBLICATION_MIN_CONCURRENTEXACT_REVIEW_PUBLICATION_BASE_CONCURRENTEXACT_REVIEW_PUBLICATION_MAX_CONCURRENTEXACT_REVIEW_DISPATCH_LEASE_MSEXACT_REVIEW_EXECUTION_LEASE_MSEXACT_REVIEW_HEARTBEAT_GRACE_MSEXACT_REVIEW_WORKFLOW_PAUSED_RETRY_MSEXACT_REVIEW_DISPATCH_DEBOUNCE_MSEXACT_REVIEW_DISPATCH_DEBOUNCE_MAX_MSEXACT_REVIEW_PENDING_SOFT_LIMITEXACT_REVIEW_TARGET_RATE_PER_HOUREXACT_REVIEW_TARGET_BURSTEXACT_REVIEW_PUBLICATION_BATCHING_ENABLEDEXACT_REVIEW_DIRECT_PUBLICATION_ENABLEDEXACT_REVIEW_STATE_REPOEXACT_REVIEW_STATE_REFEXACT_REVIEW_PUBLICATION_BATCH_SIZEEXACT_REVIEW_PUBLICATION_BATCH_MAX_CONCURRENTEXACT_REVIEW_PUBLICATION_FRESH_LANE_ENABLEDEXACT_REVIEW_PUBLICATION_FRESH_LANE_MAX_ITEMSEXACT_REVIEW_PUBLICATION_FRESH_LANE_MAX_AGE_MSEXACT_REVIEW_PUBLICATION_BATCH_WAIT_MSEXACT_REVIEW_PUBLICATION_BATCH_DISPATCH_COOLDOWN_MSEXACT_REVIEW_PUBLICATION_BATCH_DISPATCH_RESERVATION_MSCLAWSWEEPER_ENABLE_CLAWHUB, when set in the deployed environment- GitHub App identity used for Actions reads and dispatch:
CLAWSWEEPER_APP_ID or CLAWSWEEPER_APP_CLIENT_ID, optional CLAWSWEEPER_APP_INSTALLATION_ID, and secret CLAWSWEEPER_APP_PRIVATE_KEY
Keep STATUS_STORE, dashboard cache/telemetry variables, assets, dashboard cron, custom domain, and INGEST_TOKEN on clawsweeper-status. CLAWSWEEPER_WEBHOOK_SECRET can remain on the source while the source router authenticates queue writes. If the public queue endpoints move later, copy that secret in a separately reviewed route cutover; never print it while comparing settings.
#Pre-checks
- Freeze the source and destination script names, account, environment, and
- Record the current source deployment version and preserve its deployable
- Run the full local queue test set and deploy the destination code to a
- Establish the destination script without declaring or binding the
- Copy exact vars and secrets from the inventory above through approved secret
- Capture two source snapshots at least 30 seconds apart:
migration tag. Confirm the source script name from deployed Cloudflare settings, not only dashboard/wrangler.toml.
artifact/config. Prepare, but do not apply, both the forward transfer and reverse-transfer rollback configs.
non-production environment with a newly created test namespace. Prove enqueue, dedupe, claim, heartbeat, complete, reclaim, alarm, SQL migration, and /stats behavior there.
destination Durable Object class. This inert staging deploy exists only so its secrets can be installed before the transfer. Do not use new_sqlite_classes for ExactReviewQueue.
tooling. Compare names and secret versions, never values. Prove the staged script has no public route.
``bash curl --fail --silent --show-error \ https://clawsweeper.openclaw.ai/api/exact-review-queue > before-1.json sleep 30 curl --fail --silent --show-error \ https://clawsweeper.openclaw.ai/api/exact-review-queue > before-2.json ``
- Prefer a completely drained cut:
pending == 0,dispatching == 0, and - Require dispatcher state
activeor an empty queue, handoff healthidleor
leased == 0. If zero pending cannot be reached, require pending <= 5, no growth between snapshots, oldest pending under three minutes, and an explicit operator decision. Do not transfer while any active lease exists.
healthy, shed_since_reset unchanged, and no alarm/reconciliation errors in Worker logs. As a stale-lease safety gate, no dispatching lease may be older than the configured 6-minute dispatch TTL and no claimed lease may be older than the configured 130-minute execution TTL. A nonzero active count still blocks the planned drained cut even when under those limits.
#Forward deployment
- Stop initiating optional/manual exact-review dispatches. Do not disable the
- Re-read
/api/exact-review-queueimmediately before deployment. Abort if - Add the transfer only to the destination Worker's Wrangler config. Use the
live sweep workflow. Wait for the drained pre-check above.
depth increased, an active lease appeared, health degraded, or the source version changed.
real frozen source script name and a new destination migration tag:
```toml [[durable_objects.bindings]] name = "EXACT_REVIEW_QUEUE" class_name = "ExactReviewQueue"
[[migrations]] tag = "v1-transfer-exact-review-queue"
[[migrations.transferred_classes]] from = "ExactReviewQueue" from_script = "clawsweeper-status" to = "ExactReviewQueue" ```
The destination Worker must export ExactReviewQueue. Do not add new_sqlite_classes = ["ExactReviewQueue"].
- Deploy the destination Worker once with the transfer. Treat any ambiguous
- Verify the existing source URL immediately. Source bindings should forward
- Update the source binding to the external destination script and deploy the
Wrangler result as a stop condition: inspect deployed versions and migration state before retrying. Do not change the tag and blindly retry.
after the transfer, so /api/exact-review-queue must still return the same queue namespace before the source Worker is redeployed.
source Worker:
``toml [[durable_objects.bindings]] name = "EXACT_REVIEW_QUEUE" class_name = "ExactReviewQueue" script_name = "clawsweeper-exact-review-queue" ``
Keep the source's historical v1/v2 migration entries. Do not delete the old migration history or add a delete migration.
#Verification
- Fetch
/api/exact-review-queuethrough the unchanged public source route.
Compare these fields with the final pre-transfer snapshot:
pending,dispatching,leased, andshed_since_resetdispatcher.state,dispatcher.reason, anddispatcher.retry_atlanes.reviewandlanes.publicationdepth, active count, capacity, andtarget_stats,next_wake_at, andhandoff_health
next attempt
Timestamps may advance; counts, item ownership, capacities, and cumulative publication telemetry must not reset or disappear.
- Tail both Workers. The source should show router traffic but no local
- Enqueue one low-risk exact-review canary through the existing signed path.
- Run the signed terminal reconciliation workflow and verify zero unexpected
- Resume optional/manual intake only after both observation windows remain
ExactReviewQueue execution. The destination should show the queue fetches and alarms, with no storage-schema, GitHub App, dispatch, or alarm errors.
Confirm one accepted/deduped response, one dispatch, one tuple-safe claim, heartbeats, completion, and return to the prior depth. Do not use a live close/apply command as the canary.
requeues. Recheck stats after one alarm interval and after 15 minutes.
healthy. Record deployed version IDs, migration tag, before/after snapshots, canary run, and operator.
#Rollback
Application rollback and namespace rollback are different:
- If the destination class is healthy but the source router deploy is bad,
- If the destination class/runtime must be removed, do not merely roll back a
- Apply the same drain gates before reverse transfer. A rollback under active
- Never use
deleted_classes, a freshnew_sqlite_classes, or manual data copy
redeploy the last known-good source router. Cloudflare's automatic forwarding keeps its old source-class binding reaching the transferred namespace. Do not attempt a state migration for this case.
Worker version or delete the destination migration tag. Prepare a new, uniquely tagged reverse transferred_classes migration on clawsweeper-status, with from = "ExactReviewQueue", from_script = "clawsweeper-exact-review-queue", and to = "ExactReviewQueue". Deploy the source reverse transfer first, verify forwarding and stats, then update/remove the destination's local binding in a separate deploy.
leases risks duplicated workflow dispatch or delayed completion even though the namespace transfer itself is atomic.
as rollback. Escalate to Cloudflare support before destructive recovery if a reverse transfer is rejected or stats show missing state.
Because Durable Object migrations are atomic and excluded from gradual deployments, practice both the forward and reverse transfer in a non-production environment before scheduling production.