{"record":{"id":"5e460ca60aecb3d5","repo":"block/buzz","slug":"request-has-not-crossed-the-explicit-approval-boun","errorCode":null,"errorMessage":"request has not crossed the explicit approval boundary","messagePattern":"request has not crossed the explicit approval boundary","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/buzz-deletion/src/lib.rs","lineNumber":1244,"sourceCode":"                    &token,\n                    serde_json::json!({\"postgres\": true, \"object_store\": true, \"redis\": true}),\n                )\n                .await?;\n        }\n        DeletionStage::LogicallyVerified => {\n            validate_frozen_inventory(request)?;\n            services\n                .store\n                .mark_retention_pending(\n                    &token,\n                    serde_json::json!({\n                        \"policy\": \"member-erasure and fleet-wide shared-CAS GC are out of V1 scope\"\n                    }),\n                )\n                .await?;\n        }\n        DeletionStage::Submitted | DeletionStage::Inventoried => {\n            anyhow::bail!(\"request has not crossed the explicit approval boundary\")\n        }\n        DeletionStage::RetentionPending | DeletionStage::Aborted => {}\n    }\n    Ok(())\n}\n\nfn token_with_current_fence(token: &LeaseToken, request: &DeletionRequest) -> LeaseToken {\n    LeaseToken {\n        fence_generation: request.fence_generation,\n        ..token.clone()\n    }\n}\n\n/// Prove logical absence by listing each tenant prefix and requiring it\n/// empty — O(1) requests per prefix, independent of fleet size.\nasync fn verify_storage_absence(services: &Services, request: &DeletionRequest) -> Result<()> {\n    for prefix in tenant_prefixes(*request.community_id.as_uuid()) {\n        let page = services.media.list_prefix_page(&prefix, None, 1).await?;","sourceCodeStart":1226,"sourceCodeEnd":1262,"githubUrl":"https://github.com/block/buzz/blob/f956e6fe06a76e50cbd8fba1a162482e752e7f1a/crates/buzz-deletion/src/lib.rs#L1226-L1262","documentation":"The deletion state machine only permits execution past an explicit approval boundary. validate/execute paths accept requests in approved-or-later stages (down through RetentionPending/Aborted handling); a request still in DeletionStage::Submitted or DeletionStage::Inventoried has not been approved by an operator, so attempting to progress it bails with this message. The two-phase design is deliberate: destructive, fleet-wide erasure must never run from a bare submit, and inventory must be reviewed before approval (the frozen-inventory validation runs for approved requests).","triggerScenarios":"Calling the run/execute path (or certain stage transitions) against a request that was submitted but never approved — e.g. `buzz-deletion run --id <uuid>` right after `submit`, or an automation tool driving stage transitions directly via the store API while the request is still Submitted/Inventoried.","commonSituations":"Scripts that chain submit+run and skip the approve step (or call approve against the wrong id); a request re-created after abort so its old id is reused by a stale runbook; operators assuming inventory implies approval.","solutions":["Advance the request through the approval step first: `buzz-deletion approve --id <request-id>` (with whatever authorisation your runbook requires), then re-run execution.","Verify the request's current stage (store listing/admin output) and operate on the id you actually approved — approve-then-run with mismatched ids is the classic slip.","If the request should not proceed, abort it instead of trying to force execution; approved-boundary checks are intentional and not bypassable by retry.","For automation, encode submit → (review inventory) → approve → run as separate steps with an explicit gate between submit and approve."],"exampleFix":"# before\nbuzz-deletion submit --host localhost:3000 --requester npub1... --reason gdpr  # -> id R\nbuzz-deletion run --id R\n# error: request has not crossed the explicit approval boundary\n\n# after\nbuzz-deletion submit --host localhost:3000 --requester npub1... --reason gdpr  # -> id R\nbuzz-deletion approve --id R\nbuzz-deletion run --id R","handlingStrategy":"validation","validationCode":"# before executing, require an approved-or-later stage\nstage=$(psql -tA \"$DATABASE_URL\" -c \"SELECT stage FROM deletion_requests WHERE id='<uuid>';\" )\nif [[ \"$stage\" == \"submitted\" || \"$stage\" == \"inventoried\" ]]; then\n  echo \"request <uuid> not approved yet — run approve first\" >&2; exit 1\nfi","typeGuard":"function isApprovedStage(stage: string): boolean {\n  return !['submitted', 'inventoried'].includes(stage);\n}","tryCatchPattern":null,"preventionTips":["Encode the approval gate as a mandatory pipeline step with human sign-off between submit and approve.","Echo the request id at each step so approve/run can never target different requests.","Treat this error as a workflow-ordering bug in your automation, never retry past it."],"tags":["buzz-deletion","state-machine","approval-workflow","gdpr","deletion"],"backgroundTag":"invalid-state-transition","analyzedSha":"f956e6fe06a76e50cbd8fba1a162482e752e7f1a","analyzedAt":"2026-08-16T22:11:40.750Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}