1
0

docs: replace ASCII umlaut fallbacks with proper äöüß across all .md

Most German docs were written with `ue`/`ae`/`oe`/`ss` ASCII fallbacks
(`fuer`, `aenderung`, `koennen`, `gemaess`) — relic from older toolchain
constraints. Replaced 237 occurrences across 38 .md files with proper
umlauts and ß so the docs read naturally.

Substitutions are constrained to plain prose: fenced code blocks
(```...```), inline code spans (`...`), markdown link targets `[..](..)`,
and HTML comments are preserved verbatim. Path references like
`docs/howto-erste-aenderung.md` keep their original (umlaut-replaced)
filenames — only the surrounding prose is normalised.

Tool: bin/fix-md-umlauts.py (idempotent — no-op on already-clean files).
Re-runnable if ASCII fallbacks creep back in via copy-paste.

Doc-link-check + doc-drift-check stay green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 09:57:22 +02:00
parent 2e73cb98b4
commit 545bcc789b
39 changed files with 499 additions and 237 deletions

View File

@@ -1,6 +1,6 @@
--- ---
name: core-guardrails name: core-guardrails
description: Verbindliche Guardrails fuer Architektur-, Refactor-, Zentralisierungs-, Standardisierungs- und Implementierungsaufgaben in diesem Starterkit. Verwenden, wenn Code geaendert, neu strukturiert oder gegen bestehende Layer-/UI-/Security-Standards geprueft werden soll. description: Verbindliche Guardrails für Architektur-, Refactor-, Zentralisierungs-, Standardisierungs- und Implementierungsaufgaben in diesem Starterkit. Verwenden, wenn Code geändert, neu strukturiert oder gegen bestehende Layer-/UI-/Security-Standards geprüft werden soll.
--- ---
# Core Guardrails # Core Guardrails
@@ -12,21 +12,21 @@ Durchgaengig dieselben technischen Grenzen durchsetzen, damit Core robust, wartb
## Verbindliche Regeln ## Verbindliche Regeln
1. MUST Layering einhalten (`pages` -> `Service` -> `Repository`, Templates nur Rendering). 1. MUST Layering einhalten (`pages` -> `Service` -> `Repository`, Templates nur Rendering).
2. MUST Request/Input/Validation-Contract einhalten (`requestInput()`, `FormErrors`, API-Validation ueber `ApiResponse::validationFromFormErrors(...)`, kein `ApiResponse::readJsonBody(...)` in `pages/api/v1/**`, keine direkten Superglobals in `pages/**`). 2. MUST Request/Input/Validation-Contract einhalten (`requestInput()`, `FormErrors`, API-Validation über `ApiResponse::validationFromFormErrors(...)`, kein `ApiResponse::readJsonBody(...)` in `pages/api/v1/**`, keine direkten Superglobals in `pages/**`).
3. MUST AuthZ/RBAC serverseitig durchsetzen und im UI nur ueber `$viewAuth` steuern. 3. MUST AuthZ/RBAC serverseitig durchsetzen und im UI nur über `$viewAuth` steuern.
4. MUST Security-Guards einhalten (CSRF auf POST-Endpunkten, keine unredacted PII/Secrets in Logs/Audit-Meta, SQL nur vorbereitet ueber Repository). 4. MUST Security-Guards einhalten (CSRF auf POST-Endpunkten, keine unredacted PII/Secrets in Logs/Audit-Meta, SQL nur vorbereitet über Repository).
5. MUST UI-Standards und Data-Contracts fuer Listen/Detailseiten nutzen (Wrapper, Partials, globaler Confirm-Dialog statt `window.confirm`, inklusive globaler Grid-Standards fuer rowInteraction/pageSize). 5. MUST UI-Standards und Data-Contracts für Listen/Detailseiten nutzen (Wrapper, Partials, globaler Confirm-Dialog statt `window.confirm`, inklusive globaler Grid-Standards für rowInteraction/pageSize).
6. MUST Frontend-Hardcuts einhalten (keine inline ESM-Module in Templates/Pages, JS-`src` mit `assetVersion(...)`, zentrale Telemetrie fuer relevante Frontend-Warnings/Fetch-Fehler). 6. MUST Frontend-Hardcuts einhalten (keine inline ESM-Module in Templates/Pages, JS-`src` mit `assetVersion(...)`, zentrale Telemetrie für relevante Frontend-Warnings/Fetch-Fehler).
7. MUST Quality-Gates ausfuehren und Ergebnis transparent berichten. 7. MUST Quality-Gates ausführen und Ergebnis transparent berichten.
8. MUST bei Konflikten den regelkonformen Weg waehlen oder den Konflikt als Blocker markieren. 8. MUST bei Konflikten den regelkonformen Weg wählen oder den Konflikt als Blocker markieren.
9. MUST NOT Extension-Architektur erfinden; dieses Thema ist aktuell Out of Scope. 9. MUST NOT Extension-Architektur erfinden; dieses Thema ist aktuell Out of Scope.
## Vorgehen je Aufgabe ## Vorgehen je Aufgabe
1. Scope bestimmen (`core`, `ui`, `api`, `mixed`). 1. Scope bestimmen (`core`, `ui`, `api`, `mixed`).
2. Vorhandene Contracts zuerst suchen, dann wiederverwenden. 2. Vorhandene Contracts zuerst suchen, dann wiederverwenden.
3. Aenderung minimal halten, keine Seiteneffekte ohne Grund. 3. Änderung minimal halten, keine Seiteneffekte ohne Grund.
4. Nach Aenderung die relevanten Gates ausfuehren. 4. Nach Änderung die relevanten Gates ausführen.
5. Rest-Risiken explizit benennen. 5. Rest-Risiken explizit benennen.
## Out of Scope ## Out of Scope

View File

@@ -16,18 +16,18 @@
## Input / Validation ## Input / Validation
1. MUST Input in `pages/**` ueber `requestInput()` lesen. 1. MUST Input in `pages/**` über `requestInput()` lesen.
2. MUST NOT `$_POST`, `$_GET`, `$_FILES`, `REQUEST_METHOD` in `pages/**` verwenden. 2. MUST NOT `$_POST`, `$_GET`, `$_FILES`, `REQUEST_METHOD` in `pages/**` verwenden.
3. MUST NOT `$_SESSION`, `$_SERVER`, `$_COOKIE`, `$_ENV` in `pages/**` direkt verwenden. 3. MUST NOT `$_SESSION`, `$_SERVER`, `$_COOKIE`, `$_ENV` in `pages/**` direkt verwenden.
4. MUST Form-Validation ueber `FormErrors` fuehren. 4. MUST Form-Validation über `FormErrors` führen.
5. MUST API-Validation-Fehler ueber `ApiResponse::validationFromFormErrors(...)` ausgeben. 5. MUST API-Validation-Fehler über `ApiResponse::validationFromFormErrors(...)` ausgeben.
6. MUST NOT `ApiResponse::readJsonBody(...)` in `pages/api/v1/**` verwenden. 6. MUST NOT `ApiResponse::readJsonBody(...)` in `pages/api/v1/**` verwenden.
## List-Data Endpoints ## List-Data Endpoints
1. MUST `pages/**/data().php` als GET-only Endpunkte umsetzen (`gridRequireGetRequest()`). 1. MUST `pages/**/data().php` als GET-only Endpunkte umsetzen (`gridRequireGetRequest()`).
2. MUST Query-Filter ueber `gridParseFilters(...)` + `filter-schema.php` normalisieren. 2. MUST Query-Filter über `gridParseFilters(...)` + `filter-schema.php` normalisieren.
3. MUST NOT Query-Filter inline/parallele Alias-Parser in `data().php` neu einfuehren. 3. MUST NOT Query-Filter inline/parallele Alias-Parser in `data().php` neu einführen.
## Security / API ## Security / API
@@ -35,11 +35,11 @@
2. MUST Tenant-Scope serverseitig erzwingen. 2. MUST Tenant-Scope serverseitig erzwingen.
3. MUST CSRF auf POST-Endpunkten vor Body-Verarbeitung verifizieren. 3. MUST CSRF auf POST-Endpunkten vor Body-Verarbeitung verifizieren.
4. MUST NOT PII/Secrets unredacted in Logs oder Audit-Metadata schreiben. 4. MUST NOT PII/Secrets unredacted in Logs oder Audit-Metadata schreiben.
5. MUST SQL nur ueber Repository mit prepared statements ausfuehren. 5. MUST SQL nur über Repository mit prepared statements ausführen.
6. MUST extern UUID-first bleiben. 6. MUST extern UUID-first bleiben.
7. MUST API-Fehler konsistent halten (`error`, optional `errors`). 7. MUST API-Fehler konsistent halten (`error`, optional `errors`).
8. MUST OpenAPI im selben Merge aktualisieren, wenn API geaendert wird. 8. MUST OpenAPI im selben Merge aktualisieren, wenn API geändert wird.
## Pruef-Hinweis ## Prüf-Hinweis
- Harte Repo-Gates: `tests/Architecture/CoreStarterkitContractTest.php` - Harte Repo-Gates: `tests/Architecture/CoreStarterkitContractTest.php`

View File

@@ -2,69 +2,69 @@
## Listen-Standard ## Listen-Standard
1. MUST Listen ueber `initStandardListPage(...)` initialisieren. 1. MUST Listen über `initStandardListPage(...)` initialisieren.
2. MUST Filter-Toolbar ueber zentrale Helper/Partials rendern. 2. MUST Filter-Toolbar über zentrale Helper/Partials rendern.
3. MUST aktive Chips/Drawer-Verhalten ueber Standard-Contracts laufen lassen. 3. MUST aktive Chips/Drawer-Verhalten über Standard-Contracts laufen lassen.
4. MUST NOT direkt `gridFiltersFromSchema(...)` oder `initListFilterExperience(...)` in Listen-Templates verwenden. 4. MUST NOT direkt `gridFiltersFromSchema(...)` oder `initListFilterExperience(...)` in Listen-Templates verwenden.
5. MUST NOT Legacy-Filter-Toggle-Markup (`data-toolbar-toggle`, `data-filter-overflow`, `data-filter-toggle`) neu einbauen. 5. MUST NOT Legacy-Filter-Toggle-Markup (`data-toolbar-toggle`, `data-filter-overflow`, `data-filter-toggle`) neu einbauen.
6. MUST rowInteraction-Standard fuer Listen mit Row-Navigation beibehalten (sichtbare Action-Spalte + Enter + Doppelklick-Fallback). 6. MUST rowInteraction-Standard für Listen mit Row-Navigation beibehalten (sichtbare Action-Spalte + Enter + Doppelklick-Fallback).
7. MUST pageSize-Standard beibehalten (10/25/50/100, Toolbar rechts, URL > localStorage > default). 7. MUST pageSize-Standard beibehalten (10/25/50/100, Toolbar rechts, URL > localStorage > default).
## Detailseiten-Standard ## Detailseiten-Standard
1. MUST Hauptformular mit `data-standard-detail-form="1"` markieren. 1. MUST Hauptformular mit `data-standard-detail-form="1"` markieren.
2. MUST Unsaved/Shortcut/Dirty-State ueber `initStandardDetailPage(...)` nutzen. 2. MUST Unsaved/Shortcut/Dirty-State über `initStandardDetailPage(...)` nutzen.
3. MUST Detail-Aktionen ueber Action-Policy-Contract (`data-detail-action-policy`, `data-detail-action-kind`, `data-detail-confirm-message`) steuern. 3. MUST Detail-Aktionen über Action-Policy-Contract (`data-detail-action-policy`, `data-detail-action-kind`, `data-detail-confirm-message`) steuern.
4. MUST NOT inline `confirm(...)` (`onclick` / `onsubmit`) auf standardisierten Detailseiten nutzen. 4. MUST NOT inline `confirm(...)` (`onclick` / `onsubmit`) auf standardisierten Detailseiten nutzen.
## Confirm + Telemetry Standard ## Confirm + Telemetry Standard
1. MUST Confirm-Dialoge zentral ueber den globalen Confirm-Service abwickeln. 1. MUST Confirm-Dialoge zentral über den globalen Confirm-Service abwickeln.
2. MUST NOT `window.confirm(...)` in App-JS oder inline HTML-Handlern verwenden. 2. MUST NOT `window.confirm(...)` in App-JS oder inline HTML-Handlern verwenden.
3. MUST relevante Frontend-Warnpfade zentral ueber Frontend-Telemetrie erfassen. 3. MUST relevante Frontend-Warnpfade zentral über Frontend-Telemetrie erfassen.
4. MUST NOT relevante UX-/Fehlerpfade nur als Console-Warnung implementieren. 4. MUST NOT relevante UX-/Fehlerpfade nur als Console-Warnung implementieren.
## Frontend Page-Module Hard Cut ## Frontend Page-Module Hard Cut
1. MUST Seiteninitialisierung ueber dedizierte Module unter `web/js/pages/*` umsetzen. 1. MUST Seiteninitialisierung über dedizierte Module unter `web/js/pages/*` umsetzen.
2. MUST Page-Config ueber `script[type="application/json"]` + `readPageConfig(...)` bereitstellen/lesen. 2. MUST Page-Config über `script[type="application/json"]` + `readPageConfig(...)` bereitstellen/lesen.
3. MUST alle JS-`src` in `pages/**` und `templates/**` ueber `assetVersion('...js...')` laden. 3. MUST alle JS-`src` in `pages/**` und `templates/**` über `assetVersion('...js...')` laden.
4. MUST NOT inline `<script type="module">...</script>` in `pages/**` oder `templates/**` verwenden. 4. MUST NOT inline `<script type="module">...</script>` in `pages/**` oder `templates/**` verwenden.
## Component Lifecycle Runtime ## Component Lifecycle Runtime
1. MUST migrierte Komponenten ueber `init(root, config)` + `destroy()` standardisieren. 1. MUST migrierte Komponenten über `init(root, config)` + `destroy()` standardisieren.
2. MUST NOT migrierte Komponenten per `DOMContentLoaded`/Module-Sideeffect selbst auto-initialisieren. 2. MUST NOT migrierte Komponenten per `DOMContentLoaded`/Module-Sideeffect selbst auto-initialisieren.
3. MUST Listener, Timer und Observer, die in `init(...)` entstehen, in `destroy()` wieder abbauen. 3. MUST Listener, Timer und Observer, die in `init(...)` entstehen, in `destroy()` wieder abbauen.
4. MUST Runtime-Konfiguration ueber Page-Config (`readPageConfig(...)`) beziehen, keine verteilten Ad-hoc-Globals. 4. MUST Runtime-Konfiguration über Page-Config (`readPageConfig(...)`) beziehen, keine verteilten Ad-hoc-Globals.
5. MUST host-gebundene UI-Komponenten ueber explizite `data-app-component`-Hosts im Markup binden. 5. MUST host-gebundene UI-Komponenten über explizite `data-app-component`-Hosts im Markup binden.
6. Global-Utilities ohne natuerliches Host-Element MAY via Runtime `scope: 'global'` registriert werden, MUESSEN aber denselben Lifecycle-Contract inkl. `destroy()` einhalten. 6. Global-Utilities ohne natürliches Host-Element MAY via Runtime `scope: 'global'` registriert werden, MÜSSEN aber denselben Lifecycle-Contract inkl. `destroy()` einhalten.
## Shared Partials ## Shared Partials
1. MUST Listen-Titlebar ueber `templates/partials/app-list-titlebar.phtml` rendern. 1. MUST Listen-Titlebar über `templates/partials/app-list-titlebar.phtml` rendern.
2. MUST List-Tabs ueber `templates/partials/app-list-tabs.phtml` rendern. 2. MUST List-Tabs über `templates/partials/app-list-tabs.phtml` rendern.
3. MUST Purge-Action ueber `templates/partials/app-list-purge-action.phtml` rendern. 3. MUST Purge-Action über `templates/partials/app-list-purge-action.phtml` rendern.
4. MUST Detail-Validation-Summary ueber `templates/partials/app-details-validation-summary.phtml` rendern. 4. MUST Detail-Validation-Summary über `templates/partials/app-details-validation-summary.phtml` rendern.
## RBAC in Templates ## RBAC in Templates
1. MUST UI-Capabilities nur ueber `$viewAuth` lesen. 1. MUST UI-Capabilities nur über `$viewAuth` lesen.
2. MUST NOT `can('...')` in Templates verwenden. 2. MUST NOT `can('...')` in Templates verwenden.
## CSS Boundaries ## CSS Boundaries
1. MUST Styles moeglichst lokal in `web/css/components`, `web/css/layout` oder `web/css/pages` halten. 1. MUST Styles möglichst lokal in `web/css/components`, `web/css/layout` oder `web/css/pages` halten.
2. MUST NOT globale unscoped Overrides ohne klaren Scope einfuehren. 2. MUST NOT globale unscoped Overrides ohne klaren Scope einführen.
3. MUST Vendor-Overrides nur in `web/css/vendor-overrides/**` pflegen. 3. MUST Vendor-Overrides nur in `web/css/vendor-overrides/**` pflegen.
4. MUST bestehende CSS-Variablen bevorzugen statt neue Inline-Farbwerte zu verteilen. 4. MUST bestehende CSS-Variablen bevorzugen statt neue Inline-Farbwerte zu verteilen.
## Komponenten in Formularen ## Komponenten in Formularen
1. MUST Core-Button-Klassen (`secondary outline small`, `danger outline small`) verwenden statt Custom-Button-CSS. Custom-CSS nur fuer Layout (z.B. `border-radius: 50%`). 1. MUST Core-Button-Klassen (`secondary outline small`, `danger outline small`) verwenden statt Custom-Button-CSS. Custom-CSS nur für Layout (z.B. `border-radius: 50%`).
2. MUST `margin-bottom: 0` auf Buttons, Inputs, Selects und Labels innerhalb selbststaendiger Komponenten in Formularen resetten (Core setzt `margin-bottom: var(--app-spacing)` global). 2. MUST `margin-bottom: 0` auf Buttons, Inputs, Selects und Labels innerhalb selbststaendiger Komponenten in Formularen resetten (Core setzt `margin-bottom: var(--app-spacing)` global).
3. MUST Checkboxen in Flex-Containern mit expliziter Groesse (`width: 1.25em; height: 1.25em`) und `flex-shrink: 0` versehen (Core nutzt `appearance: none` mit Custom-Sizing). 3. MUST Checkboxen in Flex-Containern mit expliziter Groesse (`width: 1.25em; height: 1.25em`) und `flex-shrink: 0` versehen (Core nutzt `appearance: none` mit Custom-Sizing).
4. MUST `position: sticky` mit `top: calc(var(--app-topbar-height) + ...)` verwenden, nicht `top: 0` (Topbar ist sticky). 4. MUST `position: sticky` mit `top: calc(var(--app-topbar-height) + ...)` verwenden, nicht `top: 0` (Topbar ist sticky).
5. MUST Icon-Only-Buttons mit `data-tooltip` und `aria-label` versehen. 5. MUST Icon-Only-Buttons mit `data-tooltip` und `aria-label` versehen.
6. MUST bei `grid-template-columns` mit mehr als einer Spalte ein `@media`-Fallback auf Single-Column definieren. 6. MUST bei `grid-template-columns` mit mehr als einer Spalte ein `@media`-Fallback auf Single-Column definieren.
7. MUST bestehende `--app-*` CSS-Variablen fuer Farben verwenden, nie Farbwerte hardcoden. 7. MUST bestehende `--app-*` CSS-Variablen für Farben verwenden, nie Farbwerte hardcoden.

View File

@@ -29,18 +29,18 @@ Erwartung: jeweils `0`.
## JS-Scope ## JS-Scope
- Bei JS-Aenderungen Browser-Smoke mit DevTools-Console durchfuehren. - Bei JS-Änderungen Browser-Smoke mit DevTools-Console durchführen.
- Erwartung: keine neuen JS-Errors. - Erwartung: keine neuen JS-Errors.
## Docs-/Skills-Gates (schnell) ## Docs-/Skills-Gates (schnell)
1. `bin/docs-link-check.sh` 1. `bin/docs-link-check.sh`
- prueft Doku-Links in `README.md`, `docs/**`, `.agents/skills/**` und `.agents/**` - prüft Doku-Links in `README.md`, `docs/**`, `.agents/skills/**` und `.agents/**`
- `.agents/runs/**` ist standardmaessig ausgeschlossen (historische Artefakte) - `.agents/runs/**` ist standardmäßig ausgeschlossen (historische Artefakte)
2. `bin/docs-drift-check.sh` 2. `bin/docs-drift-check.sh`
- prueft bekannte Legacy-Drifts (entfernte Config-Pfade/Constants, obsolete Search-Marker) - prüft bekannte Legacy-Drifts (entfernte Config-Pfade/Constants, obsolete Search-Marker)
3. `bin/codex-skills-sync.sh --check` 3. `bin/codex-skills-sync.sh --check`
- prueft Drift zwischen Repo-Skills und `~/.codex/skills` - prüft Drift zwischen Repo-Skills und `~/.codex/skills`
## Berichtspflicht ## Berichtspflicht

View File

@@ -1,6 +1,6 @@
--- ---
name: starterkit-grid-standards name: starterkit-grid-standards
description: Standardisierungs-Skill fuer GridJS-Listen im Starterkit. Verwenden, wenn Listen neu gebaut oder refactored werden sollen (rowInteraction, pageSize, filter drawer, URL-state, accessibility). description: Standardisierungs-Skill für GridJS-Listen im Starterkit. Verwenden, wenn Listen neu gebaut oder refactored werden sollen (rowInteraction, pageSize, filter drawer, URL-state, accessibility).
--- ---
# Starterkit Grid Standards # Starterkit Grid Standards
@@ -18,18 +18,18 @@ GridJS-Listen projektweit einheitlich, zuganglich und wartbar halten.
## Verbindliche Regeln ## Verbindliche Regeln
1. MUST `initStandardListPage(...)` als Standard nutzen (nur begruendete Ausnahmen direkt mit `createServerGrid(...)`). 1. MUST `initStandardListPage(...)` als Standard nutzen (nur begruendete Ausnahmen direkt mit `createServerGrid(...)`).
2. MUST `rowDblClick.getUrl(...)` fuer zeilenbezogene Navigation setzen, wenn Row-Navigation vorgesehen ist. 2. MUST `rowDblClick.getUrl(...)` für zeilenbezogene Navigation setzen, wenn Row-Navigation vorgesehen ist.
3. MUST globale `rowInteraction`-Defaults respektieren (Action-Spalte, Enter, Doppelklick-Fallback). 3. MUST globale `rowInteraction`-Defaults respektieren (Action-Spalte, Enter, Doppelklick-Fallback).
4. MUST globale `pageSize`-Standards respektieren (`10/25/50/100`, Toolbar-Placement, URL > storage > default). 4. MUST globale `pageSize`-Standards respektieren (`10/25/50/100`, Toolbar-Placement, URL > storage > default).
5. MUST Filter-Drawer/Chips ueber den Standard-Contract nutzen, keine parallelen Custom-Patterns. 5. MUST Filter-Drawer/Chips über den Standard-Contract nutzen, keine parallelen Custom-Patterns.
6. MUST NOT pro Seite eigene Row-Action-Hacks bauen, wenn der Factory-Contract ausreicht. 6. MUST NOT pro Seite eigene Row-Action-Hacks bauen, wenn der Factory-Contract ausreicht.
## Vorgehen ## Vorgehen
1. Ist-Liste gegen `references/list-checklist.md` pruefen. 1. Ist-Liste gegen `references/list-checklist.md` prüfen.
2. Fehlende Standards zuerst zentral in der Factory loesen, danach nur minimale Seitenanpassungen. 2. Fehlende Standards zuerst zentral in der Factory loesen, danach nur minimale Seitenanpassungen.
3. UI/CSS nur als additive Erweiterung zu bestehenden Komponenten. 3. UI/CSS nur als additive Erweiterung zu bestehenden Komponenten.
4. Relevante Gates und Browser-Smoke (Keyboard + Console) ausfuehren. 4. Relevante Gates und Browser-Smoke (Keyboard + Console) ausführen.
## Referenzen ## Referenzen

View File

@@ -3,19 +3,19 @@
## Bootstrap ## Bootstrap
1. `initStandardListPage({ grid, filters, hooks })` verwendet. 1. `initStandardListPage({ grid, filters, hooks })` verwendet.
2. `filterSchema` aus Server-Config uebergeben. 2. `filterSchema` aus Server-Config übergeben.
3. `urlSync` bewusst gesetzt. 3. `urlSync` bewusst gesetzt.
## Interaction ## Interaction
1. `rowDblClick.getUrl(...)` liefert stabile Ziel-URL oder leer. 1. `rowDblClick.getUrl(...)` liefert stabile Ziel-URL oder leer.
2. Keine Doppel-Action-Spalte (Factory auto-action vs explizite actions). 2. Keine Doppel-Action-Spalte (Factory auto-action vs explizite actions).
3. Enter/Pointer/Focus fuer oeffnbare Rows funktionieren. 3. Enter/Pointer/Focus für öffnbare Rows funktionieren.
## Pagination + URL ## Pagination + URL
1. Page-size Optionen nur `10/25/50/100`. 1. Page-size Optionen nur `10/25/50/100`.
2. URL Sync pruefen (`page`, `order`, `dir`, `limit`). 2. URL Sync prüfen (`page`, `order`, `dir`, `limit`).
3. Default-Werte erzeugen saubere URL. 3. Default-Werte erzeugen saubere URL.
## Filter UX ## Filter UX
@@ -26,5 +26,5 @@
## Regression ## Regression
1. Sortierung, Selection/Bulk, Doppelklick unveraendert. 1. Sortierung, Selection/Bulk, Doppelklick unverändert.
2. Keine neuen Console Errors. 2. Keine neuen Console Errors.

View File

@@ -15,6 +15,6 @@
## Verhalten ## Verhalten
1. Aenderung der Seitengroesse setzt Seite auf 1. 1. Änderung der Seitengroesse setzt Seite auf 1.
2. Sortierung/Filter bleiben erhalten. 2. Sortierung/Filter bleiben erhalten.
3. LocalStorage-Key pro Tabelle (path + dataUrl + container). 3. LocalStorage-Key pro Tabelle (path + dataUrl + container).

View File

@@ -8,7 +8,7 @@ Row-Navigation discoverable und keyboard-freundlich machen.
1. Konfigurierbares Link-Column: Die primaere Datenspalte (z.B. Name, Beschreibung) wird als klickbarer `<a>`-Link gerendert. 1. Konfigurierbares Link-Column: Die primaere Datenspalte (z.B. Name, Beschreibung) wird als klickbarer `<a>`-Link gerendert.
2. Enter auf fokussierter Row loest dieselbe Navigation aus. 2. Enter auf fokussierter Row loest dieselbe Navigation aus.
3. Doppelklick bleibt als Fallback fuer Power-User. 3. Doppelklick bleibt als Fallback für Power-User.
4. Nur Rows mit Ziel-URL erhalten `data-row-open-url` und den Link im Link-Column. 4. Nur Rows mit Ziel-URL erhalten `data-row-open-url` und den Link im Link-Column.
5. Rows ohne URL (z.B. fehlende Berechtigung) zeigen den Inhalt als Plain-Text ohne Link. 5. Rows ohne URL (z.B. fehlende Berechtigung) zeigen den Inhalt als Plain-Text ohne Link.
@@ -40,6 +40,6 @@ rowInteraction: {
## Guardrails ## Guardrails
1. Keine globale Tab-Stop-Flut auf allen Rows. 1. Keine globale Tab-Stop-Flut auf allen Rows.
2. Fokusstil fuer Row und Link-Element sichtbar. 2. Fokusstil für Row und Link-Element sichtbar.
3. Explizite `actions.enabled: true` pro Seite darf Link-Column uebersteuern (eigene Action-Spalte). 3. Explizite `actions.enabled: true` pro Seite darf Link-Column übersteuern (eigene Action-Spalte).
4. Link-Column darf keine Spalte mit verschachtelten `<a>`-Elementen umwickeln (z.B. Avatar mit Lightbox). 4. Link-Column darf keine Spalte mit verschachtelten `<a>`-Elementen umwickeln (z.B. Avatar mit Lightbox).

View File

@@ -1,24 +1,24 @@
--- ---
name: starterkit-php-style-ci name: starterkit-php-style-ci
description: Standard-Skill fuer PHP Coding-Style im Starterkit (php-cs-fixer, dry-run in CI, lokaler fix/check workflow). Verwenden bei Style-Einfuehrung, CI-Checks oder grossen Format-Diffs. description: Standard-Skill für PHP Coding-Style im Starterkit (php-cs-fixer, dry-run in CI, lokaler fix/check workflow). Verwenden bei Style-Einführung, CI-Checks oder grossen Format-Diffs.
--- ---
# Starterkit PHP Style CI # Starterkit PHP Style CI
## Ziel ## Ziel
Coding-Style pruefbar, reproduzierbar und regressionsarm machen. Coding-Style prüfbar, reproduzierbar und regressionsarm machen.
## Wann verwenden ## Wann verwenden
1. Einfuehrung oder Nachschaerfung von Style-Regeln. 1. Einführung oder Nachschaerfung von Style-Regeln.
2. CI-Dry-Run fuer Style-Checks. 2. CI-Dry-Run für Style-Checks.
3. Aufraeumen grosser Style-Diff-Wellen. 3. Aufraeumen grosser Style-Diff-Wellen.
## Verbindliche Regeln ## Verbindliche Regeln
1. MUST `vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose` fuer nicht-mutierende Pruefung nutzen (Convenience-Alias: `composer cs:check`). 1. MUST `vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose` für nicht-mutierende Prüfung nutzen (Convenience-Alias: `composer cs:check`).
2. MUST `composer cs:fix` nur bewusst und mit Diff-Kontrolle ausfuehren. 2. MUST `composer cs:fix` nur bewusst und mit Diff-Kontrolle ausführen.
3. MUST Style-Check vor Merge gruen halten (lokal und optional CI). 3. MUST Style-Check vor Merge gruen halten (lokal und optional CI).
4. MUST nach groesseren Fixes mindestens `phpunit` und `phpstan` laufen lassen. 4. MUST nach groesseren Fixes mindestens `phpunit` und `phpstan` laufen lassen.
5. MUST NOT manuell Einzelstellen formatieren, wenn der Fixer sie zentral loesen kann. 5. MUST NOT manuell Einzelstellen formatieren, wenn der Fixer sie zentral loesen kann.
@@ -26,8 +26,8 @@ Coding-Style pruefbar, reproduzierbar und regressionsarm machen.
## Workflow ## Workflow
1. Baseline erfassen: `vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose`. 1. Baseline erfassen: `vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose`.
2. Falls noetig fixen: `composer cs:fix`. 2. Falls nötig fixen: `composer cs:fix`.
3. Diff fokussiert pruefen (Imports, Braces, Nebenwirkungen). 3. Diff fokussiert prüfen (Imports, Braces, Nebenwirkungen).
4. Qualitaetsgates laufen lassen. 4. Qualitaetsgates laufen lassen.
5. CI-Dry-Run konfigurieren oder validieren. 5. CI-Dry-Run konfigurieren oder validieren.

View File

@@ -2,7 +2,7 @@
## Ziel ## Ziel
CI soll nur pruefen, nicht formatieren. CI soll nur prüfen, nicht formatieren.
## Minimaler Check ## Minimaler Check
@@ -13,7 +13,7 @@ vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php -
## Erwartung ## Erwartung
1. Exit Code 0: Style konform. 1. Exit Code 0: Style konform.
2. Exit Code != 0: Diff ausgeben, Entwickler fuehrt lokal `composer cs:fix` aus. 2. Exit Code != 0: Diff ausgeben, Entwickler führt lokal `composer cs:fix` aus.
## Hinweise ## Hinweise

View File

@@ -11,7 +11,7 @@ composer cs:fix
1. Erst Style-Check laufen lassen (`vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose`). 1. Erst Style-Check laufen lassen (`vendor/bin/php-cs-fixer fix --config=tools/php-cs-fixer/.php-cs-fixer.dist.php --dry-run --diff --verbose`).
2. Danach `cs:fix`. 2. Danach `cs:fix`.
3. Nur relevante Diffs uebernehmen, grosse Misch-Diffs vermeiden. 3. Nur relevante Diffs übernehmen, grosse Misch-Diffs vermeiden.
4. Anschliessend: 4. Anschliessend:
```bash ```bash

View File

@@ -1,13 +1,13 @@
--- ---
name: starterkit-planner name: starterkit-planner
description: Planungs-Skill fuer Architektur-, Rollout-, Refactor- und Standardisierungsaufgaben im Starterkit. Verwenden, wenn ein decision-complete Plan mit klaren Interfaces, Tests, Risiken und Annahmen benoetigt wird. description: Planungs-Skill für Architektur-, Rollout-, Refactor- und Standardisierungsaufgaben im Starterkit. Verwenden, wenn ein decision-complete Plan mit klaren Interfaces, Tests, Risiken und Annahmen benötigt wird.
--- ---
# Starterkit Planner # Starterkit Planner
## Ziel ## Ziel
Planungen so strukturieren, dass Implementierung ohne offene Entscheidungen moeglich ist. Planungen so strukturieren, dass Implementierung ohne offene Entscheidungen möglich ist.
Bei Agent-Workflows muss der Plan maschinenlesbar in die Projekt-Contracts passen. Bei Agent-Workflows muss der Plan maschinenlesbar in die Projekt-Contracts passen.
## Verbindliches Ausgabeformat ## Verbindliches Ausgabeformat
@@ -23,9 +23,9 @@ Bei Agent-Workflows muss der Plan maschinenlesbar in die Projekt-Contracts passe
Wenn `.agents/` verwendet wird: Wenn `.agents/` verwendet wird:
1. Plan muss dem Schema `.agents/contracts/planner.schema.json` entsprechen. 1. Plan muss dem Schema `.agents/contracts/planner.schema.json` entsprechen.
2. Success Criteria muessen stabile IDs tragen (`SC-001`, `SC-002`, ...). 2. Success Criteria müssen stabile IDs tragen (`SC-001`, `SC-002`, ...).
3. Success Criteria muessen so formuliert sein, dass Reviewer Acceptance sie direkt gegen `criterion_id` pruefen kann. 3. Success Criteria müssen so formuliert sein, dass Reviewer Acceptance sie direkt gegen `criterion_id` prüfen kann.
4. Guard- und Quality-Gate-IDs muessen aus den Katalogen kommen (`GR-*`, `QG-*`). 4. Guard- und Quality-Gate-IDs müssen aus den Katalogen kommen (`GR-*`, `QG-*`).
## Vorgehen ## Vorgehen
@@ -33,7 +33,7 @@ Wenn `.agents/` verwendet wird:
2. Entscheidungen mit Tradeoffs transparent machen. 2. Entscheidungen mit Tradeoffs transparent machen.
3. Oeffentliche Contracts explizit benennen. 3. Oeffentliche Contracts explizit benennen.
4. Success Criteria frueh mit stabilen IDs (`SC-*`) definieren. 4. Success Criteria frueh mit stabilen IDs (`SC-*`) definieren.
5. Reihenfolge und Rollout so planen, dass Rueckbau moeglich bleibt. 5. Reihenfolge und Rollout so planen, dass Rueckbau möglich bleibt.
6. Teststrategie nach Risiko priorisieren. 6. Teststrategie nach Risiko priorisieren.
## Qualitaetskriterien ## Qualitaetskriterien
@@ -42,7 +42,7 @@ Wenn `.agents/` verwendet wird:
2. Jeder betroffene Bereich hat klare Akzeptanzkriterien mit stabiler ID (`SC-*`). 2. Jeder betroffene Bereich hat klare Akzeptanzkriterien mit stabiler ID (`SC-*`).
3. Risiken sind priorisiert und beobachtbar. 3. Risiken sind priorisiert und beobachtbar.
4. Ungeklaerte Themen stehen explizit unter Annahmen. 4. Ungeklaerte Themen stehen explizit unter Annahmen.
5. Guard-/Gate-Referenzen sind vollstaendig und pruefbar (`GR-*`, `QG-*`). 5. Guard-/Gate-Referenzen sind vollstaendig und prüfbar (`GR-*`, `QG-*`).
## Out of Scope ## Out of Scope

View File

@@ -19,7 +19,7 @@
### Option B: Shared Partial/Helper ### Option B: Shared Partial/Helper
- Vorteile: Schneller Nutzen, moderates Risiko - Vorteile: Schneller Nutzen, moderates Risiko
- Nachteile: Teilweise Restduplikate moeglich - Nachteile: Teilweise Restduplikate möglich
- Geeignet wenn: Wiederkehrendes UI-/Glue-Muster mit klaren Datencontracts - Geeignet wenn: Wiederkehrendes UI-/Glue-Muster mit klaren Datencontracts
### Option C: Lokal belassen ### Option C: Lokal belassen

View File

@@ -13,7 +13,7 @@ docker compose exec php php bin/console module:sync # 3. Modul-Migrationen + A
App: <http://localhost:8080> · phpMyAdmin: <http://localhost:8081> App: <http://localhost:8080> · phpMyAdmin: <http://localhost:8081>
> `APP_CRYPTO_KEY` aus `.env.example` nicht aendern — sonst lassen sich die geseeded Microsoft-SSO- und BC-Test-Secrets nicht entschluesseln. > `APP_CRYPTO_KEY` aus `.env.example` nicht ändern — sonst lassen sich die geseeded Microsoft-SSO- und BC-Test-Secrets nicht entschlüsseln.
### Login ### Login
@@ -28,7 +28,7 @@ Alle Demo-User haben das Passwort `Demo123`:
| `hilde@user.com` | Support | | `hilde@user.com` | Support |
Plus der Live-User `fs@breadcrumb-solutions.de` (alle Rollen, Passwort nur per Reset). Plus der Live-User `fs@breadcrumb-solutions.de` (alle Rollen, Passwort nur per Reset).
Microsoft SSO ist fuer den Tenant `breadcrumb mediasolutions GmbH` vorkonfiguriert (Domain `breadcrumb-solutions.de`). Microsoft SSO ist für den Tenant `breadcrumb mediasolutions GmbH` vorkonfiguriert (Domain `breadcrumb-solutions.de`).
## Quality Gates ## Quality Gates

262
bin/fix-md-umlauts.py Executable file
View File

@@ -0,0 +1,262 @@
#!/usr/bin/env python3
"""
Replace ASCII-fallback German umlauts (ue, ae, oe, ss) with proper umlauts
inside .md prose, while preserving:
- fenced code blocks (``` ... ```)
- inline code spans (` ... `)
- markdown link targets ([label](path)) — only the (path) part is preserved
- HTML comments (<!-- ... -->)
Usage: python3 bin/fix-md-umlauts.py <file.md> [<file.md> ...]
--dry-run print proposed changes per file (default: write in place)
--check exit 1 if any file would change (CI mode)
"""
from __future__ import annotations
import re
import sys
from pathlib import Path
# Order matters: longer patterns first to prevent partial matches
# (e.g. "aenderung" must be tried before "aender" so prose-ending stays clean).
SUBSTITUTIONS: list[tuple[str, str]] = [
# ß / ss
("massgeblich", "maßgeblich"),
("Massgeblich", "Maßgeblich"),
("gemaess", "gemäß"),
("Gemaess", "Gemäß"),
("regelmaessig", "regelmäßig"),
("Regelmaessig", "Regelmäßig"),
("groesst", "größt"),
("Groesst", "Größt"),
# ä longer first
("aenderung", "änderung"),
("Aenderung", "Änderung"),
("aenderbar", "änderbar"),
("aendert", "ändert"),
("aendern", "ändern"),
("Aender", "Änder"),
("aender", "änder"),
("naechst", "nächst"),
("Naechst", "Nächst"),
("naemlich", "nämlich"),
("Naemlich", "Nämlich"),
("waehrend", "während"),
("Waehrend", "Während"),
("waehl", "wähl"),
("Waehl", "Wähl"),
("haeufig", "häufig"),
("Haeufig", "Häufig"),
("spaeter", "später"),
("Spaeter", "Später"),
("aufzaehl", "aufzähl"),
("Aufzaehl", "Aufzähl"),
("gefaehr", "gefähr"),
("Gefaehr", "Gefähr"),
("erlaeut", "erläut"),
("Erlaeut", "Erläut"),
("bestaetig", "bestätig"),
("Bestaetig", "Bestätig"),
("Geschaeft", "Geschäft"),
("geschaeft", "geschäft"),
# ü longer first
("ausfuehr", "ausführ"),
("Ausfuehr", "Ausführ"),
("durchfuehr", "durchführ"),
("Durchfuehr", "Durchführ"),
("einfuehr", "einführ"),
("Einfuehr", "Einführ"),
("fuehrun", "führun"),
("Fuehrun", "Führun"),
("fuehrt", "führt"),
("fuehren", "führen"),
("Fuehren", "Führen"),
("verfuegb", "verfügb"),
("Verfuegb", "Verfügb"),
("unterstuetz", "unterstütz"),
("Unterstuetz", "Unterstütz"),
("nuetzlich", "nützlich"),
("Nuetzlich", "Nützlich"),
("Schluess", "Schlüss"),
("schluess", "schlüss"),
("wuerde", "würde"),
("Wuerde", "Würde"),
("wuerden", "würden"),
("MUESSEN", "MÜSSEN"),
("muessen", "müssen"),
("Muessen", "Müssen"),
("muess", "müss"),
("Muess", "Müss"),
("KOENNEN", "KÖNNEN"),
("koennen", "können"),
("Koennen", "Können"),
("koenn", "könn"),
("Koenn", "Könn"),
("natuerli", "natürli"),
("Natuerli", "Natürli"),
("tatsaechli", "tatsächli"),
("Tatsaechli", "Tatsächli"),
("aehnlich", "ähnlich"),
("Aehnlich", "Ähnlich"),
("standardmaessig", "standardmäßig"),
("Standardmaessig", "Standardmäßig"),
("gleichmaessig", "gleichmäßig"),
("Gleichmaessig", "Gleichmäßig"),
("zugaengli", "zugängli"),
("Zugaengli", "Zugängli"),
("uebersicht", "übersicht"),
("Uebersicht", "Übersicht"),
("uebersetzun", "übersetzun"),
("Uebersetzun", "Übersetzun"),
("uebernahme", "übernahme"),
("Uebernahme", "Übernahme"),
("uebernehm", "übernehm"),
("Uebernehm", "Übernehm"),
("hoeher", "höher"),
("Hoeher", "Höher"),
("pruefen", "prüfen"),
("Pruefen", "Prüfen"),
("prueft", "prüft"),
("Prueft", "Prüft"),
("Pruefung", "Prüfung"),
("pruefung", "prüfung"),
("pruef", "prüf"),
("Pruef", "Prüf"),
("ueber", "über"),
("Ueber", "Über"),
("fuer", "für"),
# ö
("oeffn", "öffn"),
("Oeffn", "Öffn"),
("loescht", "löscht"),
("Loescht", "Löscht"),
("loeschen", "löschen"),
("Loeschen", "Löschen"),
("loesung", "lösung"),
("Loesung", "Lösung"),
("moegli", "mögli"),
("Moegli", "Mögli"),
("moeglich", "möglich"),
("Moeglich", "Möglich"),
("noetig", "nötig"),
("Noetig", "Nötig"),
("hoer", "hör"), # gehör, höher
("Hoer", "Hör"),
]
# Regex that splits a line into "preserve" tokens and replaceable text.
# Order matters: longer / more specific tokens first.
TOKENIZER = re.compile(
r"""
( `[^`\n]*` ) # inline code: `...`
| ( !?\[[^\]]*\]\([^)]*\) ) # full markdown link [label](target)
| ( <!--.*?--> ) # HTML comment
""",
re.VERBOSE | re.DOTALL,
)
def transform_text(text: str) -> str:
"""Apply substitutions to non-preserved spans only."""
for needle, replacement in SUBSTITUTIONS:
text = text.replace(needle, replacement)
return text
def transform_line(line: str) -> str:
"""Tokenise a line into [preserved | text | preserved | text | ...] and replace only text segments."""
out: list[str] = []
pos = 0
for match in TOKENIZER.finditer(line):
# Replaceable run before the preserved span
if match.start() > pos:
out.append(transform_text(line[pos : match.start()]))
out.append(match.group(0)) # keep preserved span verbatim
pos = match.end()
# Tail after last preserved span
if pos < len(line):
out.append(transform_text(line[pos:]))
return "".join(out)
def transform_file(content: str) -> str:
"""Walk lines, skip fenced code blocks, transform the rest line-by-line."""
out_lines: list[str] = []
in_fence = False
fence_marker = ""
for line in content.splitlines(keepends=True):
stripped = line.lstrip()
if in_fence:
out_lines.append(line)
if stripped.startswith(fence_marker):
in_fence = False
fence_marker = ""
continue
# Detect fenced code start (``` or ~~~)
if stripped.startswith("```"):
in_fence = True
fence_marker = "```"
out_lines.append(line)
continue
if stripped.startswith("~~~"):
in_fence = True
fence_marker = "~~~"
out_lines.append(line)
continue
out_lines.append(transform_line(line))
return "".join(out_lines)
def main(argv: list[str]) -> int:
args = list(argv[1:])
dry_run = False
check = False
while args and args[0].startswith("--"):
flag = args.pop(0)
if flag == "--dry-run":
dry_run = True
elif flag == "--check":
check = True
elif flag == "--":
break
else:
print(f"Unknown flag: {flag}", file=sys.stderr)
return 2
if not args:
print(__doc__.strip(), file=sys.stderr)
return 2
changed_files: list[Path] = []
for raw in args:
path = Path(raw)
if not path.is_file():
print(f"Skipping (not a file): {path}", file=sys.stderr)
continue
original = path.read_text(encoding="utf-8")
transformed = transform_file(original)
if transformed != original:
changed_files.append(path)
if dry_run or check:
# Brief summary; full diff is what `git diff` shows after a real run.
added = sum(1 for c in transformed if c in "äöüÄÖÜß") - sum(
1 for c in original if c in "äöüÄÖÜß"
)
print(f"WOULD CHANGE {path} (+{added} umlauts)")
else:
path.write_text(transformed, encoding="utf-8")
print(f"updated {path}")
if check and changed_files:
return 1
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))

View File

@@ -115,7 +115,7 @@ flowchart TD
- In `pages/**` und `templates/**` sind inline `<script type="module">...</script>` ohne `src` verboten. - In `pages/**` und `templates/**` sind inline `<script type="module">...</script>` ohne `src` verboten.
- Seiteninitialisierung liegt in dedizierten ESM-Page-Modulen unter `web/js/pages/*`. - Seiteninitialisierung liegt in dedizierten ESM-Page-Modulen unter `web/js/pages/*`.
- Pro Seite wird Konfiguration per `<script type="application/json" id="page-config-<page-key>">...</script>` bereitgestellt und im Modul über `readPageConfig(...)` gelesen. - Pro Seite wird Konfiguration per `<script type="application/json" id="page-config-<page-key>">...</script>` bereitgestellt und im Modul über `readPageConfig(...)` gelesen.
- In `pages/**` und `templates/**` muessen alle JS-`src` (eigene und Vendor) ueber `assetVersion('...js...')` laufen. - In `pages/**` und `templates/**` müssen alle JS-`src` (eigene und Vendor) über `assetVersion('...js...')` laufen.
## Mindestchecks vor Merge ## Mindestchecks vor Merge

View File

@@ -103,6 +103,6 @@ $meinService = app(\MintyPHP\Service\MeinNeuerService::class);
den konkreten Service direkt registrieren und per `app()` holen. den konkreten Service direkt registrieren und per `app()` holen.
- Der Container lebt **pro HTTP-Anfrage** — er wird in `web/index.php` initialisiert - Der Container lebt **pro HTTP-Anfrage** — er wird in `web/index.php` initialisiert
und über `setAppContainer()` im globalen State abgelegt. und über `setAppContainer()` im globalen State abgelegt.
- In CLI-Commands wird der Container separat initialisiert (ueber die Runtime-Bootstrap-Klassen unter `core/Console/Support/`). - In CLI-Commands wird der Container separat initialisiert (über die Runtime-Bootstrap-Klassen unter `core/Console/Support/`).
- In `core/Service/**` außerhalb von `*Factory.php` niemals `new ...Factory()`, - In `core/Service/**` außerhalb von `*Factory.php` niemals `new ...Factory()`,
`new ...Service()` oder `new ...Repository()` direkt instanziieren. `new ...Service()` oder `new ...Repository()` direkt instanziieren.

View File

@@ -53,7 +53,7 @@ Das ist oft erwartbar, solange es um nicht-gecachte Keys geht.
Prüfregel: Prüfregel:
- Geht es um `appSetting(...)`-Keys? Dann Datei-Cache relevant. - Geht es um `appSetting(...)`-Keys? Dann Datei-Cache relevant.
- Geht es um SMTP/SSO/API-Details? Dann DB ist massgeblich. - Geht es um SMTP/SSO/API-Details? Dann DB ist maßgeblich.
## Betriebs-Hinweis ## Betriebs-Hinweis

View File

@@ -6,7 +6,7 @@ Letzte Aktualisierung: 2026-03-14
Vollstaendige Erklaerung des Session-Lifecycle: Wann wird ein User abgemeldet, wann greift der Remember-Token, wie bleiben Frontend und Server synchron. Vollstaendige Erklaerung des Session-Lifecycle: Wann wird ein User abgemeldet, wann greift der Remember-Token, wie bleiben Frontend und Server synchron.
## Session-Lifecycle (Uebersicht) ## Session-Lifecycle (Übersicht)
``` ```
Login (Passwort / SSO) Login (Passwort / SSO)
@@ -35,7 +35,7 @@ Jeder Request (web/index.php)
| Idle | 30 Min | 5 Min 24 Std | Ja, bei jeder Aktivitaet | `session_idle_timeout_minutes` | | Idle | 30 Min | 5 Min 24 Std | Ja, bei jeder Aktivitaet | `session_idle_timeout_minutes` |
| Absolute | 8 Std | 1 Std 72 Std | Nein, nie | `session_absolute_timeout_hours` | | Absolute | 8 Std | 1 Std 72 Std | Nein, nie | `session_absolute_timeout_hours` |
Pruefung in `web/index.php` (bei jedem Request): Prüfung in `web/index.php` (bei jedem Request):
``` ```
Idle: (now - last_activity) > idle_timeout → Timeout Idle: (now - last_activity) > idle_timeout → Timeout
@@ -67,7 +67,7 @@ Quelle: `web/js/components/app-session-warning.js`
Problem ohne Keepalive: User scrollt 29 Min auf einer Seite → JS zeigt "aktiv", Server sagt "idle". Problem ohne Keepalive: User scrollt 29 Min auf einer Seite → JS zeigt "aktiv", Server sagt "idle".
Loesung: Alle 5 Minuten prueft ein Intervall ob seit dem letzten Sync Interaktion stattfand. Falls ja → `POST /admin/session-ping/data` → Server-Timer zurueckgesetzt. Lösung: Alle 5 Minuten prüft ein Intervall ob seit dem letzten Sync Interaktion stattfand. Falls ja → `POST /admin/session-ping/data` → Server-Timer zurueckgesetzt.
| Szenario | Keepalive-Ping? | | Szenario | Keepalive-Ping? |
|----------|-----------------| |----------|-----------------|
@@ -100,12 +100,12 @@ DB: user_remember_tokens
└─ last_used └─ last_used
``` ```
Selector und Token sind getrennt: Selector fuer schnellen DB-Lookup, Token-Hash fuer timing-sichere Validierung. Kein Klartext-Token in der DB. Selector und Token sind getrennt: Selector für schnellen DB-Lookup, Token-Hash für timing-sichere Validierung. Kein Klartext-Token in der DB.
### Auto-Login-Ablauf ### Auto-Login-Ablauf
1. Kein aktiver Session-User → Remember-Cookie lesen 1. Kein aktiver Session-User → Remember-Cookie lesen
2. Rate-Limit pruefen (IP nicht blockiert?) 2. Rate-Limit prüfen (IP nicht blockiert?)
3. Selector → DB-Lookup 3. Selector → DB-Lookup
4. Token → `hash_equals(SHA256)` Verify 4. Token → `hash_equals(SHA256)` Verify
5. User aktiv? Token nicht abgelaufen? 5. User aktiv? Token nicht abgelaufen?
@@ -125,7 +125,7 @@ Quelle: `core/Service/Auth/RememberMeRateLimiter.php`
- Fail-open: Bei Memcached-Ausfall wird nicht blockiert - Fail-open: Bei Memcached-Ausfall wird nicht blockiert
- Erfolgreicher Auto-Login setzt Counter zurueck - Erfolgreicher Auto-Login setzt Counter zurueck
Siehe `/docs/howto-anfragelimits.md` fuer alle Rate-Limits. Siehe `/docs/howto-anfragelimits.md` für alle Rate-Limits.
## Zusammenspiel Absolute Timeout + Remember-Token ## Zusammenspiel Absolute Timeout + Remember-Token
@@ -137,7 +137,7 @@ Login → 8h Absolute Timeout → Session endet
└─ NEIN → Login-Seite └─ NEIN → Login-Seite
``` ```
Solange der Remember-Token lebt (Default 30 Tage), bekommt der User nach Ablauf des Absolute-Timeout automatisch eine neue Session. Der Uebergang ist nahtlos (kein Flash, kein Login-Screen). Solange der Remember-Token lebt (Default 30 Tage), bekommt der User nach Ablauf des Absolute-Timeout automatisch eine neue Session. Der Übergang ist nahtlos (kein Flash, kein Login-Screen).
Das bedeutet: Mit aktivem Remember-Token ist die effektive Session-Dauer nicht 8 Stunden, sondern bis zu 30 Tage. Das ist by-design, sollte aber bei Compliance-Anforderungen (z. B. echte Re-Authentifizierung nach X Stunden) beruecksichtigt werden. Das bedeutet: Mit aktivem Remember-Token ist die effektive Session-Dauer nicht 8 Stunden, sondern bis zu 30 Tage. Das ist by-design, sollte aber bei Compliance-Anforderungen (z. B. echte Re-Authentifizierung nach X Stunden) beruecksichtigt werden.
@@ -148,9 +148,9 @@ Das bedeutet: Mit aktivem Remember-Token ist die effektive Session-Dauer nicht 8
| `httponly` | `true` | Kein JS-Zugriff (XSS-Schutz) | | `httponly` | `true` | Kein JS-Zugriff (XSS-Schutz) |
| `samesite` | `Lax` | CSRF-Schutz bei Cross-Site-Navigation | | `samesite` | `Lax` | CSRF-Schutz bei Cross-Site-Navigation |
| `secure` | dynamisch | `true` bei HTTPS, `false` bei HTTP (Dev) | | `secure` | dynamisch | `true` bei HTTPS, `false` bei HTTP (Dev) |
| `path` | `/` | Gueltig fuer gesamte Domain | | `path` | `/` | Gueltig für gesamte Domain |
Das Secure-Flag wird automatisch anhand des Protokolls gesetzt (`RequestRuntime::isSecure()`). In Produktion muss HTTPS ueber Nginx erzwungen werden (Redirect 80→443 + HSTS). Das Secure-Flag wird automatisch anhand des Protokolls gesetzt (`RequestRuntime::isSecure()`). In Produktion muss HTTPS über Nginx erzwungen werden (Redirect 80→443 + HSTS).
## Audit-Events ## Audit-Events
@@ -165,7 +165,7 @@ Das Secure-Flag wird automatisch anhand des Protokolls gesetzt (`RequestRuntime:
| Komponente | Pfad | | Komponente | Pfad |
|------------|------| |------------|------|
| Timeout-Pruefung | `web/index.php` | | Timeout-Prüfung | `web/index.php` |
| AuthService | `core/Service/Auth/AuthService.php` | | AuthService | `core/Service/Auth/AuthService.php` |
| RememberMeService | `core/Service/Auth/RememberMeService.php` | | RememberMeService | `core/Service/Auth/RememberMeService.php` |
| RememberMeRateLimiter | `core/Service/Auth/RememberMeRateLimiter.php` | | RememberMeRateLimiter | `core/Service/Auth/RememberMeRateLimiter.php` |

View File

@@ -78,4 +78,4 @@ Unterschied zu den DB-basierten Limits oben: Memcached statt `request_rate_limit
1. API ohne Token >120 Requests/min gegen `/api/v1/*` -> `429` erwartet. 1. API ohne Token >120 Requests/min gegen `/api/v1/*` -> `429` erwartet.
2. Web-Login mit falschem Passwort >5 pro E-Mail+IP -> `429` erwartet. 2. Web-Login mit falschem Passwort >5 pro E-Mail+IP -> `429` erwartet.
3. API-Login mit falschen Credentials wiederholt -> `429` erwartet. 3. API-Login mit falschen Credentials wiederholt -> `429` erwartet.
4. Remember-Cookie mit falschem Token >5 pro IP -> Auto-Login blockiert, Cookie geloescht. 4. Remember-Cookie mit falschem Token >5 pro IP -> Auto-Login blockiert, Cookie gelöscht.

View File

@@ -4,9 +4,9 @@ Letzte Aktualisierung: 2026-03-19
## Ziel ## Ziel
`php bin/console doctor` ist der schnelle Gesundheitscheck fuer eine Instanz. `php bin/console doctor` ist der schnelle Gesundheitscheck für eine Instanz.
Er prueft unter anderem: Er prüft unter anderem:
- ENV-Validierung (Pflichtkeys + Formate) - ENV-Validierung (Pflichtkeys + Formate)
- AppContainer-Bootstrap - AppContainer-Bootstrap
@@ -35,13 +35,13 @@ docker compose exec php php bin/console doctor
- `0`: keine harten Fehler - `0`: keine harten Fehler
- `1`: mindestens ein `FAIL`-Check - `1`: mindestens ein `FAIL`-Check
`WARN` fuehrt nicht zu Exit `1`, sollte aber geprueft werden. `WARN` führt nicht zu Exit `1`, sollte aber geprüft werden.
## Typischer Einsatz im Betrieb ## Typischer Einsatz im Betrieb
1. Direkt nach Neuinstallation / Deployment. 1. Direkt nach Neuinstallation / Deployment.
2. Bei 4xx/5xx-Symptomen als erster Schritt. 2. Bei 4xx/5xx-Symptomen als erster Schritt.
3. Nach Konfigurations- oder RBAC-Aenderungen. 3. Nach Konfigurations- oder RBAC-Änderungen.
## Beispielausgabe ## Beispielausgabe

View File

@@ -16,8 +16,8 @@ Neue Seiten schnell, konsistent und langfristig wartbar ergänzen.
Die Dokumentation folgt dem Diataxis-Modell mit genau vier Quadranten in dieser Reihenfolge: Die Dokumentation folgt dem Diataxis-Modell mit genau vier Quadranten in dieser Reihenfolge:
1. **Tutorials** — Lernorientierte Schritt-fuer-Schritt-Anleitungen 1. **Tutorials** — Lernorientierte Schritt-für-Schritt-Anleitungen
2. **How-to Guides** — Aufgabenorientierte Anleitungen fuer konkrete Ziele 2. **How-to Guides** — Aufgabenorientierte Anleitungen für konkrete Ziele
3. **Explanation** — Verstaendnisorientierte Hintergrunderklaerungen 3. **Explanation** — Verstaendnisorientierte Hintergrunderklaerungen
4. **Reference** — Informationsorientierte Fakten und Nachschlagewerke 4. **Reference** — Informationsorientierte Fakten und Nachschlagewerke

View File

@@ -4,7 +4,7 @@ Letzte Aktualisierung: 2026-04-01
## Ziel ## Ziel
QA-Pflichtchecks fuer CoreCore in Gitea vorbereiten, ohne sie sofort als Merge-Blocker zu aktivieren. QA-Pflichtchecks für CoreCore in Gitea vorbereiten, ohne sie sofort als Merge-Blocker zu aktivieren.
## Enthaltene Basis ## Enthaltene Basis
@@ -14,7 +14,7 @@ QA-Pflichtchecks fuer CoreCore in Gitea vorbereiten, ohne sie sofort als Merge-B
## Was `qa-required` abdeckt ## Was `qa-required` abdeckt
`bin/qa-required.sh` fuehrt die aktuell blockenden Gates aus: `bin/qa-required.sh` führt die aktuell blockenden Gates aus:
- `QG-001` PHPUnit - `QG-001` PHPUnit
- `QG-002` PHPStan - `QG-002` PHPStan
@@ -25,14 +25,14 @@ QA-Pflichtchecks fuer CoreCore in Gitea vorbereiten, ohne sie sofort als Merge-B
## Phase-1 Setup (noch nicht blockend) ## Phase-1 Setup (noch nicht blockend)
1. Sicherstellen, dass der Gitea-Runner Docker + Docker Compose ausfuehren darf. 1. Sicherstellen, dass der Gitea-Runner Docker + Docker Compose ausführen darf.
2. Workflow-Datei nach `.gitea/workflows/qa-required.yaml` uebernehmen. 2. Workflow-Datei nach `.gitea/workflows/qa-required.yaml` übernehmen.
3. PR gegen `main` oeffnen und pruefen, dass Job `qa-required` gruen laeuft. 3. PR gegen `main` öffnen und prüfen, dass Job `qa-required` gruen laeuft.
4. Branch Protection noch **nicht** auf "required" setzen. 4. Branch Protection noch **nicht** auf "required" setzen.
## Phase-2 Aktivierung (blockend) ## Phase-2 Aktivierung (blockend)
1. Branch-Protection fuer `main` aktivieren. 1. Branch-Protection für `main` aktivieren.
2. Statuscheck `qa-required` als required markieren. 2. Statuscheck `qa-required` als required markieren.
3. Optional: weitere Jobs ergaenzen (z. B. nightly/extended Checks), aber nur `qa-required` blockend setzen. 3. Optional: weitere Jobs ergaenzen (z. B. nightly/extended Checks), aber nur `qa-required` blockend setzen.
@@ -40,4 +40,4 @@ QA-Pflichtchecks fuer CoreCore in Gitea vorbereiten, ohne sie sofort als Merge-B
- `QG-005` (Browser-Smoke) bleibt manuell/non-blocking. - `QG-005` (Browser-Smoke) bleibt manuell/non-blocking.
- `QG-007` (composer-unused) bleibt periodisch/non-blocking. - `QG-007` (composer-unused) bleibt periodisch/non-blocking.
- Fuer lokale Ausfuehrung: `bin/dev qa-required` bzw. `bin/dev qa-extended`. - Fuer lokale Ausführung: `bin/dev qa-required` bzw. `bin/dev qa-extended`.

View File

@@ -47,7 +47,7 @@ Provider:
- `mapPreviewItem()` - `mapPreviewItem()`
- `mapResultItem()` - `mapResultItem()`
5. Bei Modul-Resources: Provider-Klasse in `module.php` unter `search_resources` registrieren. 5. Bei Modul-Resources: Provider-Klasse in `module.php` unter `search_resources` registrieren.
6. i18n-Labels pruefen. 6. i18n-Labels prüfen.
## Wichtige Regeln ## Wichtige Regeln

View File

@@ -12,7 +12,7 @@ Kompakter Tagesworkflow für Entwicklung, Tests und schema-nahe Änderungen.
bin/dev init bin/dev init
``` ```
`bin/dev init` ist nicht-destruktiv und fuehrt aus: `bin/dev init` ist nicht-destruktiv und führt aus:
1. `.env` aus `.env.example` erstellen (falls fehlend) 1. `.env` aus `.env.example` erstellen (falls fehlend)
2. `docker compose up --build -d` 2. `docker compose up --build -d`
@@ -36,17 +36,17 @@ docker compose restart php nginx
## Module-Sync ## Module-Sync
Nach Aenderungen an Modulen, Manifesten oder `APP_ENABLED_MODULES`: Nach Änderungen an Modulen, Manifesten oder `APP_ENABLED_MODULES`:
```bash ```bash
docker compose exec php php bin/console module:sync docker compose exec php php bin/console module:sync
``` ```
Dieser Befehl fuehrt automatisch aus: `migrate``permissions-sync``build``assets-sync`. Dieser Befehl führt automatisch aus: `migrate``permissions-sync``build``assets-sync`.
Falls `web/index.php` den Fehler "Module runtime is stale" wirft, ist der Sync noetig. Falls `web/index.php` den Fehler "Module runtime is stale" wirft, ist der Sync nötig.
Alle verfuegbaren CLI-Kommandos: `php bin/console list` (siehe `/docs/reference-cli-commands.md`). Alle verfügbaren CLI-Kommandos: `php bin/console list` (siehe `/docs/reference-cli-commands.md`).
Alle Dev-Orchestrator-Kommandos: `bin/dev --help`. Alle Dev-Orchestrator-Kommandos: `bin/dev --help`.
## Schema-Stand ## Schema-Stand
@@ -70,7 +70,7 @@ docker compose exec php vendor/bin/phpunit
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
``` ```
Bei Modul-/Manifest-Aenderungen zusaetzlich: Bei Modul-/Manifest-Änderungen zusaetzlich:
```bash ```bash
docker compose exec php php bin/console module:sync docker compose exec php php bin/console module:sync

View File

@@ -4,7 +4,7 @@ Letzte Aktualisierung: 2026-03-26
## Ziel ## Ziel
Schritt-fuer-Schritt Anleitung: vom leeren Verzeichnis zum lauffaehigen Modul mit Route, Permission, UI-Slot und Test. Schritt-für-Schritt Anleitung: vom leeren Verzeichnis zum lauffaehigen Modul mit Route, Permission, UI-Slot und Test.
## Voraussetzungen ## Voraussetzungen
@@ -54,7 +54,7 @@ return [
]; ];
``` ```
Alternative: `APP_ENABLED_MODULES` Umgebungsvariable (ueberschreibt die Datei). Alternative: `APP_ENABLED_MODULES` Umgebungsvariable (überschreibt die Datei).
## Schritt 3: Manifest konfigurieren ## Schritt 3: Manifest konfigurieren
@@ -138,7 +138,7 @@ final class MeinModulContainerRegistrar implements ContainerRegistrar
} }
``` ```
Ohne diese Registrierung wird die Policy von `ModuleClassResolver` ignoriert und die Permission-Pruefung fuer UI-Slots schlaegt fehl (Slot wird nicht angezeigt). Ohne diese Registrierung wird die Policy von `ModuleClassResolver` ignoriert und die Permission-Prüfung für UI-Slots schlaegt fehl (Slot wird nicht angezeigt).
## Schritt 5: Erste Seite anlegen ## Schritt 5: Erste Seite anlegen
@@ -171,14 +171,14 @@ Datei `modules/mein-modul/pages/mein-modul/index(default).phtml`:
docker compose exec php php bin/console module:sync docker compose exec php php bin/console module:sync
``` ```
Dieser Befehl fuehrt automatisch aus: Dieser Befehl führt automatisch aus:
1. `module:migrate` — SQL-Migrationen anwenden 1. `module:migrate` — SQL-Migrationen anwenden
2. `module:permissions-sync` — Permissions registrieren 2. `module:permissions-sync` — Permissions registrieren
3. `module:build` — Seiten-Symlinks erstellen 3. `module:build` — Seiten-Symlinks erstellen
4. `module:assets-sync` — CSS/JS-Assets veroeffentlichen 4. `module:assets-sync` — CSS/JS-Assets veroeffentlichen
Nach jedem Manifest-Aenderung erneut ausfuehren. Nach jedem Manifest-Änderung erneut ausführen.
## Schritt 7: Validieren ## Schritt 7: Validieren
@@ -186,7 +186,7 @@ Nach jedem Manifest-Aenderung erneut ausfuehren.
docker compose exec php php bin/console module:validate mein-modul docker compose exec php php bin/console module:validate mein-modul
``` ```
Prueft: Manifest-Syntax, Klassen-Existenz, Namespace-Konventionen, Verzeichnisstruktur. Prüft: Manifest-Syntax, Klassen-Existenz, Namespace-Konventionen, Verzeichnisstruktur.
## Schritt 8: Testen ## Schritt 8: Testen
@@ -195,7 +195,7 @@ docker compose exec php vendor/bin/phpunit --filter=MeinModul
docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon modules/mein-modul/ docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon modules/mein-modul/
``` ```
Architektur-Tests (pruefen automatisch Namespace-Isolation, Session-Key-Prefix, Manifest-Contract): Architektur-Tests (prüfen automatisch Namespace-Isolation, Session-Key-Prefix, Manifest-Contract):
```bash ```bash
docker compose exec php vendor/bin/phpunit tests/Architecture/ docker compose exec php vendor/bin/phpunit tests/Architecture/
@@ -203,7 +203,7 @@ docker compose exec php vendor/bin/phpunit tests/Architecture/
--- ---
## Erweitern: Haeufige Erweiterungspunkte ## Erweitern: Häufige Erweiterungspunkte
### UI-Slot: Sidebar-Tab ### UI-Slot: Sidebar-Tab
@@ -278,11 +278,11 @@ Fuer globale Assets (auf jeder Seite) stattdessen `layout.head_style` oder `runt
Interface: `core/App/Module/Contracts/SessionProvider.php` (Methoden: `populate()`, `clear()`). Interface: `core/App/Module/Contracts/SessionProvider.php` (Methoden: `populate()`, `clear()`).
**Session-Key-Konvention:** `module.mein-modul.*` (wird per Architektur-Test geprueft). **Session-Key-Konvention:** `module.mein-modul.*` (wird per Architektur-Test geprüft).
### Layout-Kontext ### Layout-Kontext
`LayoutContextProvider` implementieren — liefert Daten fuer Templates bei jedem Page-Render: `LayoutContextProvider` implementieren — liefert Daten für Templates bei jedem Page-Render:
```php ```php
'layout_context_providers' => [ 'layout_context_providers' => [
@@ -292,7 +292,7 @@ Interface: `core/App/Module/Contracts/SessionProvider.php` (Methoden: `populate(
Interface: `core/App/Module/Contracts/LayoutContextProvider.php` (Methode: `provide()`). Interface: `core/App/Module/Contracts/LayoutContextProvider.php` (Methode: `provide()`).
**Key-Konvention:** Top-Level-Keys muessen namespaced sein: `mein-modul.nav`, nicht `nav`. **Key-Konvention:** Top-Level-Keys müssen namespaced sein: `mein-modul.nav`, nicht `nav`.
### Event-Listener ### Event-Listener
@@ -308,7 +308,7 @@ Interface: `core/App/Module/Contracts/EventListener.php` (Methode: `handle(strin
### Scheduler-Job ### Scheduler-Job
Siehe `/docs/howto-geplante-aufgaben.md` und `modules/audit/module.php` fuer Beispiele. Siehe `/docs/howto-geplante-aufgaben.md` und `modules/audit/module.php` für Beispiele.
### Modul-eigene Datenbank ### Modul-eigene Datenbank
@@ -317,16 +317,16 @@ Siehe `/docs/howto-geplante-aufgaben.md` und `modules/audit/module.php` fuer Bei
``` ```
SQL-Dateien unter `modules/mein-modul/db/updates/` — idempotent (IF NOT EXISTS, ON DUPLICATE KEY). SQL-Dateien unter `modules/mein-modul/db/updates/` — idempotent (IF NOT EXISTS, ON DUPLICATE KEY).
Ausgefuehrt ueber `php bin/console module:migrate`. Ausgeführt über `php bin/console module:migrate`.
### Uebersetzungen ### Übersetzungen
```php ```php
'i18n_path' => 'i18n', 'i18n_path' => 'i18n',
``` ```
Dateien: `modules/mein-modul/i18n/default_de.json`, `modules/mein-modul/i18n/default_en.json`. Dateien: `modules/mein-modul/i18n/default_de.json`, `modules/mein-modul/i18n/default_en.json`.
Schluessel werden per `t('key')` aufgeloest — identisch zu Core-Translations. Schlüssel werden per `t('key')` aufgeloest — identisch zu Core-Translations.
### Abhaengigkeit von anderem Modul ### Abhaengigkeit von anderem Modul
@@ -334,7 +334,7 @@ Schluessel werden per `t('key')` aufgeloest — identisch zu Core-Translations.
'requires' => ['notifications'], 'requires' => ['notifications'],
``` ```
Nicht-deklarierte Cross-Module-Imports sind verboten und werden per Architektur-Test geprueft. Nicht-deklarierte Cross-Module-Imports sind verboten und werden per Architektur-Test geprüft.
--- ---
@@ -351,13 +351,13 @@ Nicht-deklarierte Cross-Module-Imports sind verboten und werden per Architektur-
- [ ] Session-Keys verwenden `module.<id>.*` Prefix - [ ] Session-Keys verwenden `module.<id>.*` Prefix
- [ ] Keine Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`) im Modul-Code - [ ] Keine Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`) im Modul-Code
## Haeufige Fehler ## Häufige Fehler
| Problem | Ursache | Loesung | | Problem | Ursache | Lösung |
|---|---|---| |---|---|---|
| Sidebar-Tab/UI-Slot wird nicht angezeigt | AuthorizationPolicy nicht im Container registriert | Policy in ContainerRegistrar mit `PermissionService` registrieren | | Sidebar-Tab/UI-Slot wird nicht angezeigt | AuthorizationPolicy nicht im Container registriert | Policy in ContainerRegistrar mit `PermissionService` registrieren |
| 404 auf Modul-Route | `module:sync` nicht ausgefuehrt | `php bin/console module:sync` | | 404 auf Modul-Route | `module:sync` nicht ausgeführt | `php bin/console module:sync` |
| Permission existiert nicht | Permission nicht in Manifest deklariert | `permissions`-Array in `module.php` ergaenzen, dann `module:sync` | | Permission existiert nicht | Permission nicht in Manifest deklariert | `permissions`-Array in `module.php` ergaenzen, dann `module:sync` |
| CSS/JS werden nicht geladen | Assets nicht veroeffentlicht | `php bin/console module:assets-sync` oder `module:sync` | | CSS/JS werden nicht geladen | Assets nicht veroeffentlicht | `php bin/console module:assets-sync` oder `module:sync` |
| RuntimeException: fingerprint mismatch | Build veraltet nach Manifest-Aenderung | `php bin/console module:sync` | | RuntimeException: fingerprint mismatch | Build veraltet nach Manifest-Änderung | `php bin/console module:sync` |
| Namespace-Fehler in Tests | Modul-Klasse in Core-Namespace | Alle Klassen unter `MintyPHP\Module\<Name>\*` | | Namespace-Fehler in Tests | Modul-Klasse in Core-Namespace | Alle Klassen unter `MintyPHP\Module\<Name>\*` |

View File

@@ -2,15 +2,15 @@
Letzte Aktualisierung: 2026-03-06 Letzte Aktualisierung: 2026-03-06
Kurzstandard fuer neue Actions/Endpoints. Kurzstandard für neue Actions/Endpoints.
## Regeln ## Regeln
- Input immer ueber `requestInput()`. - Input immer über `requestInput()`.
- Validierungsfehler intern immer als `FormErrors`. - Validierungsfehler intern immer als `FormErrors`.
- API-Validation immer mit `ApiResponse::validationFromFormErrors(...)`. - API-Validation immer mit `ApiResponse::validationFromFormErrors(...)`.
- Web-Templates bekommen kompakte Fehlerlisten (`$errors`) aus `toFlatList()`. - Web-Templates bekommen kompakte Fehlerlisten (`$errors`) aus `toFlatList()`.
- `Flash` ist fuer Redirect-/Outcome-Meldungen (z. B. Success) gedacht, nicht als primaerer Validation-Container. - `Flash` ist für Redirect-/Outcome-Meldungen (z. B. Success) gedacht, nicht als primaerer Validation-Container.
- Bei Redirect-POST-Formen: `FormErrors` intern sammeln und erst am Redirect-Rand transportieren (z. B. `flashFormErrors(...)`). - Bei Redirect-POST-Formen: `FormErrors` intern sammeln und erst am Redirect-Rand transportieren (z. B. `flashFormErrors(...)`).
## Web-Beispiel (Action) ## Web-Beispiel (Action)

View File

@@ -15,20 +15,20 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
4. `/docs/tutorial-04-architektur-request-flow.md` 4. `/docs/tutorial-04-architektur-request-flow.md`
- Request-Flow, Schichtmodell, Do/Don't. - Request-Flow, Schichtmodell, Do/Don't.
5. `/docs/tutorial-05-erste-aenderung-ui.md` 5. `/docs/tutorial-05-erste-aenderung-ui.md`
- Erste sichere UI-Aenderung im Admin-Bereich. - Erste sichere UI-Änderung im Admin-Bereich.
6. `/docs/tutorial-06-erste-aenderung-backend.md` 6. `/docs/tutorial-06-erste-aenderung-backend.md`
- Erste Backend-/Service-/Repository-Aenderung. - Erste Backend-/Service-/Repository-Änderung.
7. `/docs/tutorial-07-security-tenant-scope.md` 7. `/docs/tutorial-07-security-tenant-scope.md`
- Sicherheitsgrundlagen, Tenant-Scope, Pflichtchecks. - Sicherheitsgrundlagen, Tenant-Scope, Pflichtchecks.
8. `/docs/tutorial-08-api-erweitern.md` 8. `/docs/tutorial-08-api-erweitern.md`
- API-Aenderung sauber mit OpenAPI und Tests umsetzen. - API-Änderung sauber mit OpenAPI und Tests umsetzen.
9. `/docs/tutorial-09-advanced-scheduler-sso.md` 9. `/docs/tutorial-09-advanced-scheduler-sso.md`
- Fortgeschrittene Themen: Scheduler, Lifecycle, SSO. - Fortgeschrittene Themen: Scheduler, Lifecycle, SSO.
## How-to Guides ## How-to Guides
- `/docs/howto-erste-aenderung.md` - `/docs/howto-erste-aenderung.md`
- Schritt-fuer-Schritt durch die erste Codeaenderung. - Schritt-für-Schritt durch die erste Codeänderung.
- `/docs/howto-globale-suche.md` - `/docs/howto-globale-suche.md`
- Neue Resource in der globalen Suche integrieren. - Neue Resource in der globalen Suche integrieren.
- `/docs/howto-importe.md` - `/docs/howto-importe.md`
@@ -44,7 +44,7 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
- `/docs/howto-rbac-permissions-playbook.md` - `/docs/howto-rbac-permissions-playbook.md`
- RBAC-Rechte sauber erweitern (neue Permission, neue Ability, UI/API-Faelle). - RBAC-Rechte sauber erweitern (neue Permission, neue Ability, UI/API-Faelle).
- `/docs/howto-request-input-validation.md` - `/docs/howto-request-input-validation.md`
- Einheitliches Request/Input- und Validation-Muster fuer Web + API. - Einheitliches Request/Input- und Validation-Muster für Web + API.
- `/docs/howto-docker-lokal.md` - `/docs/howto-docker-lokal.md`
- Lokaler Docker-Betrieb. - Lokaler Docker-Betrieb.
- `/docs/howto-docker-produktiv.md` - `/docs/howto-docker-produktiv.md`
@@ -52,15 +52,15 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
- `/docs/howto-betriebscheck-doctor.md` - `/docs/howto-betriebscheck-doctor.md`
- Schneller Systemcheck mit `php bin/console doctor`. - Schneller Systemcheck mit `php bin/console doctor`.
- `/docs/howto-fehlerbehebung.md` - `/docs/howto-fehlerbehebung.md`
- Haeufige Fehler und schnelle Loesungen. - Häufige Fehler und schnelle Lösungen.
- `/docs/howto-lokale-entwicklung.md` - `/docs/howto-lokale-entwicklung.md`
- Tages-Workflow fuer Entwicklung und Checks. - Tages-Workflow für Entwicklung und Checks.
- `/docs/howto-modul-erstellen.md` - `/docs/howto-modul-erstellen.md`
- Neues Modul erstellen: Scaffold, Manifest, Route, Permission, UI-Slot, Test. - Neues Modul erstellen: Scaffold, Manifest, Route, Permission, UI-Slot, Test.
- `/docs/howto-gitea-required-checks.md` - `/docs/howto-gitea-required-checks.md`
- Gitea-Workflow fuer `qa-required` vorbereiten (Phase 1 enforcement-ready). - Gitea-Workflow für `qa-required` vorbereiten (Phase 1 enforcement-ready).
- `/docs/howto-dokumentation-erweitern.md` - `/docs/howto-dokumentation-erweitern.md`
- Regeln fuer konsistente, wachsende Doku. - Regeln für konsistente, wachsende Doku.
## Explanation ## Explanation
@@ -75,7 +75,7 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
- `/docs/explanation-einstellungen-speicherung.md` - `/docs/explanation-einstellungen-speicherung.md`
- Warum Settings in DB + Datei-Cache koexistieren. - Warum Settings in DB + Datei-Cache koexistieren.
- `/docs/explanation-docker-betrieb.md` - `/docs/explanation-docker-betrieb.md`
- Uebersicht und Entscheidungshilfe fuer Dev/Prod. - Übersicht und Entscheidungshilfe für Dev/Prod.
## Reference ## Reference
@@ -88,9 +88,9 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
- `/docs/reference-konventionen.md` - `/docs/reference-konventionen.md`
- Projektweite Konventionen (kurz und verbindlich). - Projektweite Konventionen (kurz und verbindlich).
- `/docs/reference-core-standards.md` - `/docs/reference-core-standards.md`
- Regeln speziell fuer `core/**`. - Regeln speziell für `core/**`.
- `/docs/reference-benutzer-lifecycle-policy.md` - `/docs/reference-benutzer-lifecycle-policy.md`
- Policy- und Laufzeitreferenz fuer User Lifecycle. - Policy- und Laufzeitreferenz für User Lifecycle.
- `/docs/reference-frontend-css.md` - `/docs/reference-frontend-css.md`
- CSS-Schichtsystem und Namenskonventionen. - CSS-Schichtsystem und Namenskonventionen.
- `/docs/reference-frontend-javascript.md` - `/docs/reference-frontend-javascript.md`
@@ -98,13 +98,13 @@ Diese Dokumentation ist nach dem Diataxis-Modell in vier Quadranten gegliedert:
- `/docs/reference-entwickler-checkliste.md` - `/docs/reference-entwickler-checkliste.md`
- Definition of Done vor Merge. - Definition of Done vor Merge.
- `/docs/reference-codex-prompts.md` - `/docs/reference-codex-prompts.md`
- Standard-Prompts fuer Skill-basierte Planung und Implementierung. - Standard-Prompts für Skill-basierte Planung und Implementierung.
- `/docs/reference-extension-readiness.md` - `/docs/reference-extension-readiness.md`
- Freigabe-Checkliste fuer weitere Module auf Basis des bestehenden Modul-Contracts. - Freigabe-Checkliste für weitere Module auf Basis des bestehenden Modul-Contracts.
- `/docs/reference-cli-commands.md` - `/docs/reference-cli-commands.md`
- Alle CLI-Kommandos (`php bin/console`), Bootstrap-Helfer, Anleitung neue Kommandos. - Alle CLI-Kommandos (`php bin/console`), Bootstrap-Helfer, Anleitung neue Kommandos.
- `/docs/reference-module-manifest-contract.md` - `/docs/reference-module-manifest-contract.md`
- Verbindlicher Runtime- und Manifest-Contract fuer Module (V1.1). - Verbindlicher Runtime- und Manifest-Contract für Module (V1.1).
- `/docs/reference-notifications-module.md` - `/docs/reference-notifications-module.md`
- Verbindliche Referenz fuer Notifications (Events, Guardrails, Dedupe, Bell-Vertrag, Tests). - Verbindliche Referenz für Notifications (Events, Guardrails, Dedupe, Bell-Vertrag, Tests).
- Agent-Workflow (Rollen, State-Machine, Contracts): siehe `/.agents/workflow.md` - Agent-Workflow (Rollen, State-Machine, Contracts): siehe `/.agents/workflow.md`

View File

@@ -4,13 +4,13 @@ Letzte Aktualisierung: 2026-04-01
## Einstiegspunkt ## Einstiegspunkt
Alle CLI-Kommandos laufen ueber einen einzigen Einstiegspunkt: Alle CLI-Kommandos laufen über einen einzigen Einstiegspunkt:
```bash ```bash
php bin/console <command> [argumente] [optionen] php bin/console <command> [argumente] [optionen]
``` ```
Uebersicht aller verfuegbaren Kommandos: Übersicht aller verfügbaren Kommandos:
```bash ```bash
php bin/console list php bin/console list
@@ -32,9 +32,9 @@ Fuehrt den vollstaendigen Runtime-Sync aus: `db-migrate` → `migrate` → `perm
docker compose exec php php bin/console module:sync docker compose exec php php bin/console module:sync
``` ```
Noetig nach jeder Aenderung an Modulen, Manifesten, Routen oder `APP_ENABLED_MODULES` — und nach jedem `git pull`, der Schema-Updates unter `db/updates/*.sql` mitbringt. Nötig nach jeder Änderung an Modulen, Manifesten, Routen oder `APP_ENABLED_MODULES` — und nach jedem `git pull`, der Schema-Updates unter `db/updates/*.sql` mitbringt.
Falls `web/index.php` den Fehler "Module runtime is stale" wirft, ist dieser Befehl die Loesung. Falls `web/index.php` den Fehler "Module runtime is stale" wirft, ist dieser Befehl die Lösung.
Maschinenlesbar: Maschinenlesbar:
@@ -52,7 +52,7 @@ Jede `db/updates/*.sql` MUSS idempotent sein (`CREATE TABLE IF NOT EXISTS`, `INS
docker compose exec php php bin/console db:migrate docker compose exec php php bin/console db:migrate
``` ```
`module:sync` ruft den Befehl als ersten Schritt automatisch auf. Direktaufruf nuetzlich, wenn nur DB-Updates gewuenscht sind ohne Module-Build. `module:sync` ruft den Befehl als ersten Schritt automatisch auf. Direktaufruf nützlich, wenn nur DB-Updates gewuenscht sind ohne Module-Build.
Status-Anzeige (kein Schreibzugriff): Status-Anzeige (kein Schreibzugriff):
@@ -60,7 +60,7 @@ Status-Anzeige (kein Schreibzugriff):
docker compose exec php php bin/console db:migrate --status docker compose exec php php bin/console db:migrate --status
``` ```
Listet `applied:` und `pending:` separat — nuetzlich beim Debugging veralteter Schemata. Listet `applied:` und `pending:` separat — nützlich beim Debugging veralteter Schemata.
### module:migrate ### module:migrate
@@ -103,7 +103,7 @@ docker compose exec php php bin/console module:deactivate <module-id> --confirm
docker compose exec php php bin/console module:deactivate <module-id> --dry-run docker compose exec php php bin/console module:deactivate <module-id> --dry-run
``` ```
Das Modul muss nicht in der Aktivliste stehen. `--dry-run` validiert den Handler ohne Ausfuehrung. Das Modul muss nicht in der Aktivliste stehen. `--dry-run` validiert den Handler ohne Ausführung.
### scheduler:run ### scheduler:run
@@ -117,7 +117,7 @@ Der Docker-Scheduler-Service ruft dieses Kommando automatisch alle 60 Sekunden a
### module:validate ### module:validate
Validiert Manifest, Klassen und Struktur eines Moduls. Prueft JSON-Schema-Compliance des `module.php`, Namespace-Konventionen, Interface-Implementierungen, Permission-Key-Formate und Verzeichnisstruktur. Validiert Manifest, Klassen und Struktur eines Moduls. Prüft JSON-Schema-Compliance des `module.php`, Namespace-Konventionen, Interface-Implementierungen, Permission-Key-Formate und Verzeichnisstruktur.
```bash ```bash
docker compose exec php php bin/console module:validate <module-id> docker compose exec php php bin/console module:validate <module-id>
@@ -128,7 +128,7 @@ docker compose exec php php bin/console module:validate --all
### make:module ### make:module
Erzeugt die vollstaendige Verzeichnisstruktur fuer ein neues Modul inkl. Manifest, ContainerRegistrar-Stub, AuthorizationPolicy-Stub und leeren Verzeichnissen. Erzeugt die vollstaendige Verzeichnisstruktur für ein neues Modul inkl. Manifest, ContainerRegistrar-Stub, AuthorizationPolicy-Stub und leeren Verzeichnissen.
```bash ```bash
docker compose exec php php bin/console make:module <module-id> docker compose exec php php bin/console make:module <module-id>
@@ -144,7 +144,7 @@ Fuehrt System-Gesundheitschecks aus (ENV, DB, RBAC, Scheduler-Heartbeat).
docker compose exec php php bin/console doctor docker compose exec php php bin/console doctor
``` ```
Siehe `/docs/howto-betriebscheck-doctor.md` fuer Details. Siehe `/docs/howto-betriebscheck-doctor.md` für Details.
Maschinenlesbar: Maschinenlesbar:
@@ -168,15 +168,15 @@ bin/dev qa-extended
``` ```
`bin/dev console ...` delegiert intern auf `docker compose exec -T php php bin/console ...`. `bin/dev console ...` delegiert intern auf `docker compose exec -T php php bin/console ...`.
`bin/dev qa` ist ein Alias fuer `bin/dev qa-required`. `bin/dev qa` ist ein Alias für `bin/dev qa-required`.
## Neue Kommandos anlegen ## Neue Kommandos anlegen
1. Klasse in `core/Console/Commands/` erstellen, die `MintyPHP\Console\Command` erweitert. 1. Klasse in `core/Console/Commands/` erstellen, die `MintyPHP\Console\Command` erweitert.
2. `name()`, `description()` und `execute()` implementieren. 2. `name()`, `description()` und `execute()` implementieren.
3. Optional: `usage()` fuer `--help`-Ausgabe ueberschreiben. 3. Optional: `usage()` für `--help`-Ausgabe überschreiben.
Das Kommando wird automatisch erkannt — kein manuelles Registrieren noetig. Das Kommando wird automatisch erkannt — kein manuelles Registrieren nötig.
Beispiel: Beispiel:
@@ -204,5 +204,5 @@ Die `Command`-Basisklasse stellt Helfer bereit:
| Methode | Zweck | | Methode | Zweck |
|---|---| |---|---|
| `$this->bootstrapApp($channel)` | App-Bootstrap fuer Nicht-Modul-Kommandos | | `$this->bootstrapApp($channel)` | App-Bootstrap für Nicht-Modul-Kommandos |
| `$this->projectRoot()` | Gibt den absoluten Projekt-Root-Pfad zurueck | | `$this->projectRoot()` | Gibt den absoluten Projekt-Root-Pfad zurueck |

View File

@@ -4,7 +4,7 @@ Letzte Aktualisierung: 2026-03-09
## Ziel ## Ziel
Wiederverwendbare Prompt-Muster fuer konsistente Planung und Implementierung mit den Skills `core-guardrails` und `starterkit-planner`. Wiederverwendbare Prompt-Muster für konsistente Planung und Implementierung mit den Skills `core-guardrails` und `starterkit-planner`.
## Prompt: Implementierung (strict) ## Prompt: Implementierung (strict)

View File

@@ -45,9 +45,9 @@ Kurze Definition of Done vor Merge.
- [ ] Runtime-Komponenten folgen Contract `init(root, config) -> { destroy() }` - [ ] Runtime-Komponenten folgen Contract `init(root, config) -> { destroy() }`
- [ ] Runtime-Komponenten enthalten keinen eigenen `DOMContentLoaded`-/Sideeffect-Auto-Init - [ ] Runtime-Komponenten enthalten keinen eigenen `DOMContentLoaded`-/Sideeffect-Auto-Init
- [ ] Listener/Timer/Observer aus Runtime-Komponenten werden in `destroy()` sauber abgebaut - [ ] Listener/Timer/Observer aus Runtime-Komponenten werden in `destroy()` sauber abgebaut
- [ ] Runtime-Konfiguration liegt in `page-config-app-components` (default) bzw. `page-config-login-components` (login) und wird ueber `readPageConfig(...)` gelesen - [ ] Runtime-Konfiguration liegt in `page-config-app-components` (default) bzw. `page-config-login-components` (login) und wird über `readPageConfig(...)` gelesen
- [ ] Host-gebundene Runtime-Komponenten haben explizite `data-app-component`-Hosts im Markup - [ ] Host-gebundene Runtime-Komponenten haben explizite `data-app-component`-Hosts im Markup
- [ ] Global-Utilities ohne natuerliches Host-Element sind explizit runtime-global registriert (`scope: 'global'`) - [ ] Global-Utilities ohne natürliches Host-Element sind explizit runtime-global registriert (`scope: 'global'`)
- [ ] Service-Auflösung in `pages/**`/`templates/**`/`core/Support/**` nur via `app(Foo::class)` - [ ] Service-Auflösung in `pages/**`/`templates/**`/`core/Support/**` nur via `app(Foo::class)`
- [ ] Keine `new ...Factory()` in `core/Service/**` außerhalb `*Factory.php` - [ ] Keine `new ...Factory()` in `core/Service/**` außerhalb `*Factory.php`
- [ ] Keine direkten `new ...Service()`, `new ...Gateway()`, `new ...Repository()` in `core/Service/**` außerhalb `*Factory.php` - [ ] Keine direkten `new ...Service()`, `new ...Gateway()`, `new ...Repository()` in `core/Service/**` außerhalb `*Factory.php`
@@ -74,12 +74,12 @@ Kurze Definition of Done vor Merge.
- [ ] `docker compose exec php vendor/bin/phpunit` zweimal hintereinander in identischer Umgebung (beide Runs gruen) - [ ] `docker compose exec php vendor/bin/phpunit` zweimal hintereinander in identischer Umgebung (beide Runs gruen)
- [ ] `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress` - [ ] `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress`
- [ ] `docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php` - [ ] `docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php`
- [ ] bei Modul-/Manifest-/Routen-Aenderungen: `APP_ENABLED_MODULES='' php bin/console module:sync` - [ ] bei Modul-/Manifest-/Routen-Änderungen: `APP_ENABLED_MODULES='' php bin/console module:sync`
- [ ] bei Modul-/Manifest-/Routen-Aenderungen: `APP_ENABLED_MODULES='addressbook,bookmarks' php bin/console module:sync` - [ ] bei Modul-/Manifest-/Routen-Änderungen: `APP_ENABLED_MODULES='addressbook,bookmarks' php bin/console module:sync`
- [ ] Runtime-Sync erzeugt Step-Summary (`migrate`, `permissions-sync`, `build`, `assets-sync`) und `vendor_warnings_ignored=<n>` - [ ] Runtime-Sync erzeugt Step-Summary (`migrate`, `permissions-sync`, `build`, `assets-sync`) und `vendor_warnings_ignored=<n>`
- [ ] Keine First-party CLI-Warnings/Notices/Deprecations im Runtime-Sync (Vendor-Warnings sind nur temporaer toleriert und muessen gezaehlt sichtbar sein) - [ ] Keine First-party CLI-Warnings/Notices/Deprecations im Runtime-Sync (Vendor-Warnings sind nur temporaer toleriert und müssen gezaehlt sichtbar sein)
- [ ] bei Docs-/Skills-Aenderungen: `docker compose exec php vendor/bin/phpunit tests/Architecture/CodexSkillReferenceContractTest.php` - [ ] bei Docs-/Skills-Änderungen: `docker compose exec php vendor/bin/phpunit tests/Architecture/CodexSkillReferenceContractTest.php`
- [ ] `bin/docs-link-check.sh` (QG-008, scannt standardmaessig ohne `.agents/runs/**`) - [ ] `bin/docs-link-check.sh` (QG-008, scannt standardmäßig ohne `.agents/runs/**`)
- [ ] `bin/docs-drift-check.sh` (QG-008-Zusatz, blockt Legacy-Referenzen + fehlenden Setup-Vertrag) - [ ] `bin/docs-drift-check.sh` (QG-008-Zusatz, blockt Legacy-Referenzen + fehlenden Setup-Vertrag)
- [ ] `bin/codex-skills-sync.sh --check` (QG-009) - [ ] `bin/codex-skills-sync.sh --check` (QG-009)
- [ ] bei JS-Änderungen: Browser-Smoke mit DevTools-Console (keine JS-Errors) - [ ] bei JS-Änderungen: Browser-Smoke mit DevTools-Console (keine JS-Errors)

View File

@@ -4,13 +4,13 @@ Letzte Aktualisierung: 2026-03-18
## Ziel ## Ziel
Verbindliche Vorbedingungen fuer weitere eigenstaendige Module auf dem bestehenden Modul-System festhalten. Verbindliche Vorbedingungen für weitere eigenstaendige Module auf dem bestehenden Modul-System festhalten.
## Kanonische Quelle ## Kanonische Quelle
Normativer Manifest- und Runtime-Contract: `/docs/reference-module-manifest-contract.md`. Normativer Manifest- und Runtime-Contract: `/docs/reference-module-manifest-contract.md`.
Diese Readiness-Doku ist bewusst kurz und verweist fuer Felddefinitionen/Guards immer auf den Manifest-Contract. Diese Readiness-Doku ist bewusst kurz und verweist für Felddefinitionen/Guards immer auf den Manifest-Contract.
## Readiness-Checkliste vor Modul 2+ ## Readiness-Checkliste vor Modul 2+
@@ -21,17 +21,17 @@ Diese Readiness-Doku ist bewusst kurz und verweist fuer Felddefinitionen/Guards
- `module.php` validiert gegen den kanonischen Contract (`permissions`, `scheduler_jobs`, `ui_slots`, `routes`). - `module.php` validiert gegen den kanonischen Contract (`permissions`, `scheduler_jobs`, `ui_slots`, `routes`).
- Route-/Slot-/Layout-Guards laufen fail-fast. - Route-/Slot-/Layout-Guards laufen fail-fast.
3. **Security + Scope absichern** 3. **Security + Scope absichern**
- AuthZ/RBAC serverseitig pruefen. - AuthZ/RBAC serverseitig prüfen.
- Tenant-Scope serverseitig pruefen. - Tenant-Scope serverseitig prüfen.
- POST-Mutationen mit CSRF absichern. - POST-Mutationen mit CSRF absichern.
4. **Runtime-Hygiene absichern** 4. **Runtime-Hygiene absichern**
- `php bin/console module:sync` laeuft reproduzierbar mit standardisierter Step-Summary. - `php bin/console module:sync` laeuft reproduzierbar mit standardisierter Step-Summary.
- Keine First-party CLI-Warnings/Notices/Deprecations; Vendor-Rauschen wird nur gezaehlt/reportet. - Keine First-party CLI-Warnings/Notices/Deprecations; Vendor-Rauschen wird nur gezaehlt/reportet.
5. **Testbarkeit und Gates** 5. **Testbarkeit und Gates**
- Architektur- und Domain-Tests fuer neue Logik ergaenzen. - Architektur- und Domain-Tests für neue Logik ergaenzen.
- `phpunit` und `phpstan` als Pflicht-Gates gruen. - `phpunit` und `phpstan` als Pflicht-Gates gruen.
6. **Daten- und Betriebsfaehigkeit** 6. **Daten- und Betriebsfaehigkeit**
- Schema-/Datenaenderungen fuer Bestandsinstallationen idempotent in `db/updates/*.sql`. - Schema-/Datenänderungen für Bestandsinstallationen idempotent in `db/updates/*.sql`.
- Logs/Audit ohne unredacted PII/Secrets. - Logs/Audit ohne unredacted PII/Secrets.
## Derzeit Out of Scope ## Derzeit Out of Scope
@@ -39,7 +39,7 @@ Diese Readiness-Doku ist bewusst kurz und verweist fuer Felddefinitionen/Guards
- Eigener DB-Server oder Netzwerk-Microservice je Modul. - Eigener DB-Server oder Netzwerk-Microservice je Modul.
- Externe Drittanbieter-Module mit Legacy-Kompatibilitaet. - Externe Drittanbieter-Module mit Legacy-Kompatibilitaet.
## Verfuegbare Core-Services fuer Module ## Verfügbare Core-Services für Module
JS-seitig: JS-seitig:

View File

@@ -88,6 +88,6 @@ Regel: Vendor-Overrides nur für Bibliotheks-Selektoren; projektspezifische UI-S
## Module-CSS Token-Regel ## Module-CSS Token-Regel
1. Modul-CSS nutzt primär Core-Tokens (`--app-*`) statt eigener Farbwerte. 1. Modul-CSS nutzt primär Core-Tokens (`--app-*`) statt eigener Farbwerte.
2. Modul-spezifische Variablen sind als Alias erlaubt, muessen aber auf Core-Tokens mappen. 2. Modul-spezifische Variablen sind als Alias erlaubt, müssen aber auf Core-Tokens mappen.
3. Keine Legacy-/Phantom-Tokens (`--app-text`, `--app-hover`, `--app-border-light`) neu einführen. 3. Keine Legacy-/Phantom-Tokens (`--app-text`, `--app-hover`, `--app-border-light`) neu einführen.
4. Harte Hex-Fallbacks nur als Ausnahmefall, nicht als Standard. 4. Harte Hex-Fallbacks nur als Ausnahmefall, nicht als Standard.

View File

@@ -48,14 +48,14 @@ Empfohlenes Muster:
- `destroy()` muss alle Listener/Timer/Observer abbauen. - `destroy()` muss alle Listener/Timer/Observer abbauen.
- Hybrid Host-Policy: - Hybrid Host-Policy:
- Host-gebundene UI-Komponenten nutzen explizite `data-app-component`-Hosts im Markup. - Host-gebundene UI-Komponenten nutzen explizite `data-app-component`-Hosts im Markup.
- Tabs sind host-gebunden und laufen ausschliesslich ueber `data-app-component="tabs"`. - Tabs sind host-gebunden und laufen ausschliesslich über `data-app-component="tabs"`.
- Globale Utilities ohne natuerliches Host-Element werden runtime-global (`scope: 'global'`) registriert, aber mit demselben Lifecycle-Contract. - Globale Utilities ohne natürliches Host-Element werden runtime-global (`scope: 'global'`) registriert, aber mit demselben Lifecycle-Contract.
- Root-Strict gilt fuer host-gebundene Komponenten: keine `document.querySelector`-Fallback-Pfade. - Root-Strict gilt für host-gebundene Komponenten: keine `document.querySelector`-Fallback-Pfade.
- Projektseitige Custom-Globals auf `window.*` sind verboten; Komponenten kommunizieren ueber Core-Channels. - Projektseitige Custom-Globals auf `window.*` sind verboten; Komponenten kommunizieren über Core-Channels.
## UI Storage Contract ## UI Storage Contract
- Persistente UI-States laufen fuer Runtime-Komponenten ueber `web/js/core/app-ui-storage.js`. - Persistente UI-States laufen für Runtime-Komponenten über `web/js/core/app-ui-storage.js`.
- Storage-Keys sind namespaced (`namespace:version:scope:*`) statt ungeordnet verteilt. - Storage-Keys sind namespaced (`namespace:version:scope:*`) statt ungeordnet verteilt.
- Hard-Cut-Policy: keine Legacy-Key-Fallbacks und kein Legacy-Key-Sync in migrierten Runtime-Komponenten/Core-Modulen. - Hard-Cut-Policy: keine Legacy-Key-Fallbacks und kein Legacy-Key-Sync in migrierten Runtime-Komponenten/Core-Modulen.

View File

@@ -5,7 +5,7 @@ Letzte Aktualisierung: 2026-04-01
Alle Konfiguration erfolgt über Umgebungsvariablen in der `.env`-Datei. Alle Konfiguration erfolgt über Umgebungsvariablen in der `.env`-Datei.
Vorlage: `.env.example` — niemals echte Credentials committen. Vorlage: `.env.example` — niemals echte Credentials committen.
Setup-Vertrag (Source of Truth): `config/config.php` ist im Repository getrackt und liest alle Werte über `$envString()`/`$envInt()`/`$envBool()` aus den ENV-Variablen. Diese Seite und `web/index.php` sind die massgeblichen Referenzen dafuer. Setup-Vertrag (Source of Truth): `config/config.php` ist im Repository getrackt und liest alle Werte über `$envString()`/`$envInt()`/`$envBool()` aus den ENV-Variablen. Diese Seite und `web/index.php` sind die maßgeblichen Referenzen dafür.
--- ---

View File

@@ -53,7 +53,7 @@ Letzte Aktualisierung: 2026-03-24
- Namespace: `MintyPHP\Module\<Name>\*` — nie Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`). - Namespace: `MintyPHP\Module\<Name>\*` — nie Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`).
- Verzeichnis: `modules/<id>/lib/Module/<Name>/` (Service, Repository, Providers, Support). - Verzeichnis: `modules/<id>/lib/Module/<Name>/` (Service, Repository, Providers, Support).
- Session-Keys: Prefix `module.<id>.*`. - Session-Keys: Prefix `module.<id>.*`.
- UI-Integration nur ueber Plattform-Slots (`aside.tab_panel`, `topbar.right_item`, `layout.head_style`, `runtime.component`, etc.). - UI-Integration nur über Plattform-Slots (`aside.tab_panel`, `topbar.right_item`, `layout.head_style`, `runtime.component`, etc.).
- Kein Modul-spezifisches Markup in Core-Templates. - Kein Modul-spezifisches Markup in Core-Templates.
- Tests: `modules/<id>/tests/` (werden von `phpunit.xml` automatisch entdeckt via `modules/*/tests`). - Tests: `modules/<id>/tests/` (werden von `phpunit.xml` automatisch entdeckt via `modules/*/tests`).
- Modul-Actions importieren nie `OperationsAuthorizationPolicy::ABILITY_*` — eigene Policy nutzen. - Modul-Actions importieren nie `OperationsAuthorizationPolicy::ABILITY_*` — eigene Policy nutzen.
@@ -63,5 +63,5 @@ Letzte Aktualisierung: 2026-03-24
- `docker compose exec php vendor/bin/phpunit` - `docker compose exec php vendor/bin/phpunit`
- `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress` - `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress`
- bei Modul-/Manifest-Aenderungen: `docker compose exec php php bin/console module:sync` - bei Modul-/Manifest-Änderungen: `docker compose exec php php bin/console module:sync`
- bei JS-Änderungen: Browser-Smoke mit DevTools-Console (keine JS-Errors) - bei JS-Änderungen: Browser-Smoke mit DevTools-Console (keine JS-Errors)

View File

@@ -4,11 +4,11 @@ Letzte Aktualisierung: 2026-03-25
## Ziel ## Ziel
Verbindlicher Contract fuer `modules/*/module.php`, damit Module zur Runtime deterministisch geladen und validiert werden. Verbindlicher Contract für `modules/*/module.php`, damit Module zur Runtime deterministisch geladen und validiert werden.
## Status ## Status
Dieses Dokument ist die kanonische (normative) Quelle fuer Manifest-, Runtime- und Guard-Regeln des Modul-Systems. Dieses Dokument ist die kanonische (normative) Quelle für Manifest-, Runtime- und Guard-Regeln des Modul-Systems.
`/docs/reference-extension-readiness.md` referenziert diesen Contract und dupliziert keine Felddefinitionen. `/docs/reference-extension-readiness.md` referenziert diesen Contract und dupliziert keine Felddefinitionen.
## JSON-Schema-Enforcement ## JSON-Schema-Enforcement
@@ -25,8 +25,8 @@ Ein Manifest muss sowohl:
## Namespace-Konvention ## Namespace-Konvention
Alle PHP-Klassen eines Moduls muessen den Namespace `MintyPHP\Module\<Name>\*` verwenden. Alle PHP-Klassen eines Moduls müssen den Namespace `MintyPHP\Module\<Name>\*` verwenden.
Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`, `MintyPHP\Support\*`) sind fuer Modul-Code verboten. Core-Namespaces (`MintyPHP\Service\*`, `MintyPHP\Repository\*`, `MintyPHP\Support\*`) sind für Modul-Code verboten.
Verzeichnis-Layout: Verzeichnis-Layout:
@@ -75,8 +75,8 @@ Der `ModuleAutoloader` registriert daraus den absoluten Pfad `modules/<id>/i18n/
``` ```
- `key` und `description` sind Pflichtfelder. - `key` und `description` sind Pflichtfelder.
- Runtime-Sync erfolgt ueber `php bin/console module:sync`. - Runtime-Sync erfolgt über `php bin/console module:sync`.
- `module:sync` fuehrt diesen Sync automatisch aus. - `module:sync` führt diesen Sync automatisch aus.
## `scheduler_jobs` Contract ## `scheduler_jobs` Contract
@@ -101,8 +101,8 @@ Der `ModuleAutoloader` registriert daraus den absoluten Pfad `modules/<id>/i18n/
], ],
``` ```
- Manifest ist Source of Truth fuer Job-Metadaten und Defaults. - Manifest ist Source of Truth für Job-Metadaten und Defaults.
- Handler werden zur Laufzeit container-resolvable fuer `execute(...)` geladen. - Handler werden zur Laufzeit container-resolvable für `execute(...)` geladen.
## `ui_slots` Contract (V1.2) ## `ui_slots` Contract (V1.2)
@@ -118,15 +118,15 @@ Neue generische Slot-Typen:
Abgrenzung CSS: Abgrenzung CSS:
- `layout.head_style` = global auf allen Seiten. - `layout.head_style` = global auf allen Seiten.
- `asset_groups` = seiten-/kontextbezogen ueber `style_groups`. - `asset_groups` = seiten-/kontextbezogen über `style_groups`.
## Guard-Regeln ## Guard-Regeln
- Route-Kollisionen sind fail-fast: - Route-Kollisionen sind fail-fast:
- Modul vs Modul auf `target` und `path`. - Modul vs Modul auf `target` und `path`.
- Modul vs Core auf `path` beim Router-Boot. - Modul vs Core auf `path` beim Router-Boot.
- Layout-Provider duerfen reservierte Core-Keys nicht ueberschreiben (`moduleSlots`, `csrfKey`, `currentTenant`, ...). - Layout-Provider duerfen reservierte Core-Keys nicht überschreiben (`moduleSlots`, `csrfKey`, `currentTenant`, ...).
- Layout-Provider-Top-Level-Keys muessen namespaced sein (`<module_id>.<key>`). - Layout-Provider-Top-Level-Keys müssen namespaced sein (`<module_id>.<key>`).
## Runtime-Fingerprint ## Runtime-Fingerprint
@@ -141,16 +141,16 @@ Methoden: `ModuleRuntimePageBuilder::expectedFingerprint()`, `readFingerprint()`
## API-Channel-Isolation ## API-Channel-Isolation
API-Requests (`api/`-Prefix) ueberspringen den gesamten Web-Session-Flow: API-Requests (`api/`-Prefix) überspringen den gesamten Web-Session-Flow:
- Kein `Session::start()`, kein Cookie-Setting, kein CSRF-Token - Kein `Session::start()`, kein Cookie-Setting, kein CSRF-Token
- Kein Remember-Me, kein Session-Timeout, kein Tenant-UI-Refresh - Kein Remember-Me, kein Session-Timeout, kein Tenant-UI-Refresh
- API-Auth erfolgt ueber `ApiBootstrap.php` (Bearer-Token) - API-Auth erfolgt über `ApiBootstrap.php` (Bearer-Token)
- `AccessControl` listet `api/` als always-public - `AccessControl` listet `api/` als always-public
## Standard-Runbook ## Standard-Runbook
`php bin/console module:sync` fuehrt in fester Reihenfolge aus: `php bin/console module:sync` führt in fester Reihenfolge aus:
1. `module:migrate` 1. `module:migrate`
2. `module:permissions-sync` 2. `module:permissions-sync`

View File

@@ -4,7 +4,7 @@ Letzte Aktualisierung: 2026-03-25
## Ziel ## Ziel
Referenz fuer das Modul `notifications`: Event-Registrierung, Erzeugungsregeln, Guardrails, Frontend-Vertrag und Testbarkeit. Referenz für das Modul `notifications`: Event-Registrierung, Erzeugungsregeln, Guardrails, Frontend-Vertrag und Testbarkeit.
## Scope v1 ## Scope v1
@@ -15,7 +15,7 @@ Referenz fuer das Modul `notifications`: Event-Registrierung, Erzeugungsregeln,
- `user.activated` - `user.activated`
- `user.deactivated` - `user.deactivated`
- `user.assignment_changed` - `user.assignment_changed`
- Dedupe fuer diese Typen mit 30 Minuten Fenster. - Dedupe für diese Typen mit 30 Minuten Fenster.
## Modul-Verkabelung ## Modul-Verkabelung
@@ -25,7 +25,7 @@ Kanonische Quelle: `modules/notifications/module.php`
- mappt Eventtyp -> Listenerklasse + Methode `handle`. - mappt Eventtyp -> Listenerklasse + Methode `handle`.
- `ui_slots` - `ui_slots`
- `topbar.right_item`: Bell-Template - `topbar.right_item`: Bell-Template
- `layout.head_style`: Modul-CSS fuer Topbar + Dropdown - `layout.head_style`: Modul-CSS für Topbar + Dropdown
- `runtime.component`: Bell-JS (`initNotificationBell`) - `runtime.component`: Bell-JS (`initNotificationBell`)
- `permissions` - `permissions`
- `notifications.view` - `notifications.view`
@@ -45,9 +45,9 @@ Kanonische Quelle: `modules/notifications/module.php`
5. `NotificationRepository` persistiert in `user_notifications`. 5. `NotificationRepository` persistiert in `user_notifications`.
6. Bell-Endpunkte lesen tenant-scoped Daten und aktualisieren den Session-`unread_count`. 6. Bell-Endpunkte lesen tenant-scoped Daten und aktualisieren den Session-`unread_count`.
## Producer-Contract (fuer Core + andere Module) ## Producer-Contract (für Core + andere Module)
Stabile Erzeugung erfolgt ueber: Stabile Erzeugung erfolgt über:
- `MintyPHP\Module\Notifications\Service\NotificationMessage` - `MintyPHP\Module\Notifications\Service\NotificationMessage`
- `MintyPHP\Module\Notifications\Service\NotificationService::createFromMessage(...)` - `MintyPHP\Module\Notifications\Service\NotificationService::createFromMessage(...)`
@@ -78,7 +78,7 @@ $notificationService->createForTenantAdminUsersFromMessage(
); );
``` ```
Plain/Fallback-Erzeugung bleibt moeglich mit `NotificationMessage::plain(...)`. Plain/Fallback-Erzeugung bleibt möglich mit `NotificationMessage::plain(...)`.
## Datenvertrag Notification ## Datenvertrag Notification
@@ -90,7 +90,7 @@ Persistierter Kernvertrag:
- `link` (nullable string) - `link` (nullable string)
- `data` (nullable JSON) - `data` (nullable JSON)
Erweiterung fuer locale-neutrale Texte: Erweiterung für locale-neutrale Texte:
- `data.__message.title_key` (string) - `data.__message.title_key` (string)
- `data.__message.title_params` (array) - `data.__message.title_params` (array)
@@ -111,20 +111,20 @@ Dedupe-Metadaten (intern):
- Alle Bell-Endpunkte erzwingen: - Alle Bell-Endpunkte erzwingen:
- Login - Login
- Ability `notifications.view` - Ability `notifications.view`
- POST-Endpunkte (`mark-read`, `delete`) pruefen CSRF. - POST-Endpunkte (`mark-read`, `delete`) prüfen CSRF.
- Tenant-Scope wird serverseitig aus `current_tenant` erzwungen. - Tenant-Scope wird serverseitig aus `current_tenant` erzwungen.
- SQL ausschliesslich im Repository (`NotificationRepository`), Service nur Orchestrierung. - SQL ausschliesslich im Repository (`NotificationRepository`), Service nur Orchestrierung.
## Guardrails (Frontend) ## Guardrails (Frontend)
- Endpunkte werden mit App-Base-URL gebaut (kein harter Root-Pfad). - Endpunkte werden mit App-Base-URL gebaut (kein harter Root-Pfad).
- Fehlerpfade laufen ueber zentrale Telemetrie (`warn_once`). - Fehlerpfade laufen über zentrale Telemetrie (`warn_once`).
- A11y-Klickzeilen im Dropdown sind lokal gestylt, damit globale `[role="button"]` Regeln nicht durchschlagen. - A11y-Klickzeilen im Dropdown sind lokal gestylt, damit globale `[role="button"]` Regeln nicht durchschlagen.
- Default-`details/summary`-Chevron wird pro Komponente ueber `data-summary-chevron="none"` deaktiviert. - Default-`details/summary`-Chevron wird pro Komponente über `data-summary-chevron="none"` deaktiviert.
## Template/Partial Best Practice ## Template/Partial Best Practice
- Core-Partials immer ueber `templatePath('partials/...')` einbinden. - Core-Partials immer über `templatePath('partials/...')` einbinden.
- Keine fragilen relativen Tiefenpfade auf Core-Templates verwenden. - Keine fragilen relativen Tiefenpfade auf Core-Templates verwenden.
- Modulinterne Templates bleiben lokal relativ zum Modul. - Modulinterne Templates bleiben lokal relativ zum Modul.
@@ -138,22 +138,22 @@ Dedupe-Metadaten (intern):
### Unit ### Unit
- Listener-Tests fuer alle Core-5 Typen: - Listener-Tests für alle Core-5 Typen:
- Empfaengerregeln - Empfaengerregeln
- Actor-Exclusion - Actor-Exclusion
- ungultige/leere Payloads - ungultige/leere Payloads
- Service-Tests fuer Dedupe (created vs suppressed) - Service-Tests für Dedupe (created vs suppressed)
- Policy-Tests fuer `notifications.view` - Policy-Tests für `notifications.view`
### Integration/Flow ### Integration/Flow
- End-to-End fuer create/delete/activate/deactivate/assignment-change. - End-to-End für create/delete/activate/deactivate/assignment-change.
- Bell-Endpunkte mit Tenant-Scope und Permission-Pruefung. - Bell-Endpunkte mit Tenant-Scope und Permission-Prüfung.
- Bulk-Flows dispatchen konsistent. - Bulk-Flows dispatchen konsistent.
### Manueller Smoke ### Manueller Smoke
1. Zwei Admins im selben Tenant, einer als Actor. 1. Zwei Admins im selben Tenant, einer als Actor.
2. Actor fuehrt User-Aktion aus. 2. Actor führt User-Aktion aus.
3. Zweiter Admin sieht Bell-Badge + Listeneintrag. 3. Zweiter Admin sieht Bell-Badge + Listeneintrag.
4. Wiederholung innerhalb 30 Minuten erzeugt kein Duplikat. 4. Wiederholung innerhalb 30 Minuten erzeugt kein Duplikat.

View File

@@ -3,8 +3,8 @@
## Ziel ## Ziel
Die Tests sind nach Schutzwirkung organisiert, nicht nur nach technischer Ebene. Die Tests sind nach Schutzwirkung organisiert, nicht nur nach technischer Ebene.
Wichtige Architektur- und Security-Invarianten sollen schnell gezielt laufen koennen, Wichtige Architektur- und Security-Invarianten sollen schnell gezielt laufen können,
ohne dass jede Aenderung immer die komplette Suite im Kopf behalten muss. ohne dass jede Änderung immer die komplette Suite im Kopf behalten muss.
## PHPUnit-Suiten ## PHPUnit-Suiten
@@ -16,42 +16,42 @@ ohne dass jede Aenderung immer die komplette Suite im Kopf behalten muss.
- `Architecture`: alle Architektur-Contracts unter `tests/Architecture/`. - `Architecture`: alle Architektur-Contracts unter `tests/Architecture/`.
- `ArchitectureCore`: Core-Boundaries, Module-/Repository-Contracts, Taxonomie- und Sync-Contracts. - `ArchitectureCore`: Core-Boundaries, Module-/Repository-Contracts, Taxonomie- und Sync-Contracts.
- `ArchitectureSecurity`: CSRF, PRG, File-Storage, Crypto, Logging sowie AuthZ-nahe Contracts. - `ArchitectureSecurity`: CSRF, PRG, File-Storage, Crypto, Logging sowie AuthZ-nahe Contracts.
- `ArchitectureUI`: UI-Contracts fuer Listen, Detailseiten, Runtime-Komponenten und A11y. - `ArchitectureUI`: UI-Contracts für Listen, Detailseiten, Runtime-Komponenten und A11y.
- `ArchitectureModules`: modulbezogene Struktur- und Isolations-Contracts. - `ArchitectureModules`: modulbezogene Struktur- und Isolations-Contracts.
- `ArchitectureMeta`: Agent-/Skill-/Meta-Contracts fuer das Repository-Setup. - `ArchitectureMeta`: Agent-/Skill-/Meta-Contracts für das Repository-Setup.
## Regeln fuer neue Architecture-Tests ## Regeln für neue Architecture-Tests
- Nur stabile Invarianten testen, keine einmaligen Migrationsdetails. - Nur stabile Invarianten testen, keine einmaligen Migrationsdetails.
- Guards oder Risiken explizit abdecken, zum Beispiel `GR-CORE-*`, `GR-SEC-*`, `GR-TEST-*`. - Guards oder Risiken explizit abdecken, zum Beispiel `GR-CORE-*`, `GR-SEC-*`, `GR-TEST-*`.
- Keine Duplikate neben bereits breiten Contracts anlegen. - Keine Duplikate neben bereits breiten Contracts anlegen.
- String-Assertions nur verwenden, wenn kein robusterer Contract moeglich ist. - String-Assertions nur verwenden, wenn kein robusterer Contract möglich ist.
- Monolithische Sammeltests vermeiden; lieber kleine thematische Contracts. - Monolithische Sammeltests vermeiden; lieber kleine thematische Contracts.
## Delete vs. Refactor vs. Keep ## Delete vs. Refactor vs. Keep
- `keep`: testet einen echten plattformweiten Contract oder Security-Guard. - `keep`: testet einen echten plattformweiten Contract oder Security-Guard.
- `refactor`: Schutz ist wichtig, aber der Test ist zu datei-, markup- oder implementation-detailnah. - `refactor`: Schutz ist wichtig, aber der Test ist zu datei-, markup- oder implementation-detailnah.
- `delete`: dupliziert bestehende Schutzwirkung oder prueft nur historische Altlasten. - `delete`: dupliziert bestehende Schutzwirkung oder prüft nur historische Altlasten.
## Regeln fuer neue Service-Tests ## Regeln für neue Service-Tests
- Services auf Guards, Orchestrierung, Normalisierung, Fehlerpfade und Seiteneffekte testen. - Services auf Guards, Orchestrierung, Normalisierung, Fehlerpfade und Seiteneffekte testen.
- Keine eigenen Tests fuer reine `find/list/get/set`-Delegation ohne zusaetzliche Regel. - Keine eigenen Tests für reine `find/list/get/set`-Delegation ohne zusaetzliche Regel.
- Thin Wrapper auf gemeinsame Primitive zentral testen, nicht pro Gateway erneut. - Thin Wrapper auf gemeinsame Primitive zentral testen, nicht pro Gateway erneut.
- Mockability-, Reflection- und Interface-Existenz-Tests ohne Risiko-Schutz vermeiden. - Mockability-, Reflection- und Interface-Existenz-Tests ohne Risiko-Schutz vermeiden.
- Kleine Normalisierungs-Matrizen mit DataProvidern oder zusammengezogenen Contract-Dateien verdichten. - Kleine Normalisierungs-Matrizen mit DataProvidern oder zusammengezogenen Contract-Dateien verdichten.
## Typische Low-Value-Muster ## Typische Low-Value-Muster
- Gateway-Test prueft nur `encrypt/decrypt` erneut, obwohl das Basisverhalten schon zentral abgesichert ist. - Gateway-Test prüft nur `encrypt/decrypt` erneut, obwohl das Basisverhalten schon zentral abgesichert ist.
- Test bestaetigt nur, dass ein Service eins zu eins an Repository oder Gateway weiterreicht. - Test bestätigt nur, dass ein Service eins zu eins an Repository oder Gateway weiterreicht.
- Test prueft nur Methodensignatur, Reflection oder dass eine Klasse mockbar ist. - Test prüft nur Methodensignatur, Reflection oder dass eine Klasse mockbar ist.
- Mehrere Minidateien decken denselben Settings-/Fallback-Mechanismus mit identischem Mock-Muster ab. - Mehrere Minidateien decken denselben Settings-/Fallback-Mechanismus mit identischem Mock-Muster ab.
## Naechste Audit-Kandidaten ## Nächste Audit-Kandidaten
- methodenweiser Low-value-Pass in `tests/Service/User/UserAccountServiceTest.php` - methodenweiser Low-value-Pass in `tests/Service/User/UserAccountServiceTest.php`
- methodenweiser Low-value-Pass in `tests/Service/Org/DepartmentServiceTest.php` - methodenweiser Low-value-Pass in `tests/Service/Org/DepartmentServiceTest.php`
- methodenweiser Low-value-Pass in `tests/Service/Access/RoleServiceTest.php` - methodenweiser Low-value-Pass in `tests/Service/Access/RoleServiceTest.php`
- verbleibende kleine Settings-Gateway-Tests auf weitere Verdichtung pruefen - verbleibende kleine Settings-Gateway-Tests auf weitere Verdichtung prüfen