feat: module architecture improvements — session keys, dependency graph, event dispatcher, deactivation hooks

Six targeted improvements to the modular monolith platform:

1. Fix AddressBook session key prefix to follow module.<id>.* convention
2. Move ADDRESS_BOOK_VIEW permission constant from core PermissionService into module
3. Add declarative JSON schema for module manifests (.agents/contracts/)
4. Add `requires` field with missing-dependency and circular-dependency detection
5. Add lightweight fire-and-forget event dispatcher (user.created/deleted/login/logout)
6. Add module deactivation hook interface and CLI script (bin/module-deactivate.php)

Includes 15 new architecture/unit tests covering all new functionality.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:20:13 +01:00
parent 866d43e15a
commit ef72b34c40
31 changed files with 1041 additions and 75 deletions

View File

@@ -184,7 +184,7 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
$canEditUser = $this->canEditUserForTarget($actorUserId, $targetUserId);
if (!$this->hasPermission($actorUserId, PermissionService::USERS_VIEW)
&& !$this->hasPermission($actorUserId, PermissionService::ADDRESS_BOOK_VIEW)
&& !$this->hasPermission($actorUserId, 'address_book.view') // Registered by addressbook module when active
&& !$canEditUser) {
return AuthorizationDecision::deny(403, 'forbidden');
}
@@ -409,7 +409,7 @@ class UserAuthorizationPolicy implements AuthorizationPolicyInterface
|| ($isOwnAccount && $this->hasPermission($actorUserId, PermissionService::USERS_SELF_UPDATE))
),
'can_manage_api_tokens' => $canEditUser && $this->hasPermission($actorUserId, PermissionService::API_TOKENS_MANAGE),
'can_view_address_book' => $this->hasPermission($actorUserId, PermissionService::ADDRESS_BOOK_VIEW),
'can_view_address_book' => $this->hasPermission($actorUserId, 'address_book.view'), // Registered by addressbook module when active
'can_view_user_meta' => $this->hasPermission($actorUserId, PermissionService::USERS_VIEW_META),
'can_view_user_audit' => $canViewUserAudit,
'can_access_pdf' => $this->hasPermission($actorUserId, PermissionService::USERS_ACCESS_PDF),