Cluster-3a batch-replay of the roles-edit pilot (step 4). Same shape
as roles-edit with three domain-specific deltas:
* Integer ID instead of UUID — passed to the aggregator as
(string) $id; the lookup itself stays integer-keyed via
PermissionService::find().
* Extra authorize context key target_is_system — pre-computed from
the loaded permission and threaded through both the CONTEXT and
SUBMIT authorize calls.
* Domain renames (can_update_permission / can_delete_permission,
permission_not_found scope-key, ABILITY_ADMIN_PERMISSIONS_*).
Confirms the cluster-3a pattern: forbiddenStrategy:'deny' produces
identical Guard::deny() semantics across actions; the helper file
stays 0-diff for the second cluster-3a action; PermissionService
warmup absence is preserved (cluster 3 does not need it).
Three drift decisions reproduced verbatim: notFoundFlashScopeKey,
t() consistency on Flash::success('Permission updated'), defensive
$canManageAllPermissions = $tenantScope['scope'] === 'all'.
ActionContextCsrfPairingContractTest now covers four callers
(departments, tenants, roles, permissions) and stays green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>