# Module Manifest Contract (V1.2) Letzte Aktualisierung: 2026-03-25 ## Ziel Verbindlicher Contract fuer `modules/*/module.php`, damit Module zur Runtime deterministisch geladen und validiert werden. ## Status Dieses Dokument ist die kanonische (normative) Quelle fuer Manifest-, Runtime- und Guard-Regeln des Modul-Systems. `/docs/reference-extension-readiness.md` referenziert diesen Contract und dupliziert keine Felddefinitionen. ## JSON-Schema-Enforcement Zusatz zur Laufzeitvalidierung: - JSON-Schema-Datei: `/.agents/contracts/module-manifest.schema.json` - Laufzeit-Check bei Modul-Laden: `ModuleManifestSchemaValidator` - CLI-Check: `php bin/console module:validate ` Ein Manifest muss sowohl: 1. das JSON-Schema erfuellen als auch 2. die Laufzeit-Contracts (Klassen/Interfaces/Namespace) bestehen. ## Namespace-Konvention Alle PHP-Klassen eines Moduls muessen den Namespace `MintyPHP\Module\\*` verwenden. Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`, `MintyPHP\Support\*`) sind fuer Modul-Code verboten. Verzeichnis-Layout: ``` modules//lib/Module// Service/ # BookmarkService, Factory, etc. Repository/ # Repository + Interfaces Providers/ # SessionProvider, LayoutProvider, SearchProvider Support/ # Modul-spezifische Utilities ContainerRegistrar.php AuthorizationPolicy.php ``` Der `ModuleAutoloader` mappt `MintyPHP\*` → `modules//lib/` per PSR-4. Enforcement: `ModuleStructureContractTest::testModuleClassesUseModuleNamespace`. ## Aktivierung (`APP_ENABLED_MODULES`) - nicht gesetzt: `config/modules.php` gilt. - gesetzt und leer (`APP_ENABLED_MODULES=''`): keine Module. - gesetzt mit Liste (`APP_ENABLED_MODULES='a,b'`): exakt diese Module. ## `i18n_path` Contract Module mit eigenen Übersetzungsdateien deklarieren den relativen Pfad zum i18n-Verzeichnis: ```php 'i18n_path' => 'i18n', ``` Der `ModuleAutoloader` registriert daraus den absoluten Pfad `modules//i18n/` als zusätzliche Übersetzungsquelle. ## `permissions` Contract `permissions` ist eine Objektliste: ```php 'permissions' => [ [ 'key' => 'module.permission', 'description' => 'Human readable text', 'active' => true, // optional, default true 'is_system' => true, // optional, default true ], ], ``` - `key` und `description` sind Pflichtfelder. - Runtime-Sync erfolgt ueber `php bin/console module:sync`. - `module:sync` fuehrt diesen Sync automatisch aus. ## `scheduler_jobs` Contract `scheduler_jobs` ist eine Objektliste mit vollem Default-Contract: ```php 'scheduler_jobs' => [ [ 'job_key' => 'module.job', 'handler' => App\\Scheduler\\MyJobHandler::class, 'label' => 'Job label', 'description' => 'Job description', 'default_enabled' => true, 'default_timezone' => 'Europe/Berlin', 'default_schedule_type' => 'daily', // hourly|daily|weekly 'default_schedule_interval' => 1, 'default_schedule_time' => '02:00', // required for daily/weekly 'default_schedule_weekdays_csv' => null, // weekly: e.g. '1,3,5' 'default_catchup_once' => true, 'allowed_schedule_types' => ['daily', 'weekly'], ], ], ``` - Manifest ist Source of Truth fuer Job-Metadaten und Defaults. - Handler werden zur Laufzeit container-resolvable fuer `execute(...)` geladen. ## `ui_slots` Contract (V1.2) Neue generische Slot-Typen: - `topbar.right_item`: `{ key, template, permission?, order }` - `layout.body_end_template`: `{ key, template, permission?, order }` - `layout.head_style`: `{ key, path, permission?, order }` - `runtime.component`: `{ key, script, export?, selector?, scope?, config_path?, default_config?, phase?, permission?, order }` `runtime.component.phase` ist `early|default|late` (Default: `late`). Abgrenzung CSS: - `layout.head_style` = global auf allen Seiten. - `asset_groups` = seiten-/kontextbezogen ueber `style_groups`. ## Guard-Regeln - Route-Kollisionen sind fail-fast: - Modul vs Modul auf `target` und `path`. - Modul vs Core auf `path` beim Router-Boot. - Layout-Provider duerfen reservierte Core-Keys nicht ueberschreiben (`moduleSlots`, `csrfKey`, `currentTenant`, ...). - Layout-Provider-Top-Level-Keys muessen namespaced sein (`.`). ## Runtime-Fingerprint `web/index.php` validiert bei jedem Request, dass der Runtime-Stand zur aktuellen Modul-Konfiguration passt. - `php bin/console module:build` schreibt nach jedem Build `storage/runtime/.module-fingerprint` (sortierte Modul-IDs, kommasepariert). - `web/index.php` vergleicht den Fingerprint mit der erwarteten Modul-Liste. - Bei Mismatch: `RuntimeException` mit Hinweis auf `php bin/console module:sync`. - Kein Auto-Build im Request-Pfad — Build ist ausschliesslich CLI-Aufgabe. Methoden: `ModuleRuntimePageBuilder::expectedFingerprint()`, `readFingerprint()`, `writeFingerprint()`. ## API-Channel-Isolation API-Requests (`api/`-Prefix) ueberspringen den gesamten Web-Session-Flow: - Kein `Session::start()`, kein Cookie-Setting, kein CSRF-Token - Kein Remember-Me, kein Session-Timeout, kein Tenant-UI-Refresh - API-Auth erfolgt ueber `ApiBootstrap.php` (Bearer-Token) - `AccessControl` listet `api/` als always-public ## Standard-Runbook `php bin/console module:sync` fuehrt in fester Reihenfolge aus: 1. `module:migrate` 2. `module:permissions-sync` 3. `module:build` 4. `module:assets-sync` Der Output enthaelt eine deterministische Step-Summary (`migrate`, `permissions-sync`, `build`, `assets-sync`) inklusive `vendor_warnings_ignored=`. First-party Warnings/Notices/Deprecations gelten als Fehler (fail-fast).