forked from fa/breadcrumb-the-shire
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:
42
.agents/README.md
Normal file
42
.agents/README.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# .agents/
|
||||
|
||||
Centralized home for the 7-role agent workflow system.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
workflow.md ← Roles, state machine, handover rules (start here)
|
||||
contracts/ ← JSON schemas for role outputs (7 roles)
|
||||
templates/ ← Starter JSON payloads per role (7 templates)
|
||||
prompts/ ← Role-specific prompt baselines (7 prompts)
|
||||
checks/ ← Guard catalog (22 guards), quality gates (9), checklists
|
||||
skills/ ← Reusable skill packages (guardrails, planner, grid, PHP CI)
|
||||
runs/ ← Runtime artifacts per workflow run (gitignored)
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
| # | Role | Artifact | Guards |
|
||||
|---|---|---|---|
|
||||
| 1 | Analyst | `analysis.json` | — |
|
||||
| 2 | Planner | `plan.json` | selects guards |
|
||||
| 3 | Executor | `execution-report.json` | provides evidence |
|
||||
| 4 | Code Reviewer | `review-code.json` | 13 guards (GR-CORE/TEST/UI) |
|
||||
| 5 | Security Reviewer | `review-security.json` | 9 guards (GR-SEC) |
|
||||
| 6 | Acceptance Tester | `review-acceptance.json` | SC-* criteria |
|
||||
| 7 | Finalizer | `finalize.json` | all reviews + CI |
|
||||
|
||||
## Quick Links
|
||||
|
||||
| What | Where |
|
||||
|---|---|
|
||||
| Workflow & roles | `workflow.md` |
|
||||
| Guard catalog (22 guards) | `checks/guard-catalog.json` |
|
||||
| Quality gates (9 gates) | `checks/quality-gates.json` |
|
||||
| Code review checklist | `checks/guard-checklist.md` |
|
||||
| Acceptance checklist | `checks/acceptance-checklist.md` |
|
||||
| Module manifest schema | `contracts/module-manifest.schema.json` |
|
||||
|
||||
## Project Context
|
||||
|
||||
See `CLAUDE.md` (repo root) for tech stack, architecture rules, and conventions.
|
||||
209
.agents/contracts/module-manifest.schema.json
Normal file
209
.agents/contracts/module-manifest.schema.json
Normal file
@@ -0,0 +1,209 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "module-manifest.schema.json",
|
||||
"title": "Module Manifest Schema",
|
||||
"description": "Declarative schema for module.php manifest files under modules/<id>/.",
|
||||
"type": "object",
|
||||
"required": ["id"],
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"pattern": "^[a-z][a-z0-9_-]*$",
|
||||
"description": "Module identifier — must match the directory name under modules/."
|
||||
},
|
||||
"version": {
|
||||
"type": "string",
|
||||
"pattern": "^\\d+\\.\\d+\\.\\d+$",
|
||||
"description": "Semantic version (e.g. 1.0.0)."
|
||||
},
|
||||
"enabled_by_default": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"load_order": {
|
||||
"type": "integer",
|
||||
"default": 100,
|
||||
"description": "Lower values load first. Deterministic tie-break by id ASC."
|
||||
},
|
||||
"requires": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"uniqueItems": true,
|
||||
"default": [],
|
||||
"description": "Module IDs that must be enabled for this module to function."
|
||||
},
|
||||
"routes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["path", "target"],
|
||||
"properties": {
|
||||
"path": { "type": "string", "minLength": 1 },
|
||||
"target": { "type": "string", "minLength": 1 },
|
||||
"public": { "type": "boolean", "default": false }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"public_paths": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"default": []
|
||||
},
|
||||
"container_registrars": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "minLength": 1, "description": "FQCN of a ContainerRegistrar." },
|
||||
"default": []
|
||||
},
|
||||
"ui_slots": {
|
||||
"type": "object",
|
||||
"description": "Keyed by slot name (e.g. 'aside.tab_panel'). Values are arrays of contribution objects.",
|
||||
"patternProperties": {
|
||||
"^.+$": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["key"],
|
||||
"properties": {
|
||||
"key": { "type": "string", "minLength": 1 },
|
||||
"label": { "type": "string" },
|
||||
"icon": { "type": "string" },
|
||||
"href": { "type": "string" },
|
||||
"permission": { "type": "string" },
|
||||
"panel_template": { "type": "string" },
|
||||
"template": { "type": "string" },
|
||||
"path": { "type": "string" },
|
||||
"script": { "type": "string" },
|
||||
"export": { "type": "string" },
|
||||
"selector": { "type": "string" },
|
||||
"scope": { "type": "string" },
|
||||
"config_path": { "type": "string" },
|
||||
"default_config": { "type": "object" },
|
||||
"phase": { "type": "string", "enum": ["early", "default", "late"] },
|
||||
"type": { "type": "string" },
|
||||
"order": { "type": "integer", "default": 100 },
|
||||
"details_storage": { "type": "string" },
|
||||
"details_open_active": { "type": "boolean" },
|
||||
"base_url": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"search_resources": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "minLength": 1, "description": "FQCN of a SearchResourceProvider." },
|
||||
"default": []
|
||||
},
|
||||
"asset_groups": {
|
||||
"type": "object",
|
||||
"description": "Asset group name → file list.",
|
||||
"patternProperties": {
|
||||
"^.+$": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"layout_context_providers": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "minLength": 1, "description": "FQCN of a LayoutContextProvider." },
|
||||
"default": []
|
||||
},
|
||||
"session_providers": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "minLength": 1, "description": "FQCN of a SessionProvider." },
|
||||
"default": []
|
||||
},
|
||||
"authorization_policies": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "minLength": 1, "description": "FQCN of an AuthorizationPolicyInterface." },
|
||||
"default": []
|
||||
},
|
||||
"permissions": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["key", "description"],
|
||||
"properties": {
|
||||
"key": { "type": "string", "minLength": 1 },
|
||||
"description": { "type": "string", "minLength": 1 },
|
||||
"active": { "type": "boolean", "default": true },
|
||||
"is_system": { "type": "boolean", "default": true }
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"scheduler_jobs": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"job_key", "handler", "label", "description",
|
||||
"default_enabled", "default_timezone", "default_schedule_type",
|
||||
"default_schedule_interval", "default_schedule_time",
|
||||
"default_schedule_weekdays_csv", "default_catchup_once",
|
||||
"allowed_schedule_types"
|
||||
],
|
||||
"properties": {
|
||||
"job_key": { "type": "string", "minLength": 1 },
|
||||
"handler": { "type": "string", "minLength": 1 },
|
||||
"label": { "type": "string", "minLength": 1 },
|
||||
"description": { "type": "string", "minLength": 1 },
|
||||
"default_enabled": { "type": ["boolean", "integer"] },
|
||||
"default_timezone": { "type": "string", "minLength": 1 },
|
||||
"default_schedule_type": { "type": "string", "enum": ["hourly", "daily", "weekly"] },
|
||||
"default_schedule_interval": { "type": "integer" },
|
||||
"default_schedule_time": { "type": ["string", "null"] },
|
||||
"default_schedule_weekdays_csv": { "type": ["string", "null"] },
|
||||
"default_catchup_once": { "type": ["boolean", "integer"] },
|
||||
"allowed_schedule_types": {
|
||||
"type": "array",
|
||||
"items": { "type": "string", "enum": ["hourly", "daily", "weekly"] },
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"event_listeners": {
|
||||
"type": "object",
|
||||
"description": "Event name → list of listener descriptors.",
|
||||
"patternProperties": {
|
||||
"^.+$": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"required": ["class", "method"],
|
||||
"properties": {
|
||||
"class": { "type": "string", "minLength": 1, "description": "FQCN implementing EventListener." },
|
||||
"method": { "type": "string", "minLength": 1 }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"default": {}
|
||||
},
|
||||
"deactivation_handler": {
|
||||
"type": ["string", "null"],
|
||||
"description": "FQCN of a ModuleDeactivationHandler. Null if no cleanup needed.",
|
||||
"default": null
|
||||
},
|
||||
"migrations_path": {
|
||||
"type": ["string", "null"],
|
||||
"description": "Relative path to SQL migration scripts. Resolved against module basePath.",
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
||||
Reference in New Issue
Block a user