FIX7 Refactor Blueprint - Rollback Blueprint
05 - Rollback Blueprint
<!-- DOC_STATUS: ACTIVE_AUTHORITY --> <!-- AUTHORITY_BOUNDARY: registry in 00-readme-first.md §Active-authority boundary -->CODEX RECHECK-3 PATCH (2026-06-09) — typed set separation. The S14/S15 owner+ACL snapshot and its both-direction effective-privilege verification are over the PG-object set
U_legacy_object(doc 02 §H.4.A) and the separate principal universeU_effective_privilege_principal(§H.4.B); the snapshot captures object owner/ACL and role-membership effective privilege — a role is a principal, never an object member. Entry-vectors are not snapshotted as objects (§H.4.C).OPTION-BETA PATCH (2026-06-08). Under the Codex-approved Option Beta, no legacy object body is ever changed, so the legacy rollback has no body-restore path at all — the prior
STUB_FAIL_CLOSEDbody-restore source and the Option α/β branching are removed. Two recheck-2 fixes remain and are unchanged: (G) forward-only history — rollback NEVER clears or editsactivated_at; it supersedes forward (newmanifest_activationof a prior-payload candidate + predecessorsuperseded_by_manifest_id), and the current-active route is derived byactivated_at IS NOT NULL AND superseded_by_manifest_id IS NULL. (E) unique rollback source — the legacy owner/ACL rollback source is the single S14 snapshot artifact uniquely bound by the existingmanifest_activation.rollback_evidence_idFK toevidence_registry.evidence_id(not a bare hash). Because no body changed, the complete legacy rollback is: restore the prior owner + captured effective-privilege ACL by replaying that one verified snapshot. Manifest-object rollback re-activates the prior immutable sealed manifest version (its source bodies are already sealed in that version's payload).evidence_registryis used for the snapshot as evidence only, never as a body-restore authority.
Principle (from approved design): rollback never edits or drops an active/history object. Pre-activation rehearsal may drop empty candidate-only objects in exact reverse dependency order. Post-activation rollback is always "create-and-activate a new candidate with the prior payload, new version/epoch/quorum/evidence." Owner/ACL and live-repoint steps roll back by restoring the prior grant/ownership, never by deleting and never by changing any object body.
| step | rollback action | rollback dependency | rollback order | verify after | safe before activation? | safe after activation? | needs operator? | preserves safe-blocked state? |
|---|---|---|---|---|---|---|---|---|
| S01 roles/schema/domains | drop empty schema, domains, roles | no objects depend | last (after S04 reversal) | schema absent | YES | n/a (foundation persists) | OP | YES |
| S02 catalog root | drop empty catalog (only if no FK/ref/history) | catalog has no children yet | after S04 | catalog absent | YES | NO (becomes FK-authority root) | OP | YES |
| S03 anchors | drop empty manifest_* |
after children dropped | after S04 | anchors absent | YES | NO | OP | YES |
| S04 27 contracts | drop empty tables in doc 02 §2.6 exact reverse order (writer_repoint ... policy_rule) | reverse of creation | first | 27 tables absent | YES | NO | OP | YES |
| S05 operator/operand | drop empty | none | with S04 group | absent | YES | NO | OP | YES |
| S06 registries/activation | drop empty registries | after runtime-evidence dropped | after S07 | absent | YES | NO | OP | YES |
| S07 runtime-evidence | drop empty 11 tables | inbound FK only from evidence | before S06 | absent | YES | NO | OP | YES |
| S08 deferred constraints | drop added FK/constraints | reverse of S08 add | before table drops | constraints absent | YES | NO | OP | YES |
| S09/S10 sealed DATA (pre-seal candidate) | delete candidate rows (candidate-only, never sealed) | candidate not sealed | n/a | candidate empty | YES | NO | OP | YES |
| S12 seal | no unseal; supersede via new candidate version | new version | forward-only | superseded marker | n/a | new version only | OP (quorum) | YES |
| S13 repoint authoring | discard #26/#27 candidate rows | candidate-only | n/a | rows absent | YES | new version | T1/OP | YES |
| S14 stage + pre-cutover snapshot (no activation) | discard the staged state (manifest stays SEALED-not-active, activated_at NULL) + delete the snapshot artifact; legacy untouched |
none | immediate | manifest still STAGED; snapshot artifact absent; legacy intact | YES | YES | OP | YES (manifest never became ACTIVE; legacy unchanged) |
| S15 atomic owner+reconcile+activate+repoint | atomic forward-only deactivation-first sequence (note 5): (1) supersede the new authoritative path FORWARD - new manifest_activation of a prior-payload candidate + set predecessor superseded_by_manifest_id; NEVER clear/edit activated_at (recheck-2 BLOCKER G); (2) verify readiness BLOCKED via the derived current-active route; (3) verify the gateway cannot route the new path; (4) restore legacy by replaying the verified S14 snapshot - prior owner (directus) + captured effective-privilege ACL, NO body change (Option Beta), uniquely sourced via manifest_activation.rollback_evidence_id; (5) verify G-NOMIXED-AUTHORITY + G-BIRTH-NEUTRAL |
the single S14 snapshot (bound by rollback_evidence_id) + prior immutable sealed manifest version |
immediate, in the order above | new path superseded forward (derived current-active = none for writer type; predecessor activated_at immutable); readiness BLOCKED; legacy owner == directus; effective privileges == captured prior set; body unchanged; birth-neutral; qt001_backfill_permit/QT001-apply still independently blocked; no mixed old+new authority |
n/a | YES | OP | YES (see note 5) |
| S16 owner/ACL cutover | restore prior ownership + prior grants; re-add directus/PUBLIC exactly as captured | the S14-captured pre-cutover snapshot | immediate | ownership/ACL == S14 snapshot | n/a | YES | OP | YES (returns to directus-owned blocked state) |
| S17 legacy retention | restore prior grants/owner from the S14 snapshot (effective privileges == captured prior set) | snapshot reversible; no body changed | immediate | legacy grants/owner restored | n/a | YES | OP | YES |
| S18 legacy deprecate-marker | clear the deprecation marker | marker reversible | immediate | legacy active again per #21 | n/a | YES | OP | YES |
Rollback ordering invariants
-
Pre-activation reversal order is the exact reverse of S04 creation (doc 02 §2.6 enumerated): writer_repoint -> gateway -> plan_payload -> analyzer_contract -> workload_profile -> dynamic_sql_target -> privilege_set -> authority_scope -> quorum_requirement -> activation_policy -> tier -> signoff_requirement -> capability_artifact_requirement -> capability_measurement_requirement -> capability -> bypass_vector -> dependency -> hash_component -> readiness_gate -> principal_separation -> authority_action -> principal_class -> storage_class -> unit -> metric -> operator_primitive -> policy_rule -> manifest_item_envelope -> manifest_set; catalog root last and only when no FK/reference/history exists.
-
Post-activation never drops; it always creates+activates a prior-payload candidate.
-
Owner/ACL cutover rollback REQUIRES a captured pre-cutover ownership+ACL snapshot. It is captured at S14, BEFORE the S15.1 ownership transfer and BEFORE any REVOKE (Codex recheck BLOCKER H: the snapshot must precede ownership transfer, not be taken at S16 after
directushas already lost ownership) - without it, rollback cannot restore the exact prior owner and grants. The snapshot is PG-native, concrete, and complete (Max-E + Codex BLOCKER 5). Per control object in the sealedU_legacyset it captures:- object owner (
pg_class.relowner/pg_proc.proowner, resolved torolname); - schema privileges (
pg_namespace.nspaclonqt001_cpand any touched schema); - table/view privileges - the full
relacl: SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER (every privtype, not a subset); - function/procedure EXECUTE (
proacl); - sequence privileges (
relaclof every owned sequence); - column ACL (
pg_attribute.attacl) - table-levelrelacldoes not represent column-level grants, so a column grant would otherwise be silently dropped; - default privileges (
pg_default_acl) affecting those objects; - role memberships / effective privileges (
pg_auth_membersexpansion +aclexplode/has_table_privilege/has_function_privilegewith role inheritance) - direct ACL rows alone do not prove the effective privilege a role holds through membership; - PUBLIC grants, Directus grants, and
qt001_cp_owner/migrator/readergrants, explicitly enumerated; - the dependency to
pg_roles/pg_auth_members(so a role-drop or membership change is captured; if any package changes role membership it must snapshot and restore it explicitly); - a
snapshot_sha256over the whole canonical snapshot (hash-bound). This snapshot is a mandatory output artifact of the stage step S14 (doc 07 PKG-E tail / PKG-F precondition). It must be captured, read-back-verified, and its restore rehearsed BEFORE any ownership transfer or REVOKE (i.e. before S15.1) (MX-3 + Codex recheck BLOCKER H); an unverified or unhashed snapshot blocks the cutover. Verification compares restored effective privilege sets in BOTH directions (pre→post and post→pre), role-membership-aware, not only raw ACL-row equality - G-OWNER-CUTOVER, G-NOLEGACY-POST, and G-U-LEGACY-OPTION-BETA-UNIFORM-ENDSTATE use effective-privilege checks computed over non-superuser, non-owner roles. Superuser disposition (Codex recheck BLOCKER E/H): the cluster superuserworkflow_admin(rolsuper=true) inherently bypasses object ACL and cannot be made privilege-zero by ownership/ACL changes; it is excluded from the effective-privilege=0 claim and explicitly recorded as an accepted out-of-band property, not silently passed. Role membership is asserted unchanged by FIX7 packages; if any package changes it, the change is snapshotted and restored explicitly. Because Option Beta changes no object body, the snapshot is the SOLE legacy rollback source — there is no body artifact to capture or restore.
- object owner (
-
Every rollback returns the system to a safe-blocked state: apply/permit/REAL_RUN/QT001-apply remain blocked; birth-neutral preserved; directus-owned legacy state restorable.
-
S15 rollback is an explicit, ordered, atomic deactivation-first sequence that cannot leave a mixed old+new authoritative state (Codex BLOCKER 4 / CR-E1). The earlier wording ("the new plane remains present but dormant/superseded") asserted dormancy without an explicit deactivation action; this makes deactivation a first-class, ordered, guard-proven step. The rollback executes strictly in this order, fail-closed at each step:
- Supersede the new authoritative path FORWARD (recheck-2 BLOCKER G - forward-only,
append-only history): record a NEW
manifest_activationof a prior-payload candidate manifest (newactivation_id/control_epoch/quorum/evidence) and set the predecessor'ssuperseded_by_manifest_id. Noactivated_atis cleared or edited and no activation/history row is deleted (CP-01 §2.1/§2.4 immutability). The current active route is derived by themanifest_setpartial unique indexactivated_at IS NOT NULL AND superseded_by_manifest_id IS NULL, which now yields no active manifest for the writer type; the #26 gateway routes fail-closed.G-NOMIXED-AUTHORITYreads this derived current-active fact, never a mutated timestamp. - Verify readiness is BLOCKED (readiness gate facts show blocked) - the new path provides no authority.
- Verify the writer gateway cannot route to the new active path (no ACTIVE manifest reachable from the gateway).
- Only THEN restore legacy state - for ALL members uniformly, restore the prior owner
(
directus) + captured effective-privilege ACL by replaying the verified S14 snapshot (the S15.1 ownership transfer and the S15.2 privilege reconcile are reverted). The snapshot is uniquely identified bymanifest_activation.rollback_evidence_id→evidence_registry.evidence_id(a single approved FK; not a non-uniqueartifact_sha256lookup - recheck-2 BLOCKER E); restore fails closed on a missing/revoked/expired evidence row. There is NO body change to restore (Option Beta) — owner-isolation + privilege reconcile already reached effective-EXECUTE = 0 without a stub, so the whole legacy restore is owner + captured ACL. Restoration replays the verified S14 snapshot (which includes the legacy owner and PUBLIC EXECUTE state); it does not issue a fresh blanketGRANT ... TO PUBLICor set a new owner. So the restored owner+privilege set is exactly the captured prior set - the minimum needed to return to the pre-cutover baseline - never a broader grant. - Verify
G-NOMIXED-AUTHORITY(the new active path and any legacy executability are mutually exclusive - it is impossible for both to be active/executable) andG-BIRTH-NEUTRAL(gateway hash unchanged).
Because the new path is provably inactive before any legacy EXECUTE is restored (steps 1-3 precede step 4), there is no window in which both the new
qt001_cpauthoritative path and the legacy path are simultaneously executable. The pre-cutover baseline this returns to was itself safe-blocked (QT001 apply has been blocked since CodexNOT_SAFE; noqt001_backfill_permitis open;G-ROLLBACK-SAFEre-asserts these independent blocks). The newly-createdqt001_cpcontrol plane is NOT deleted - it is left superseded/inactive (re-attempt re-activates without rebuild). The gateway is never CREATE-OR-REPLACEd - it is the owner-isolatedqt001_cpobject (and the birth gateway is a HARD_BLOCK boundary). Under Option Beta no legacy object body is ever replaced or restored, so the legacy path performs no function-body operation at all on rollback (the former CR-E3 STUB-body carve-out is removed). If any step cannot prove its safe-blocked condition, the rollback halts fail-closed rather than proceeding to restore executability. - Supersede the new authoritative path FORWARD (recheck-2 BLOCKER G - forward-only,
append-only history): record a NEW
Rollback completeness verdict
ROLLBACK_BLUEPRINT_COMPLETE - every construction step S01..S18 has a defined rollback action,
dependency, order, post-verify, operator requirement, and safe-blocked guarantee. The single hard
precondition (the S14 pre-cutover ownership/ACL snapshot, captured BEFORE the S15.1 owner
transfer per Codex recheck BLOCKER H) is captured as a mandatory package output, not left implicit.
Under Option Beta the S15 rollback restores only the prior owner and effective-privilege ACL from
that one verified snapshot — there is no body to restore, so the former "restore the STUB bodies from
sealed evidence_registry artifacts" step is removed and the body-restore risk surface is eliminated.