@@ -396,26 +396,32 @@ substrate above.
396396## Grant lifecycle: the ` active ` switch (ADR-0049)
397397
398398Validity windows above date a ** user's grant row** . The second lifecycle
399- control dates the ** catalogue row itself** : ` sys_permission_set.active ` and
400- ` sys_position.active ` , the switch behind the Deactivate action on both objects.
401- It answers a different question — "switch this grant off for everyone, without
402- deleting it or unwinding the assignments" — and it is enforced in the same
403- place, by the same discipline: ** resolution-time filtering, fail-closed** , in
399+ control switches off the ** permission set or position itself** : a
400+ ` sys_metadata_activation ` row of type ` permission ` or ` position ` whose ` active `
401+ reads false. The activation ledger is deployment-wide. It answers a different
402+ question — "switch this grant off for everyone, without deleting it or
403+ unwinding the assignments" — and it is enforced in the same place, by the same
404+ discipline: ** resolution-time filtering, fail-closed** , in
404405` resolveAuthzContext ` , with no cleanup job involved.
405406
407+ A catalogue row's own ` active ` column (` sys_permission_set.active ` ,
408+ ` sys_position.active ` ) no longer decides a grant. That column is what Setup's
409+ Deactivate action writes today, so a Deactivate in Setup does not switch the
410+ grant off.
411+
406412- A deactivated ** permission set** contributes nothing: not its name, not its
407413 ` system_permissions ` , not its ` tab_permissions ` . As with an expired grant, a
408414 deactivated unscoped ` admin_full_access ` no longer derives ` platform_admin `
409415 — the flag is applied * before* the posture is derived, not after. A
410416 ** configured** administrator is judged elsewhere: that anchor reads the
411417 deployment's configuration and the caller's own ` sys_user ` row, never the
412- catalogue row , so the flag does not decide their standing.
418+ ledger , so the switch does not decide their standing.
413419- A deactivated ** position** stops carrying its permission sets, and its name
414420 stops appearing in ` positions ` , so a permission set that merely shares the
415421 position's name cannot resolve through it either.
416422- Assignments are untouched. ` sys_user_position ` and
417- ` sys_user_permission_set ` rows stay exactly as they were, and re-activating
418- the catalogue row restores every grant it carried, at the next resolution.
423+ ` sys_user_permission_set ` rows stay exactly as they were, and switching the
424+ ledger row back on restores every grant it carried, at the next resolution.
419425
420426The explain engine reports a deactivated-but-held row with the same dedicated
421427contributor state the expired case gets (` state: 'deactivated' ` , "held —
@@ -429,20 +435,19 @@ already walks: `sys_user_position` rows and direct `sys_user_permission_set`
429435grants (a deactivated set reached only through an active position's linkage is
430436not re-derived — that would replicate the resolver's aggregation).
431437
432- ** Absent is ACTIVE.** Only a stored value that really reads false takes a grant
433- away, so a row that predates the column keeps granting. The same predicate
434- (` isRowActive ` ) is used by every reader, including the last-administrator
435- guard's simulation — a guard that modelled "deactivated" differently from the
436- resolver would permit exactly the write it exists to refuse.
437-
438- ** Deactivating the break-glass set is refused.** Switching ` admin_full_access `
439- off un-makes every platform admin who holds it * through a grant row* , in one
440- write — and re-activating it needs the permission just lost. That write is
441- judged like deleting or renaming the row (ADR-0135 D5.2): it is refused while
442- it would leave the environment with no administrator who can sign in. The
443- enumeration behind that judgement counts ** both** anchors, so an environment
444- whose administrators are configured rather than granted is not empty, and the
445- write is permitted there. Re-activation is never refused.
438+ ** Absent is ACTIVE.** Only a ledger row whose ` active ` reads false takes a
439+ grant away; a permission set or position with no ledger row is in effect. The
440+ last-administrator guard's enumeration still reads the catalogue row's
441+ ` active ` , so it counts a row-deactivated ` admin_full_access ` as off and refuses
442+ more than it must, never less.
443+
444+ ** Switching the break-glass set off is refused.** A ledger row switching
445+ ` admin_full_access ` off would un-make every platform admin who holds it
446+ * through a grant row* , in one write — and switching it back on needs the
447+ permission just lost. The last-administrator guard refuses such a ledger
448+ write outright, whatever the administrator population (ADR-0135 D5.2), and
449+ refuses one whose target row it cannot read. Switching it back on is never
450+ refused.
446451
447452Deactivation is an incident-response control, so what it does ** not** touch is
448453deliberate. Administration surfaces keep listing and editing a deactivated
0 commit comments