ADR-0126: Project Dissolution — the Un-Birth of an Unused Scope¶
- Status: accepted (2026-08-25 — rows 34/35/36: "Extending dissolving an unused project is desirable. Please plan this." then "35 and 36: agree with your recommendations, go ahead. I really like the idea of a declared sandbox mode")
- Date: 2026-08-25
- Deciders: Nicolas Burri
- Relates to: ADR-0122 §4 (a project's
SCOPE_CREATEDanchors on the organization — the pair this completes;CREATE_PROJECTis the authority this mirrors), ADR-0124 (the empty-scope bright line, and the lesson that emptiness is read through a SECURITY-DEFINER oracle, never the caller's RLS eyes), ADR-0113 D6 + V094 (the import dissolve — the destroyer pattern and the trail-survives-one-anchor-up precedent), ADR-0109 §4 (event and destruction are one transaction or neither), V106 (the working-record tripwire that defines the widened line), V087 (the contents-oracle pattern), ADR-0125 §3 (the archive-of-record sentence §6 makes operational).
Citation corrections (2026-09-04, from the ADR-coherence review §2.5 — the cited rules are real, the cited sections are not). - "ADR-0122 §4 (a project's
SCOPE_CREATEDanchors on the organization)" — ADR-0122 §4 decides theCREATE_PROJECTsplit and that the gate is checked at the target organization's anchor scope. It says nothing aboutSCOPE_CREATED. The matching trail anchoring is realization (the V-migrations), not ADR text; §5's "beside ADR-0122 §4'sSCOPE_CREATED" should be read as "beside the org-anchoredSCOPE_CREATEDwhose gate ADR-0122 §4 places at the same anchor". The pairing this ADR mirrors is genuine — only the pointer was. - "ADR-0109 §4 (event and destruction are one transaction or neither)" — ADR-0109 §4 is the narrow governed undo:DELETEof a provably-empty organization, audited GLOBAL-anchored asMANDATOR_DELETED. The one-transaction rule's precedent is V094's destroyer (the import dissolve), already cited correctly two lines above.
Context¶
Three dissolution surfaces existed — organizations ("dissolving must destroy nothing"), import-born scopes (provisional until the first working record, story preserved one anchor up), and the import's own refusal deferring adopted scopes to "whatever governs its lifecycle" — but a wizard-born project had no lifecycle governor at all: the one birth path with no honest exit. His smoke round found it within a day of the one-party birth shipping.
The obvious alternative was considered and REJECTED: an any-status dissolve for the system administrator "with a clear record in the activity log". It trades the strongest sentence this product can say — records cannot be deleted, structurally, by anyone — for "deletion is possible but logged"; a log line preserves THAT something was destroyed, never WHAT (the org dissolve's own principle); and a GLOBAL any-status kill switch is one identity able to destroy a tenant's scope, crossing the top quality goal. Both operational needs behind it get honest shapes below instead. He asked for the honest verdict and ruled with it.
Decision¶
- Projects gain a dissolve affordance in the creation-mirror shape. The provisioning read
answers
mayBeDissolvedon PROJECT rows — TRUE/FALSE pre-resolved server-side, null to a caller without the gate (disclosure discipline, the mandator pattern). The frontend offers "Dissolve project…" exactly where the server said TRUE, never judging for itself. - The gate is
CREATE_PROJECTeffective at the project's organization anchor. Create and un-create are one authority; sovereignty stays inside the org boundary. No new permission. - The line, in two ruled slices. Wave 1: dissolvable while the scope holds ZERO documents and zero V106 record-evidence rows. Slice 2 (ruled now, built after wave 1 proves the pattern): widen to V106's working-record line — a scope this instance has witnessed no act in beyond setup. Drafts do not block (work, not records) and are destroyed with a counted confirmation; a RELEASED version blocks, because an ordinary release IS an approval act this instance witnessed — exactly why import-born releases (whose acts stayed at the source) remain provisionally destroyable and native ones do not. Both slices resolve through a V087-style contents oracle (SECURITY DEFINER, counts never rows).
- The destroyer is V113, a V094 sibling:
lqms_dissolve_empty_scopere-checks the authority and the emptiness precondition past the caller's RLS inside the function, derives its delete set frompg_constraint, and RAISES rather than reporting a dissolution it could not finish. - The trail pair outlives the scope.
SCOPE_DISSOLVEDis recorded at the organization anchor — beside ADR-0122 §4'sSCOPE_CREATED— BEFORE the rows go, in the same transaction, with a counted payload (code, name, folders, activations, policies): counts, never content. The org trail tells the whole life: born, shaped, dissolved. - The departure dissolve (scenario 2) is a SEPARATE act riding the export twin. After rows 31–33 and the exporter build: dissolving a scope that has MIGRATED requires (a) a verified completed export citing bundle sha and destination, and (b) a named final backup as the archive of record — the dissolution event cites both, making ADR-0125 §3's sentence operational. Retention is served by the archive; the live system keeps the pointer. Until it exists, a migrated scope is retained read-only at the source.
- Named direction, not yet designed: the declared SANDBOX scope kind (post-1.0). A scope declared sandbox at birth whose contents are structurally never records — visible as such everywhere, excluded from evidence surfaces, always dissolvable. The structural answer to "people will test the release workflow on a production instance", instead of weakening record scopes. Ruled desirable ("I really like the idea"); its ADR comes with the post-1.0 arc.
- What stays impossible, on every surface: deleting a scope this instance has witnessed real acts in. That path is retirement/disposal with retention semantics — the post-1.0 arc — and every refusal names it as the remedy.
Consequences¶
- The auditor sentence survives intact: no identity can destroy witnessed records; what CAN be destroyed is exactly what the system can prove nobody ever relied on.
- Re-birth under a dissolved project's code is possible (the fullstack case proves no residue holds the code hostage).
- The week-one test-scope reality is served by slice 2's line; the sandbox kind covers the remainder when it comes.
- Build plan: project-dissolution-design.md.