diff --git a/agent-system/checks/acceptance-checklist.md b/.agents/checks/acceptance-checklist.md
similarity index 70%
rename from agent-system/checks/acceptance-checklist.md
rename to .agents/checks/acceptance-checklist.md
index 3dc64a7..1b335c5 100644
--- a/agent-system/checks/acceptance-checklist.md
+++ b/.agents/checks/acceptance-checklist.md
@@ -1,6 +1,6 @@
-# Acceptance Checklist
+# Acceptance Test Checklist
-Use this checklist for the acceptance reviewer.
+Use this checklist for the Acceptance Tester role.
## Scope
- all in-scope items from `plan.json` are implemented
@@ -16,8 +16,7 @@ Use this checklist for the acceptance reviewer.
## Tests
- every test listed in `plan.json tests[]` is implemented and passes (QG-001 green)
-- new Service/Gateway logic has at least one test covering the happy path and one failure/edge case (GR-TEST-001)
-- no new untestable code patterns introduced (GR-TEST-002)
+- new Service/Gateway logic has at least one test (GR-TEST-001)
## Evidence
- each criterion has explicit pass/fail evidence
diff --git a/.agents/checks/guard-catalog.json b/.agents/checks/guard-catalog.json
new file mode 100644
index 0000000..961565c
--- /dev/null
+++ b/.agents/checks/guard-catalog.json
@@ -0,0 +1,159 @@
+{
+ "version": "2.0.0",
+ "guards": [
+ {
+ "id": "GR-CORE-003",
+ "area": "core",
+ "reviewer": "code",
+ "title": "Request input contract",
+ "requirement": "MUST read input via requestInput() in pages and avoid superglobals for request data."
+ },
+ {
+ "id": "GR-CORE-007",
+ "area": "core",
+ "reviewer": "code",
+ "title": "OpenAPI sync",
+ "requirement": "MUST update docs/openapi.yaml in the same merge when API endpoints change."
+ },
+ {
+ "id": "GR-CORE-010",
+ "area": "core",
+ "reviewer": "code",
+ "title": "Idempotent DB update scripts",
+ "requirement": "Schema or data changes for existing installs MUST be shipped as idempotent SQL scripts in db/updates/ (IF NOT EXISTS, ON DUPLICATE KEY, guarded ALTER TABLE). db/init/init.sql alone is not sufficient."
+ },
+ {
+ "id": "GR-CORE-011",
+ "area": "core",
+ "reviewer": "code",
+ "title": "Settings in DB, not config files",
+ "requirement": "New application settings MUST be stored in the settings DB table. config/settings.php is a partial hot-path cache only."
+ },
+ {
+ "id": "GR-CORE-012",
+ "area": "core",
+ "reviewer": "code",
+ "title": "POST-Redirect-GET",
+ "requirement": "Actions that successfully mutate state MUST respond with a redirect (PRG pattern). Flash messages set before redirect via Flash::set()."
+ },
+ {
+ "id": "GR-TEST-001",
+ "area": "test",
+ "reviewer": "code",
+ "title": "Tests ship with new logic",
+ "requirement": "New or changed business logic in lib/Service/ MUST be accompanied by PHPUnit tests covering the happy path and at least one failure or edge case. No new public method in a Service or Gateway without a test."
+ },
+ {
+ "id": "GR-TEST-002",
+ "area": "test",
+ "reviewer": "code",
+ "title": "Code must be testable",
+ "requirement": "New code MUST use constructor injection. MUST NOT introduce static side-effects in constructors, hidden global state, or direct DB/HTTP calls outside Repository/Gateway abstractions."
+ },
+ {
+ "id": "GR-UI-014",
+ "area": "ui",
+ "reviewer": "code",
+ "title": "UI state completeness",
+ "requirement": "New user-facing views or components MUST handle four states: loading, empty, error, success. All four defined in plan before implementation. Not required for small fixes to existing UI."
+ },
+ {
+ "id": "GR-UI-LIST",
+ "area": "ui",
+ "reviewer": "code",
+ "title": "List and grid standards",
+ "requirement": "Lists MUST preserve row action column plus enter/dblclick fallback behavior. Page size options and URL/localStorage/default precedence MUST be maintained. New lists MUST use initStandardListPage()."
+ },
+ {
+ "id": "GR-UI-DETAIL",
+ "area": "ui",
+ "reviewer": "code",
+ "title": "Detail page standards",
+ "requirement": "MUST use standard detail page contracts for dirty-state and actions. MUST NOT use inline confirm handlers on standardized flows. MUST use shared list/detail partials for standardized UI blocks. New pages MUST use the established layout system. Blocks reused in 2+ pages MUST be extracted as partials (app-{context}-{purpose}.phtml)."
+ },
+ {
+ "id": "GR-UI-A11Y",
+ "area": "ui",
+ "reviewer": "code",
+ "title": "Accessibility",
+ "requirement": "MUST NOT regress a11y basics (labels, roles, keyboard focus) on touched flows. New interactive components MUST use semantic HTML, be keyboard-operable (Tab/Enter/Escape), use ARIA only where necessary, and meet WCAG 2.1 AA contrast."
+ },
+ {
+ "id": "GR-UI-I18N",
+ "area": "ui",
+ "reviewer": "code",
+ "title": "Internationalization completeness",
+ "requirement": "All user-visible strings in phtml views MUST use t('key'). Every new t() key MUST be present in ALL language files (i18n/default_de.json and i18n/default_en.json) in the same merge."
+ },
+ {
+ "id": "GR-UI-REUSE",
+ "area": "ui",
+ "reviewer": "code",
+ "title": "Reuse-first (JS + CSS)",
+ "requirement": "MUST check web/js/core/ and web/js/components/ for existing exports before adding new functions. MUST check existing CSS layers and app-* classes before writing new rules. No duplication of logic from core modules."
+ },
+ {
+ "id": "GR-SEC-001",
+ "area": "security",
+ "reviewer": "security",
+ "title": "CSRF on POST",
+ "requirement": "MUST verify CSRF token on every POST endpoint in pages before processing the request body."
+ },
+ {
+ "id": "GR-SEC-002",
+ "area": "security",
+ "reviewer": "security",
+ "title": "No PII/secrets in logs",
+ "requirement": "MUST NOT write PII (email, names, UUIDs) or secrets (tokens, passwords) to logs or audit metadata without prior redaction."
+ },
+ {
+ "id": "GR-SEC-003",
+ "area": "security",
+ "reviewer": "security",
+ "title": "SQL via prepared statements only",
+ "requirement": "MUST NOT build SQL strings with user-controlled input. All queries MUST go through Repository classes using prepared statements."
+ },
+ {
+ "id": "GR-SEC-004",
+ "area": "security",
+ "reviewer": "security",
+ "title": "No external CDN/remote assets",
+ "requirement": "MUST NOT load JS, CSS, fonts from external CDNs or remote URLs. Third-party libraries MUST be vendored locally and served via assetVersion()."
+ },
+ {
+ "id": "GR-SEC-005",
+ "area": "security",
+ "reviewer": "security",
+ "title": "Encryption via Crypto only",
+ "requirement": "Encryption/decryption MUST use lib/Support/Crypto.php (AES-256-GCM). No raw openssl_encrypt, no base64 as encryption, no hand-rolled crypto."
+ },
+ {
+ "id": "GR-SEC-006",
+ "area": "security",
+ "reviewer": "security",
+ "title": "File uploads in storage/ only",
+ "requirement": "User-uploaded files MUST be stored under storage/ with non-guessable identifiers. MUST NOT be written to web/. MUST be served via PHP controller with authz. MUST validate MIME + extension before storing."
+ },
+ {
+ "id": "GR-SEC-007",
+ "area": "security",
+ "reviewer": "security",
+ "title": "API must not start sessions",
+ "requirement": "API requests (api/ prefix) MUST NOT trigger Session::start(), remember-me cookie handling, or session timeout redirects."
+ },
+ {
+ "id": "GR-SEC-008",
+ "area": "security",
+ "reviewer": "security",
+ "title": "Server-side authorization",
+ "requirement": "MUST enforce authorization server-side in actions and services. UI-only permission checks are insufficient."
+ },
+ {
+ "id": "GR-SEC-009",
+ "area": "security",
+ "reviewer": "security",
+ "title": "Server-side tenant scope",
+ "requirement": "MUST enforce tenant scope server-side. Every query touching tenant-scoped data MUST filter by tenant_id. No cross-tenant data leakage."
+ }
+ ]
+}
diff --git a/.agents/checks/guard-checklist.md b/.agents/checks/guard-checklist.md
new file mode 100644
index 0000000..e4f143c
--- /dev/null
+++ b/.agents/checks/guard-checklist.md
@@ -0,0 +1,45 @@
+# Code Review Checklist
+
+Use this checklist for the Code Reviewer role.
+Guard IDs live in `.agents/checks/guard-catalog.json` (guards where `reviewer` = `code`).
+
+## Architecture & Conventions
+- `GR-CORE-003` request input via requestInput(); no $_POST/$_GET/$_FILES
+- `GR-CORE-007` OpenAPI updated when API endpoints changed
+- `GR-CORE-010` DB schema changes shipped as idempotent scripts in db/updates/
+- `GR-CORE-011` new settings in DB settings table, not config files
+- `GR-CORE-012` successful mutating POSTs use PRG pattern; flash before redirect
+
+## Testing
+- `GR-TEST-001` new/changed Service/Gateway logic has PHPUnit tests (happy + edge case)
+- `GR-TEST-002` new code uses constructor injection; no hidden global state
+
+## UI (skip if backend-only)
+- `GR-UI-014` new views/components handle loading, empty, error, success states
+- `GR-UI-LIST` list/grid standards preserved (row actions, page size, initStandardListPage)
+- `GR-UI-DETAIL` detail page contracts, shared partials, no inline confirm, layout system
+- `GR-UI-A11Y` no a11y regression; new components use semantic HTML, keyboard-operable
+- `GR-UI-I18N` all visible text uses t(); keys present in all language files
+- `GR-UI-REUSE` existing JS/CSS checked before adding new functions/rules
+
+## Quality Gates
+- `QG-001` to `QG-003` are run unless explicitly out of scope
+- `QG-004` structural checks reported when relevant
+- `QG-005` JS smoke for JS-touching changes
+- `QG-006` PHP style check for PHP-touching changes
+
+# Security Review Checklist
+
+Use this checklist for the Security Reviewer role.
+Guard IDs live in `.agents/checks/guard-catalog.json` (guards where `reviewer` = `security`).
+
+## Security
+- `GR-SEC-001` CSRF token verified on every POST before processing
+- `GR-SEC-002` no PII or secrets in logs/audit metadata
+- `GR-SEC-003` all SQL via Repository with prepared statements
+- `GR-SEC-004` no external CDN or remote asset loading
+- `GR-SEC-005` encryption only via lib/Support/Crypto.php
+- `GR-SEC-006` file uploads in storage/ only; authz-gated serving; MIME validated
+- `GR-SEC-007` API requests do not start sessions
+- `GR-SEC-008` authorization enforced server-side
+- `GR-SEC-009` tenant scope enforced server-side; tenant_id filtering on all scoped queries
diff --git a/agent-system/checks/quality-gates.json b/.agents/checks/quality-gates.json
similarity index 92%
rename from agent-system/checks/quality-gates.json
rename to .agents/checks/quality-gates.json
index cfc8f79..b89e97f 100644
--- a/agent-system/checks/quality-gates.json
+++ b/.agents/checks/quality-gates.json
@@ -1,6 +1,6 @@
{
"version": "1.0.0",
- "source": "tools/codex-skills/core-guardrails/references/quality-gates.md",
+ "source": ".agents/skills/core-guardrails/references/quality-gates.md",
"gates": [
{
"id": "QG-001",
@@ -50,7 +50,7 @@
"type": "fast",
"name": "Docs link integrity",
"command": "bin/docs-link-check.sh",
- "note": "Default scanner excludes agent-system/runs/** to avoid historical artifact false positives."
+ "note": "Default scanner excludes .agents/runs/** to avoid historical artifact false positives."
},
{
"id": "QG-009",
diff --git a/.agents/contracts/analyst.schema.json b/.agents/contracts/analyst.schema.json
new file mode 100644
index 0000000..ffd46f8
--- /dev/null
+++ b/.agents/contracts/analyst.schema.json
@@ -0,0 +1,82 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Analyst Output",
+ "type": "object",
+ "required": [
+ "task_id",
+ "issue_summary",
+ "affected_layers",
+ "affected_files",
+ "existing_patterns",
+ "related_tests",
+ "security_surface",
+ "assumptions",
+ "risks_discovered"
+ ],
+ "properties": {
+ "task_id": { "type": "string", "minLength": 1 },
+ "issue_summary": { "type": "string", "minLength": 1 },
+ "affected_layers": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "enum": ["Repository", "Service", "pages", "templates", "web/js", "web/css", "modules", "config", "db", "bin"]
+ }
+ },
+ "affected_files": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["path", "role"],
+ "properties": {
+ "path": { "type": "string", "minLength": 1 },
+ "role": { "type": "string", "minLength": 1 }
+ },
+ "additionalProperties": false
+ }
+ },
+ "existing_patterns": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Partials, helpers, services, or conventions already in use for this area."
+ },
+ "related_tests": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Existing test files relevant to the change."
+ },
+ "security_surface": {
+ "type": "object",
+ "properties": {
+ "authz_relevant": { "type": "boolean" },
+ "tenant_scope_relevant": { "type": "boolean" },
+ "input_handling": { "type": "boolean" },
+ "crypto_relevant": { "type": "boolean" },
+ "file_upload_relevant": { "type": "boolean" },
+ "notes": { "type": "string" }
+ },
+ "additionalProperties": false
+ },
+ "module_context": {
+ "type": "object",
+ "description": "Present only when a module is involved.",
+ "properties": {
+ "module_id": { "type": "string" },
+ "active": { "type": "boolean" },
+ "manifest_declarations": { "type": "array", "items": { "type": "string" } }
+ },
+ "additionalProperties": false
+ },
+ "assumptions": {
+ "type": "array",
+ "items": { "type": "string" },
+ "description": "Ambiguities the Planner must resolve."
+ },
+ "risks_discovered": {
+ "type": "array",
+ "items": { "type": "string" }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/agent-system/contracts/executor.schema.json b/.agents/contracts/executor.schema.json
similarity index 97%
rename from agent-system/contracts/executor.schema.json
rename to .agents/contracts/executor.schema.json
index 2513a27..525b8d4 100644
--- a/agent-system/contracts/executor.schema.json
+++ b/.agents/contracts/executor.schema.json
@@ -38,7 +38,7 @@
"properties": {
"guard_id": {
"type": "string",
- "pattern": "^GR-[A-Z]+-[0-9]{3}$"
+ "pattern": "^GR-(CORE|TEST|UI|SEC)-[A-Z0-9]{3,}$"
},
"status": { "type": "string", "enum": ["pass", "fail", "n/a"] },
"evidence": { "type": "string", "minLength": 1 }
diff --git a/agent-system/contracts/finalizer.schema.json b/.agents/contracts/finalizer.schema.json
similarity index 60%
rename from agent-system/contracts/finalizer.schema.json
rename to .agents/contracts/finalizer.schema.json
index 379e4a3..f948f2e 100644
--- a/agent-system/contracts/finalizer.schema.json
+++ b/.agents/contracts/finalizer.schema.json
@@ -5,7 +5,8 @@
"required": [
"task_id",
"ready_to_finalize",
- "guard_review",
+ "code_review",
+ "security_review",
"acceptance_review",
"ci_status",
"final_action"
@@ -13,7 +14,8 @@
"properties": {
"task_id": { "type": "string", "minLength": 1 },
"ready_to_finalize": { "type": "boolean" },
- "guard_review": { "type": "string", "enum": ["pass", "fail"] },
+ "code_review": { "type": "string", "enum": ["pass", "fail"] },
+ "security_review": { "type": "string", "enum": ["pass", "fail"] },
"acceptance_review": { "type": "string", "enum": ["pass", "fail"] },
"ci_status": { "type": "string", "enum": ["pass", "fail", "unknown"] },
"final_action": { "type": "string", "enum": ["commit", "merge", "hold"] },
@@ -33,15 +35,14 @@
},
{
"if": {
- "properties": {
- "final_action": { "enum": ["commit", "merge"] }
- },
+ "properties": { "final_action": { "enum": ["commit", "merge"] } },
"required": ["final_action"]
},
"then": {
"properties": {
"ready_to_finalize": { "const": true },
- "guard_review": { "const": "pass" },
+ "code_review": { "const": "pass" },
+ "security_review": { "const": "pass" },
"acceptance_review": { "const": "pass" },
"ci_status": { "const": "pass" }
}
@@ -49,55 +50,47 @@
},
{
"if": {
- "properties": { "ci_status": { "const": "fail" } },
+ "properties": { "ci_status": { "enum": ["fail", "unknown"] } },
"required": ["ci_status"]
},
"then": {
- "properties": { "final_action": { "enum": ["hold"] } }
+ "properties": { "final_action": { "const": "hold" } }
}
},
{
"if": {
- "properties": {
- "guard_review": { "const": "fail" }
- },
- "required": ["guard_review"]
+ "properties": { "code_review": { "const": "fail" } },
+ "required": ["code_review"]
},
"then": {
- "properties": { "final_action": { "enum": ["hold"] } }
+ "properties": { "final_action": { "const": "hold" } }
}
},
{
"if": {
- "properties": {
- "acceptance_review": { "const": "fail" }
- },
+ "properties": { "security_review": { "const": "fail" } },
+ "required": ["security_review"]
+ },
+ "then": {
+ "properties": { "final_action": { "const": "hold" } }
+ }
+ },
+ {
+ "if": {
+ "properties": { "acceptance_review": { "const": "fail" } },
"required": ["acceptance_review"]
},
"then": {
- "properties": { "final_action": { "enum": ["hold"] } }
+ "properties": { "final_action": { "const": "hold" } }
}
},
{
"if": {
- "properties": {
- "ci_status": { "const": "unknown" }
- },
- "required": ["ci_status"]
- },
- "then": {
- "properties": { "final_action": { "enum": ["hold"] } }
- }
- },
- {
- "if": {
- "properties": {
- "ready_to_finalize": { "const": false }
- },
+ "properties": { "ready_to_finalize": { "const": false } },
"required": ["ready_to_finalize"]
},
"then": {
- "properties": { "final_action": { "enum": ["hold"] } }
+ "properties": { "final_action": { "const": "hold" } }
}
}
],
diff --git a/agent-system/contracts/planner.schema.json b/.agents/contracts/planner.schema.json
similarity index 86%
rename from agent-system/contracts/planner.schema.json
rename to .agents/contracts/planner.schema.json
index bbd0290..052bbe2 100644
--- a/agent-system/contracts/planner.schema.json
+++ b/.agents/contracts/planner.schema.json
@@ -4,6 +4,7 @@
"type": "object",
"required": [
"task_id",
+ "analysis_ref",
"summary",
"scope",
"guardrails",
@@ -15,6 +16,7 @@
],
"properties": {
"task_id": { "type": "string", "minLength": 1 },
+ "analysis_ref": { "type": "string", "minLength": 1, "description": "Path to analysis.json" },
"summary": { "type": "string", "minLength": 1 },
"assumptions": {
"type": "array",
@@ -38,7 +40,7 @@
"minItems": 1,
"items": {
"type": "string",
- "pattern": "^GR-[A-Z]+-[0-9]{3}$"
+ "pattern": "^GR-(CORE|TEST|UI|SEC)-[A-Z0-9]{3,}$"
}
},
"required_quality_gate_ids": {
@@ -83,7 +85,7 @@
"minItems": 1,
"items": {
"type": "string",
- "pattern": "^GR-[A-Z]+-[0-9]{3}$"
+ "pattern": "^GR-(CORE|TEST|UI|SEC)-[A-Z0-9]{3,}$"
}
}
},
@@ -102,16 +104,14 @@
},
"ux_notes": {
"type": "object",
- "description": "Required for tasks touching UI (templates, phtml pages, web/js, web/css). Leave fields as empty arrays if not applicable.",
+ "description": "Required for tasks touching UI. Leave fields as empty arrays if not applicable.",
"properties": {
"affected_patterns": {
"type": "array",
- "description": "Existing UI patterns or partials touched by this task.",
"items": { "type": "string" }
},
"ui_states_required": {
"type": "array",
- "description": "UI states the new view/component must handle: loading, empty, error, success.",
"items": {
"type": "string",
"enum": ["loading", "empty", "error", "success"]
@@ -119,7 +119,6 @@
},
"a11y_touchpoints": {
"type": "array",
- "description": "New interactive elements that must be keyboard-operable and use semantic HTML.",
"items": { "type": "string" }
}
},
diff --git a/agent-system/contracts/reviewer-acceptance.schema.json b/.agents/contracts/reviewer-acceptance.schema.json
similarity index 98%
rename from agent-system/contracts/reviewer-acceptance.schema.json
rename to .agents/contracts/reviewer-acceptance.schema.json
index cc4248d..254c8a8 100644
--- a/agent-system/contracts/reviewer-acceptance.schema.json
+++ b/.agents/contracts/reviewer-acceptance.schema.json
@@ -1,6 +1,6 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
- "title": "Acceptance Reviewer Output",
+ "title": "Acceptance Tester Output",
"type": "object",
"required": ["task_id", "verdict", "checked_criterion_ids", "checks"],
"properties": {
diff --git a/.agents/contracts/reviewer-code.schema.json b/.agents/contracts/reviewer-code.schema.json
new file mode 100644
index 0000000..1b512f4
--- /dev/null
+++ b/.agents/contracts/reviewer-code.schema.json
@@ -0,0 +1,46 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Code Reviewer Output",
+ "type": "object",
+ "required": ["task_id", "verdict", "checked_guard_ids", "checked_quality_gate_ids", "findings"],
+ "properties": {
+ "task_id": { "type": "string", "minLength": 1 },
+ "verdict": { "type": "string", "enum": ["pass", "fail"] },
+ "checked_guard_ids": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "pattern": "^GR-(CORE|TEST|UI)-[A-Z0-9]{3,}$"
+ }
+ },
+ "checked_quality_gate_ids": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "pattern": "^QG-[0-9]{3}$"
+ }
+ },
+ "findings": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["id", "severity", "rule_ref", "summary", "file"],
+ "properties": {
+ "id": { "type": "string", "minLength": 1 },
+ "severity": { "type": "string", "enum": ["low", "medium", "high", "critical"] },
+ "rule_ref": {
+ "type": "string",
+ "pattern": "^(GR-(CORE|TEST|UI)-[A-Z0-9]{3,}|QG-[0-9]{3})$"
+ },
+ "summary": { "type": "string", "minLength": 1 },
+ "file": { "type": "string", "minLength": 1 },
+ "fix_required": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "additionalProperties": false
+}
diff --git a/.agents/contracts/reviewer-security.schema.json b/.agents/contracts/reviewer-security.schema.json
new file mode 100644
index 0000000..4255357
--- /dev/null
+++ b/.agents/contracts/reviewer-security.schema.json
@@ -0,0 +1,61 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "title": "Security Reviewer Output",
+ "type": "object",
+ "required": ["task_id", "verdict", "checked_guard_ids", "findings"],
+ "properties": {
+ "task_id": { "type": "string", "minLength": 1 },
+ "verdict": { "type": "string", "enum": ["pass", "fail"] },
+ "checked_guard_ids": {
+ "type": "array",
+ "minItems": 1,
+ "items": {
+ "type": "string",
+ "pattern": "^GR-SEC-[0-9]{3}$"
+ }
+ },
+ "findings": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "required": ["id", "severity", "rule_ref", "summary", "file"],
+ "properties": {
+ "id": { "type": "string", "minLength": 1 },
+ "severity": {
+ "type": "string",
+ "enum": ["low", "medium", "high", "critical"],
+ "description": "critical=exploitable in prod, high=likely exploitable, medium=defense-in-depth gap, low=minor hardening"
+ },
+ "rule_ref": {
+ "type": "string",
+ "pattern": "^GR-SEC-[0-9]{3}$"
+ },
+ "summary": { "type": "string", "minLength": 1 },
+ "file": { "type": "string", "minLength": 1 },
+ "line_range": { "type": "string", "description": "e.g. '42-58'" },
+ "fix_required": { "type": "string" }
+ },
+ "additionalProperties": false
+ }
+ }
+ },
+ "allOf": [
+ {
+ "if": {
+ "properties": {
+ "findings": {
+ "contains": {
+ "type": "object",
+ "properties": { "severity": { "enum": ["critical", "high"] } },
+ "required": ["severity"]
+ }
+ }
+ }
+ },
+ "then": {
+ "properties": { "verdict": { "const": "fail" } }
+ }
+ }
+ ],
+ "additionalProperties": false
+}
diff --git a/.agents/prompts/analyst.md b/.agents/prompts/analyst.md
new file mode 100644
index 0000000..af3ebba
--- /dev/null
+++ b/.agents/prompts/analyst.md
@@ -0,0 +1,24 @@
+# Analyst Prompt Baseline
+
+Goal:
+- triage an issue or feature request by reading the codebase and producing a context brief for the Planner
+
+Input:
+- issue description or feature request (free-form)
+
+Required actions:
+- identify affected layers (Repository, Service, pages, templates, web/js, web/css, modules)
+- identify affected files and existing patterns (partials, helpers, services, repositories)
+- check for related existing tests
+- identify security surface (authz, tenant scope, input handling, crypto, file uploads)
+- note any risks or constraints discovered during exploration
+
+Output:
+- valid JSON by `.agents/contracts/analyst.schema.json`
+
+Rules:
+- DO NOT propose solutions — only gather and structure context
+- list concrete file paths, not vague layer references
+- if the issue is ambiguous, list assumptions that the Planner must resolve
+- if a module is involved, verify it is active and note its manifest declarations
+- keep the brief factual — no opinions on priority or effort
diff --git a/.agents/prompts/executor.md b/.agents/prompts/executor.md
new file mode 100644
index 0000000..e052700
--- /dev/null
+++ b/.agents/prompts/executor.md
@@ -0,0 +1,21 @@
+# Executor Prompt Baseline
+
+Goal:
+- implement the approved plan with minimal scope drift
+
+Input:
+- `plan.json` from Planner
+
+Required references:
+- `.agents/checks/guard-catalog.json`
+- `.agents/checks/quality-gates.json`
+
+Output:
+- valid JSON by `.agents/contracts/executor.schema.json`
+
+Rules:
+- only implement items in plan scope
+- run relevant quality gates and include results
+- provide guard evidence for each required guard ID
+- list each changed file with a short reason
+- if blocked, set status to `blocked` and list exact blockers
diff --git a/.agents/prompts/finalizer.md b/.agents/prompts/finalizer.md
new file mode 100644
index 0000000..1c32ccd
--- /dev/null
+++ b/.agents/prompts/finalizer.md
@@ -0,0 +1,18 @@
+# Finalizer Prompt Baseline
+
+Goal:
+- finalize only when all three reviews pass and quality gates are green
+
+Input:
+- `execution-report.json`
+- `review-code.json`
+- `review-security.json`
+- `review-acceptance.json`
+
+Output:
+- valid JSON by `.agents/contracts/finalizer.schema.json`
+
+Rules:
+- do not finalize if any reviewer verdict is `fail`
+- do not finalize unless `code_review=pass`, `security_review=pass`, `acceptance_review=pass`, and `ci_status=pass`
+- set `final_action` to `hold` and populate `hold_reason` whenever action is `hold`
diff --git a/.agents/prompts/planner.md b/.agents/prompts/planner.md
new file mode 100644
index 0000000..843548c
--- /dev/null
+++ b/.agents/prompts/planner.md
@@ -0,0 +1,29 @@
+# Planner Prompt Baseline
+
+Goal:
+- transform the analysis brief into a decision-complete implementation plan
+
+Input:
+- `analysis.json` from Analyst
+
+Required references:
+- `.agents/checks/guard-catalog.json`
+- `.agents/checks/quality-gates.json`
+
+Output:
+- valid JSON by `.agents/contracts/planner.schema.json`
+
+Rules:
+- define explicit in-scope and out-of-scope
+- define measurable success criteria with stable IDs (`SC-001`, `SC-002`, ...)
+- select required guard IDs from guard catalog (code reviewer guards + security reviewer guards)
+- select required quality gate IDs from quality gates
+- include at least one risk with mitigation
+- keep implementation steps actionable and ordered
+- reference concrete file paths from the analysis brief — do not re-explore the codebase
+
+UI task rules (apply when the task touches templates, pages/*.phtml, or web/js|css):
+- populate `ux_notes.affected_patterns`: list every existing UI pattern or partial being touched
+- populate `ux_notes.ui_states_required`: declare loading, empty, error, success states (GR-UI-014); omit only with explicit justification
+- populate `ux_notes.a11y_touchpoints`: list every new interactive element (GR-UI-A11Y)
+- if any `ux_notes` field is not applicable, set it to `[]` — do not omit the field
diff --git a/.agents/prompts/reviewer-acceptance.md b/.agents/prompts/reviewer-acceptance.md
new file mode 100644
index 0000000..f925b18
--- /dev/null
+++ b/.agents/prompts/reviewer-acceptance.md
@@ -0,0 +1,22 @@
+# Acceptance Tester Prompt Baseline
+
+Goal:
+- verify the feature or fix is implemented exactly as requested
+
+Input:
+- code diff
+- `plan.json`
+- `execution-report.json`
+
+Required references:
+- `.agents/checks/acceptance-checklist.md`
+
+Output:
+- valid JSON by `.agents/contracts/reviewer-acceptance.schema.json`
+
+Rules:
+- evaluate each `plan.json` success criterion by its `SC-*` ID
+- include evidence for pass/fail checks
+- return `checked_criterion_ids` and per-check `criterion_id` values that match the plan
+- list missing behavior explicitly when verdict is `fail`
+- verify no out-of-scope changes were silently introduced
diff --git a/.agents/prompts/reviewer-code.md b/.agents/prompts/reviewer-code.md
new file mode 100644
index 0000000..82841b8
--- /dev/null
+++ b/.agents/prompts/reviewer-code.md
@@ -0,0 +1,37 @@
+# Code Reviewer Prompt Baseline
+
+Goal:
+- verify code quality, architecture compliance, and convention adherence
+
+Input:
+- code diff
+- `execution-report.json`
+- `plan.json`
+
+Required references:
+- `.agents/checks/guard-catalog.json` (guards where `reviewer` = `code`)
+- `.agents/checks/quality-gates.json`
+
+Output:
+- valid JSON by `.agents/contracts/reviewer-code.schema.json`
+
+Scope — verify these guards:
+- GR-CORE-003: request input contract
+- GR-CORE-007: OpenAPI sync (if API touched)
+- GR-CORE-010: idempotent DB scripts (if schema touched)
+- GR-CORE-011: settings in DB (if settings added)
+- GR-CORE-012: PRG pattern (if POST actions added/changed)
+- GR-TEST-001: tests ship with new logic
+- GR-TEST-002: code is testable
+- GR-UI-014: UI state completeness (if UI touched)
+- GR-UI-LIST: list/grid standards (if lists touched)
+- GR-UI-DETAIL: detail page standards (if detail pages touched)
+- GR-UI-A11Y: accessibility (if UI touched)
+- GR-UI-I18N: i18n completeness (if visible text touched)
+- GR-UI-REUSE: reuse-first (if JS/CSS added)
+
+Rules:
+- findings MUST reference a guard or gate ID
+- findings MUST include file path
+- use `fail` only when changes are required before finalize
+- skip guards not relevant to the change (e.g. skip UI guards for backend-only changes)
diff --git a/.agents/prompts/reviewer-security.md b/.agents/prompts/reviewer-security.md
new file mode 100644
index 0000000..c475bec
--- /dev/null
+++ b/.agents/prompts/reviewer-security.md
@@ -0,0 +1,34 @@
+# Security Reviewer Prompt Baseline
+
+Goal:
+- verify the change does not introduce security vulnerabilities or weaken existing protections
+
+Input:
+- code diff
+- `execution-report.json`
+- `plan.json`
+
+Required references:
+- `.agents/checks/guard-catalog.json` (guards where `reviewer` = `security`)
+
+Output:
+- valid JSON by `.agents/contracts/reviewer-security.schema.json`
+
+Scope — verify these guards:
+- GR-SEC-001: CSRF token on POST
+- GR-SEC-002: no PII/secrets in logs or audit metadata
+- GR-SEC-003: SQL via prepared statements only
+- GR-SEC-004: no external CDN or remote asset loading
+- GR-SEC-005: encryption only via Crypto.php
+- GR-SEC-006: file uploads stored in storage/ only
+- GR-SEC-007: API must not start sessions
+- GR-SEC-008: server-side authorization enforced
+- GR-SEC-009: server-side tenant scope enforced
+
+Rules:
+- findings MUST reference a GR-SEC-* guard ID
+- findings MUST include file path and line range where possible
+- severity MUST reflect actual exploitability: `critical` = exploitable in production, `high` = likely exploitable, `medium` = defense-in-depth gap, `low` = minor hardening
+- use `fail` verdict for any `critical` or `high` finding
+- skip guards not relevant to the change (e.g. skip GR-SEC-005 if no crypto touched)
+- for multi-tenant data flows, trace the query path from action → service → repository and verify tenant_id filtering at every level
diff --git a/agent-system/runs/.gitkeep b/.agents/runs/.gitkeep
similarity index 100%
rename from agent-system/runs/.gitkeep
rename to .agents/runs/.gitkeep
diff --git a/.agents/skills/core-guardrails/SKILL.md b/.agents/skills/core-guardrails/SKILL.md
new file mode 100644
index 0000000..a4d2ea3
--- /dev/null
+++ b/.agents/skills/core-guardrails/SKILL.md
@@ -0,0 +1,42 @@
+---
+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.
+---
+
+# Core Guardrails
+
+## Ziel
+
+Durchgaengig dieselben technischen Grenzen durchsetzen, damit Core robust, wartbar und vorhersagbar bleibt.
+
+## Verbindliche Regeln
+
+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/**`).
+3. MUST AuthZ/RBAC serverseitig durchsetzen und im UI nur ueber `$viewAuth` steuern.
+4. MUST Security-Guards einhalten (CSRF auf POST-Endpunkten, keine unredacted PII/Secrets in Logs/Audit-Meta, SQL nur vorbereitet ueber 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).
+6. MUST Frontend-Hardcuts einhalten (keine inline ESM-Module in Templates/Pages, JS-`src` mit `assetVersion(...)`, zentrale Telemetrie fuer relevante Frontend-Warnings/Fetch-Fehler).
+7. MUST Quality-Gates ausfuehren und Ergebnis transparent berichten.
+8. MUST bei Konflikten den regelkonformen Weg waehlen oder den Konflikt als Blocker markieren.
+9. MUST NOT Extension-Architektur erfinden; dieses Thema ist aktuell Out of Scope.
+
+## Vorgehen je Aufgabe
+
+1. Scope bestimmen (`core`, `ui`, `api`, `mixed`).
+2. Vorhandene Contracts zuerst suchen, dann wiederverwenden.
+3. Aenderung minimal halten, keine Seiteneffekte ohne Grund.
+4. Nach Aenderung die relevanten Gates ausfuehren.
+5. Rest-Risiken explizit benennen.
+
+## Out of Scope
+
+- Extension-Mechanik (Ablageorte, Hooks, Lifecycle) wird hier nicht standardisiert.
+- Fuer Extensions nur den Status "nicht definiert" dokumentieren, keine impliziten Regeln erfinden.
+
+## Referenzen
+
+- Core-Boundaries: `references/boundaries-core.md`
+- UI-Boundaries: `references/boundaries-ui.md`
+- Quality-Gates: `references/quality-gates.md`
+- Source-Map zu Projekt-Doku: `references/source-map.md`
diff --git a/.agents/skills/core-guardrails/references/boundaries-core.md b/.agents/skills/core-guardrails/references/boundaries-core.md
new file mode 100644
index 0000000..c0d20b1
--- /dev/null
+++ b/.agents/skills/core-guardrails/references/boundaries-core.md
@@ -0,0 +1,45 @@
+# Core Boundaries (MUST / MUST NOT)
+
+## Schichtgrenzen
+
+1. MUST SQL nur in `lib/Repository/**` halten.
+2. MUST Business-Logik in `lib/Service/**` halten.
+3. MUST `pages/**` auf Input, Auth/AuthZ, Orchestrierung, Response begrenzen.
+4. MUST NOT Business- oder Permission-Logik in `templates/**` legen.
+
+## Instanziierung
+
+1. MUST in Actions/Helpern/Templates nur `app(Foo::class)` verwenden.
+2. MUST NOT in `pages/**` direkt `new ...Service|Gateway|Repository` nutzen.
+3. MUST NOT in `lib/Service/**` (ausser `*Factory.php`) direkt `new ...Factory|Service|Gateway|Repository` nutzen.
+4. MUST NOT in `pages/admin/**` `Factory::class` referenzieren oder `app(...Factory::class)->create...` nutzen.
+
+## Input / Validation
+
+1. MUST Input in `pages/**` ueber `requestInput()` lesen.
+2. MUST NOT `$_POST`, `$_GET`, `$_FILES`, `REQUEST_METHOD` in `pages/**` verwenden.
+3. MUST NOT `$_SESSION`, `$_SERVER`, `$_COOKIE`, `$_ENV` in `pages/**` direkt verwenden.
+4. MUST Form-Validation ueber `FormErrors` fuehren.
+5. MUST API-Validation-Fehler ueber `ApiResponse::validationFromFormErrors(...)` ausgeben.
+6. MUST NOT `ApiResponse::readJsonBody(...)` in `pages/api/v1/**` verwenden.
+
+## List-Data Endpoints
+
+1. MUST `pages/**/data().php` als GET-only Endpunkte umsetzen (`gridRequireGetRequest()`).
+2. MUST Query-Filter ueber `gridParseFilters(...)` + `filter-schema.php` normalisieren.
+3. MUST NOT Query-Filter inline/parallele Alias-Parser in `data().php` neu einfuehren.
+
+## Security / API
+
+1. MUST AuthZ serverseitig erzwingen.
+2. MUST Tenant-Scope serverseitig erzwingen.
+3. MUST CSRF auf POST-Endpunkten vor Body-Verarbeitung verifizieren.
+4. MUST NOT PII/Secrets unredacted in Logs oder Audit-Metadata schreiben.
+5. MUST SQL nur ueber Repository mit prepared statements ausfuehren.
+6. MUST extern UUID-first bleiben.
+7. MUST API-Fehler konsistent halten (`error`, optional `errors`).
+8. MUST OpenAPI im selben Merge aktualisieren, wenn API geaendert wird.
+
+## Pruef-Hinweis
+
+- Harte Repo-Gates: `tests/Architecture/CoreStarterkitContractTest.php`
diff --git a/.agents/skills/core-guardrails/references/boundaries-ui.md b/.agents/skills/core-guardrails/references/boundaries-ui.md
new file mode 100644
index 0000000..5d8fd28
--- /dev/null
+++ b/.agents/skills/core-guardrails/references/boundaries-ui.md
@@ -0,0 +1,60 @@
+# UI Boundaries (MUST / MUST NOT)
+
+## Listen-Standard
+
+1. MUST Listen ueber `initStandardListPage(...)` initialisieren.
+2. MUST Filter-Toolbar ueber zentrale Helper/Partials rendern.
+3. MUST aktive Chips/Drawer-Verhalten ueber Standard-Contracts laufen lassen.
+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.
+6. MUST rowInteraction-Standard fuer 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).
+
+## Detailseiten-Standard
+
+1. MUST Hauptformular mit `data-standard-detail-form="1"` markieren.
+2. MUST Unsaved/Shortcut/Dirty-State ueber `initStandardDetailPage(...)` nutzen.
+3. MUST Detail-Aktionen ueber 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.
+
+## Confirm + Telemetry Standard
+
+1. MUST Confirm-Dialoge zentral ueber den globalen Confirm-Service abwickeln.
+2. MUST NOT `window.confirm(...)` in App-JS oder inline HTML-Handlern verwenden.
+3. MUST relevante Frontend-Warnpfade zentral ueber Frontend-Telemetrie erfassen.
+4. MUST NOT relevante UX-/Fehlerpfade nur als Console-Warnung implementieren.
+
+## Frontend Page-Module Hard Cut
+
+1. MUST Seiteninitialisierung ueber dedizierte Module unter `web/js/pages/*` umsetzen.
+2. MUST Page-Config ueber `script[type="application/json"]` + `readPageConfig(...)` bereitstellen/lesen.
+3. MUST alle JS-`src` in `pages/**` und `templates/**` ueber `assetVersion('...js...')` laden.
+4. MUST NOT inline `` in `pages/**` oder `templates/**` verwenden.
+
+## Component Lifecycle Runtime
+
+1. MUST migrierte Komponenten ueber `init(root, config)` + `destroy()` standardisieren.
+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.
+4. MUST Runtime-Konfiguration ueber Page-Config (`readPageConfig(...)`) beziehen, keine verteilten Ad-hoc-Globals.
+5. MUST host-gebundene UI-Komponenten ueber 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.
+
+## Shared Partials
+
+1. MUST Listen-Titlebar ueber `templates/partials/app-list-titlebar.phtml` rendern.
+2. MUST List-Tabs ueber `templates/partials/app-list-tabs.phtml` rendern.
+3. MUST Purge-Action ueber `templates/partials/app-list-purge-action.phtml` rendern.
+4. MUST Detail-Validation-Summary ueber `templates/partials/app-details-validation-summary.phtml` rendern.
+
+## RBAC in Templates
+
+1. MUST UI-Capabilities nur ueber `$viewAuth` lesen.
+2. MUST NOT `can('...')` in Templates verwenden.
+
+## CSS Boundaries
+
+1. MUST Styles moeglichst lokal in `web/css/components`, `web/css/layout` oder `web/css/pages` halten.
+2. MUST NOT globale unscoped Overrides ohne klaren Scope einfuehren.
+3. MUST Vendor-Overrides nur in `web/css/vendor-overrides/**` pflegen.
+4. MUST bestehende CSS-Variablen bevorzugen statt neue Inline-Farbwerte zu verteilen.
diff --git a/.agents/skills/core-guardrails/references/quality-gates.md b/.agents/skills/core-guardrails/references/quality-gates.md
new file mode 100644
index 0000000..fa890ba
--- /dev/null
+++ b/.agents/skills/core-guardrails/references/quality-gates.md
@@ -0,0 +1,40 @@
+# Quality Gates
+
+## Pflicht-Gates
+
+1. `docker compose exec php vendor/bin/phpunit`
+2. `docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress`
+3. `docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php`
+
+## Struktur-Gates (schnell)
+
+```bash
+(rg 'new\s+[^\s(]+Factory\(' lib/Service || true) | wc -l
+(rg --glob '!**/*Factory.php' 'new\s+[^\s(]+(Service|Gateway|Repository)\(' lib/Service || true) | wc -l
+(rg "\b(accessServicesFactory|directoryServicesFactory|userServicesFactory|authServicesFactory|tenantServicesFactory|settingServicesFactory|userRepositoryFactory|authRepositoryFactory|authGatewayFactory)\(" pages lib templates web || true) | wc -l
+(rg -n 'new\s+[^\s(]+(Service|Gateway|Repository)\(' pages -g '*.php' || true) | wc -l
+(rg -n '\bDB::' pages -g '*.php' || true) | wc -l
+(rg -n 'app\([^\n]*Factory::class\)->create' pages/admin -g '*.php' || true) | wc -l
+(rg -n 'Factory::class' pages/admin -g '*.php' || true) | wc -l
+(rg -n 'ApiResponse::readJsonBody\(' pages/api/v1 -g '*.php' || true) | wc -l
+```
+
+Erwartung: jeweils `0`.
+
+## JS-Scope
+
+- Bei JS-Aenderungen Browser-Smoke mit DevTools-Console durchfuehren.
+- Erwartung: keine neuen JS-Errors.
+
+## Docs-/Skills-Gates (schnell)
+
+1. `bin/docs-link-check.sh`
+ - prueft Doku-Links in `README.md`, `docs/**`, `.agents/skills/**` und `.agents/**`
+ - `.agents/runs/**` ist standardmaessig ausgeschlossen (historische Artefakte)
+2. `bin/codex-skills-sync.sh --check`
+ - prueft Drift zwischen Repo-Skills und `~/.codex/skills`
+
+## Berichtspflicht
+
+- Bei jedem Gate klar angeben: `passed` / `failed` / `not run`.
+- Bei `failed` den blocker explizit benennen.
diff --git a/.agents/skills/core-guardrails/references/source-map.md b/.agents/skills/core-guardrails/references/source-map.md
new file mode 100644
index 0000000..3f6ea6f
--- /dev/null
+++ b/.agents/skills/core-guardrails/references/source-map.md
@@ -0,0 +1,25 @@
+# Source Map (Single Source of Truth)
+
+## Kernregeln
+
+- Architektur- und Schichtgrenzen: `/docs/explanation-architektur.md`
+- `lib/**`-Regeln und Instanziierung: `/docs/reference-lib-standards.md`
+- Kurzkonventionen: `/docs/reference-konventionen.md`
+- Request/Input/Validation: `/docs/howto-request-input-validation.md`
+
+## UI-/Frontend-Contracts
+
+- JavaScript-Standards und Wrapper: `/docs/reference-frontend-javascript.md`
+- CSS-Standards und Scope-Regeln: `/docs/reference-frontend-css.md`
+- DoD-Checks: `/docs/reference-entwickler-checkliste.md`
+- Rollen-Workflow (Planner/Executor/Reviewer/Finalizer): `/.agents/workflow.md`
+
+## Security / RBAC
+
+- RBAC-Erweiterungen: `/docs/howto-rbac-permissions-playbook.md`
+- Sicherheitsmodell: `/docs/explanation-sicherheitsmodell.md`
+
+## Betrieb und Qualitaetschecks
+
+- Entwickler-Checkliste: `/docs/reference-entwickler-checkliste.md`
+- Architektur-Request-Flow: `/docs/tutorial-04-architektur-request-flow.md`
diff --git a/.agents/skills/starterkit-grid-standards/SKILL.md b/.agents/skills/starterkit-grid-standards/SKILL.md
new file mode 100644
index 0000000..501ccfe
--- /dev/null
+++ b/.agents/skills/starterkit-grid-standards/SKILL.md
@@ -0,0 +1,38 @@
+---
+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).
+---
+
+# Starterkit Grid Standards
+
+## Ziel
+
+GridJS-Listen projektweit einheitlich, zuganglich und wartbar halten.
+
+## Wann verwenden
+
+1. Neue Listen mit `initStandardListPage(...)` oder `createServerGrid(...)`.
+2. Refactor von Legacy-Listen.
+3. Review von UX/Accessibility in Listen.
+
+## Verbindliche Regeln
+
+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.
+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).
+5. MUST Filter-Drawer/Chips ueber den Standard-Contract nutzen, keine parallelen Custom-Patterns.
+6. MUST NOT pro Seite eigene Row-Action-Hacks bauen, wenn der Factory-Contract ausreicht.
+
+## Vorgehen
+
+1. Ist-Liste gegen `references/list-checklist.md` pruefen.
+2. Fehlende Standards zuerst zentral in der Factory loesen, danach nur minimale Seitenanpassungen.
+3. UI/CSS nur als additive Erweiterung zu bestehenden Komponenten.
+4. Relevante Gates und Browser-Smoke (Keyboard + Console) ausfuehren.
+
+## Referenzen
+
+- Checkliste: `references/list-checklist.md`
+- Row Interaction: `references/row-interaction-standard.md`
+- Page Size: `references/page-size-standard.md`
diff --git a/.agents/skills/starterkit-grid-standards/agents/openai.yaml b/.agents/skills/starterkit-grid-standards/agents/openai.yaml
new file mode 100644
index 0000000..929f3f9
--- /dev/null
+++ b/.agents/skills/starterkit-grid-standards/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "Starterkit Grid Standards"
+ short_description: "Standardize GridJS lists and accessibility patterns"
+ default_prompt: "Use $starterkit-grid-standards to standardize a list page with rowInteraction, pageSize, and filter drawer contracts."
diff --git a/.agents/skills/starterkit-grid-standards/references/list-checklist.md b/.agents/skills/starterkit-grid-standards/references/list-checklist.md
new file mode 100644
index 0000000..c196787
--- /dev/null
+++ b/.agents/skills/starterkit-grid-standards/references/list-checklist.md
@@ -0,0 +1,30 @@
+# List Checklist
+
+## Bootstrap
+
+1. `initStandardListPage({ grid, filters, hooks })` verwendet.
+2. `filterSchema` aus Server-Config uebergeben.
+3. `urlSync` bewusst gesetzt.
+
+## Interaction
+
+1. `rowDblClick.getUrl(...)` liefert stabile Ziel-URL oder leer.
+2. Keine Doppel-Action-Spalte (Factory auto-action vs explizite actions).
+3. Enter/Pointer/Focus fuer oeffnbare Rows funktionieren.
+
+## Pagination + URL
+
+1. Page-size Optionen nur `10/25/50/100`.
+2. URL Sync pruefen (`page`, `order`, `dir`, `limit`).
+3. Default-Werte erzeugen saubere URL.
+
+## Filter UX
+
+1. Search sichtbar.
+2. Drawer-Filter und Chips intakt.
+3. Reset/Apply Verhalten reproduzierbar.
+
+## Regression
+
+1. Sortierung, Selection/Bulk, Doppelklick unveraendert.
+2. Keine neuen Console Errors.
diff --git a/.agents/skills/starterkit-grid-standards/references/page-size-standard.md b/.agents/skills/starterkit-grid-standards/references/page-size-standard.md
new file mode 100644
index 0000000..2da7f2f
--- /dev/null
+++ b/.agents/skills/starterkit-grid-standards/references/page-size-standard.md
@@ -0,0 +1,20 @@
+# Page Size Standard
+
+## Globale Defaults
+
+1. Optionen: `10`, `25`, `50`, `100`.
+2. Default: `10`.
+3. Prioritaet: `URL > localStorage > Default`.
+4. Placement: obere `app-list-toolbar`, rechts ausgerichtet.
+
+## URL Contract
+
+1. Param: `limit`.
+2. Bei Default-Limit optional clean URL (Param entfernen).
+3. Bei Hard-Reload muss gesetztes URL-Limit reproduzierbar sein.
+
+## Verhalten
+
+1. Aenderung der Seitengroesse setzt Seite auf 1.
+2. Sortierung/Filter bleiben erhalten.
+3. LocalStorage-Key pro Tabelle (path + dataUrl + container).
diff --git a/.agents/skills/starterkit-grid-standards/references/row-interaction-standard.md b/.agents/skills/starterkit-grid-standards/references/row-interaction-standard.md
new file mode 100644
index 0000000..42285de
--- /dev/null
+++ b/.agents/skills/starterkit-grid-standards/references/row-interaction-standard.md
@@ -0,0 +1,31 @@
+# Row Interaction Standard
+
+## Ziel
+
+Row-Navigation discoverable und keyboard-freundlich machen.
+
+## Standardverhalten
+
+1. Sichtbare Action-Spalte rechts (`Open`/`Edit`) fuer Listen mit `rowDblClick`.
+2. Enter auf fokussierter Row loest dieselbe Navigation aus.
+3. Doppelklick bleibt als Fallback fuer Power-User.
+4. Nur Rows mit Ziel-URL erhalten `data-row-open-url`.
+
+## Contract
+
+```js
+rowInteraction: {
+ enabled: true,
+ showActionButton: true,
+ keepDblClick: true,
+ enableEnterOnRow: true,
+ actionColumnLabel: 'Actions',
+ resolveActionLabel: (url, rowData) => (url.includes('/edit/') ? 'Edit' : 'Open')
+}
+```
+
+## Guardrails
+
+1. Keine globale Tab-Stop-Flut auf allen Rows.
+2. Fokusstil fuer Row und Action-Button sichtbar.
+3. Explizite `actions.enabled: true` pro Seite darf Auto-Action uebersteuern.
diff --git a/.agents/skills/starterkit-php-style-ci/SKILL.md b/.agents/skills/starterkit-php-style-ci/SKILL.md
new file mode 100644
index 0000000..d3d47b9
--- /dev/null
+++ b/.agents/skills/starterkit-php-style-ci/SKILL.md
@@ -0,0 +1,37 @@
+---
+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.
+---
+
+# Starterkit PHP Style CI
+
+## Ziel
+
+Coding-Style pruefbar, reproduzierbar und regressionsarm machen.
+
+## Wann verwenden
+
+1. Einfuehrung oder Nachschaerfung von Style-Regeln.
+2. CI-Dry-Run fuer Style-Checks.
+3. Aufraeumen grosser Style-Diff-Wellen.
+
+## Verbindliche Regeln
+
+1. MUST `composer cs:check` fuer nicht-mutierende Pruefung nutzen.
+2. MUST `composer cs:fix` nur bewusst und mit Diff-Kontrolle ausfuehren.
+3. MUST Style-Check vor Merge gruen halten (lokal und optional CI).
+4. MUST nach groesseren Fixes mindestens `phpunit` und `phpstan` laufen lassen.
+5. MUST NOT manuell Einzelstellen formatieren, wenn der Fixer sie zentral loesen kann.
+
+## Workflow
+
+1. Baseline erfassen: `composer cs:check`.
+2. Falls noetig fixen: `composer cs:fix`.
+3. Diff fokussiert pruefen (Imports, Braces, Nebenwirkungen).
+4. Qualitaetsgates laufen lassen.
+5. CI-Dry-Run konfigurieren oder validieren.
+
+## Referenzen
+
+- Lokaler Workflow: `references/style-workflow.md`
+- CI Dry Run: `references/ci-dry-run.md`
diff --git a/.agents/skills/starterkit-php-style-ci/agents/openai.yaml b/.agents/skills/starterkit-php-style-ci/agents/openai.yaml
new file mode 100644
index 0000000..c3e1eca
--- /dev/null
+++ b/.agents/skills/starterkit-php-style-ci/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "Starterkit PHP Style CI"
+ short_description: "Enforce PHP coding style with safe local and CI checks"
+ default_prompt: "Use $starterkit-php-style-ci to set up and run php-cs-fixer check/fix workflow with CI dry-run and guardrails."
diff --git a/.agents/skills/starterkit-php-style-ci/references/ci-dry-run.md b/.agents/skills/starterkit-php-style-ci/references/ci-dry-run.md
new file mode 100644
index 0000000..9c0a624
--- /dev/null
+++ b/.agents/skills/starterkit-php-style-ci/references/ci-dry-run.md
@@ -0,0 +1,21 @@
+# CI Dry Run
+
+## Ziel
+
+CI soll nur pruefen, nicht formatieren.
+
+## Minimaler Check
+
+```bash
+composer cs:check
+```
+
+## Erwartung
+
+1. Exit Code 0: Style konform.
+2. Exit Code != 0: Diff ausgeben, Entwickler fuehrt lokal `composer cs:fix` aus.
+
+## Hinweise
+
+1. Kein Auto-Commit aus CI.
+2. Bei initialen Altlasten zuerst einmalige Bereinigung auf dediziertem Branch.
diff --git a/.agents/skills/starterkit-php-style-ci/references/style-workflow.md b/.agents/skills/starterkit-php-style-ci/references/style-workflow.md
new file mode 100644
index 0000000..d7d707f
--- /dev/null
+++ b/.agents/skills/starterkit-php-style-ci/references/style-workflow.md
@@ -0,0 +1,25 @@
+# Local Style Workflow
+
+## Commands
+
+```bash
+composer cs:check
+composer cs:fix
+```
+
+## Praktikabler Ablauf
+
+1. Erst `cs:check` laufen lassen.
+2. Danach `cs:fix`.
+3. Nur relevante Diffs uebernehmen, grosse Misch-Diffs vermeiden.
+4. Anschliessend:
+
+```bash
+docker compose exec php vendor/bin/phpunit
+docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
+```
+
+## Review-Fokus
+
+1. `ordered_imports`, `braces_position`, `statement_indentation`.
+2. Kein semantischer Drift bei anon classes, arrow functions, array maps.
diff --git a/.agents/skills/starterkit-planner/SKILL.md b/.agents/skills/starterkit-planner/SKILL.md
new file mode 100644
index 0000000..0b890ef
--- /dev/null
+++ b/.agents/skills/starterkit-planner/SKILL.md
@@ -0,0 +1,58 @@
+---
+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.
+---
+
+# Starterkit Planner
+
+## Ziel
+
+Planungen so strukturieren, dass Implementierung ohne offene Entscheidungen moeglich ist.
+Bei Agent-Workflows muss der Plan maschinenlesbar in die Projekt-Contracts passen.
+
+## Verbindliches Ausgabeformat
+
+1. Ziel + Success Criteria
+2. In/Out-of-Scope
+3. Entscheidungsmatrix (Optionen + Gruende)
+4. Decision-complete Implementierungsplan
+5. Tests/Abnahme
+6. Risiken/Migrationshinweise
+7. Annahmen/Defaults
+
+Wenn `.agents/` verwendet wird:
+
+1. Plan muss dem Schema `.agents/contracts/planner.schema.json` entsprechen.
+2. Success Criteria muessen stabile IDs tragen (`SC-001`, `SC-002`, ...).
+3. Success Criteria muessen so formuliert sein, dass Reviewer Acceptance sie direkt gegen `criterion_id` pruefen kann.
+4. Guard- und Quality-Gate-IDs muessen aus den Katalogen kommen (`GR-*`, `QG-*`).
+
+## Vorgehen
+
+1. Ist-Zustand kurz aus Repo-Fakten ableiten.
+2. Entscheidungen mit Tradeoffs transparent machen.
+3. Oeffentliche Contracts explizit benennen.
+4. Success Criteria frueh mit stabilen IDs (`SC-*`) definieren.
+5. Reihenfolge und Rollout so planen, dass Rueckbau moeglich bleibt.
+6. Teststrategie nach Risiko priorisieren.
+
+## Qualitaetskriterien
+
+1. Keine vagen TODO-Entscheidungen im Plan.
+2. Jeder betroffene Bereich hat klare Akzeptanzkriterien mit stabiler ID (`SC-*`).
+3. Risiken sind priorisiert und beobachtbar.
+4. Ungeklaerte Themen stehen explizit unter Annahmen.
+5. Guard-/Gate-Referenzen sind vollstaendig und pruefbar (`GR-*`, `QG-*`).
+
+## Out of Scope
+
+- Extension-Mechanik ist aktuell nicht standardisiert.
+- Keine impliziten Extension-Regeln definieren.
+
+## Referenzen
+
+- Plan-Grundgeruest: `references/plan-template.md`
+- Entscheidungsraster: `references/tradeoff-matrix.md`
+- Agent Planner Contract: `.agents/contracts/planner.schema.json`
+- Guard Catalog: `.agents/checks/guard-catalog.json`
+- Quality Gates: `.agents/checks/quality-gates.json`
diff --git a/.agents/skills/starterkit-planner/references/plan-template.md b/.agents/skills/starterkit-planner/references/plan-template.md
new file mode 100644
index 0000000..8e530da
--- /dev/null
+++ b/.agents/skills/starterkit-planner/references/plan-template.md
@@ -0,0 +1,50 @@
+# Plan Template (Decision Complete)
+
+## 1) Ziel + Success Criteria
+
+- Was wird verbessert?
+- Woran ist messbar, dass es erfolgreich ist?
+- Success Criteria immer mit stabilen IDs formulieren (`SC-001`, `SC-002`, ...).
+
+Beispiel:
+
+- `SC-001`: Delete-Aktion zeigt Confirm-Dialog mit korrekter Gefahr-Variante.
+- `SC-002`: Nach Confirm erfolgt genau ein Submit (kein Doppel-Submit).
+
+## 2) Scope
+
+- In Scope
+- Out of Scope
+- Scope-Typ explizit markieren (`core`, `ui`, `api`, `mixed`), wenn der Workflow es verlangt.
+
+## 3) Oeffentliche Interfaces / Contracts
+
+- Neue/angepasste APIs, Data-Contracts, CLI-Flags, Markup-Contracts
+- Backward-Compatibility-Entscheidung
+- Bei Agent-Workflow: verwendete `GR-*` und `QG-*` IDs explizit nennen.
+
+## 4) Implementierungsschritte
+
+1. Schritt A
+2. Schritt B
+3. Schritt C
+
+Regel: Jeder Schritt ist direkt umsetzbar ohne Nachentscheid.
+
+## 5) Test- und Abnahmeplan
+
+- Syntax/Static Checks
+- Contract-/Architekturtests
+- Fachliche Smoke-Tests
+- Abnahmekriterien (klar messbar)
+- Abnahme-Checks 1:1 auf `SC-*` Kriterien-ID referenzieren.
+
+## 6) Risiken / Migration
+
+- Hauptrisiken
+- Mitigation
+- Rollout-/Rollback-Hinweis
+
+## 7) Annahmen / Defaults
+
+- Alle offenen Themen als explizite Annahme festhalten.
diff --git a/.agents/skills/starterkit-planner/references/tradeoff-matrix.md b/.agents/skills/starterkit-planner/references/tradeoff-matrix.md
new file mode 100644
index 0000000..081833e
--- /dev/null
+++ b/.agents/skills/starterkit-planner/references/tradeoff-matrix.md
@@ -0,0 +1,34 @@
+# Tradeoff Matrix
+
+## Bewertungsachsen
+
+1. Robustheit / Fehlertoleranz
+2. Wartbarkeit / Kopplung
+3. Migrationsrisiko
+4. Testbarkeit
+5. Liefergeschwindigkeit
+
+## Standard-Optionen
+
+### Option A: Zentralisieren im Core
+
+- Vorteile: Konsistenz, weniger Duplikate
+- Nachteile: Hoher Impact, breiter Testbedarf
+- Geeignet wenn: 3+ Stellen betroffen oder Contract-Sicherheit steigt deutlich
+
+### Option B: Shared Partial/Helper
+
+- Vorteile: Schneller Nutzen, moderates Risiko
+- Nachteile: Teilweise Restduplikate moeglich
+- Geeignet wenn: Wiederkehrendes UI-/Glue-Muster mit klaren Datencontracts
+
+### Option C: Lokal belassen
+
+- Vorteile: Geringstes kurzfristiges Risiko
+- Nachteile: Langfristig mehr Wartung, inkonsistente UX
+- Geeignet wenn: Einzelfall oder experimentelle Logik
+
+## Entscheidungsregel
+
+- Standardempfehlung: Option B vor Option A, wenn Nutzen hoch und Migrationsradius kleiner ist.
+- Option C nur mit expliziter Begruendung (z. B. bewusstes Out-of-Scope oder Legacy-Risiko).
diff --git a/.agents/templates/analysis.template.json b/.agents/templates/analysis.template.json
new file mode 100644
index 0000000..a8ca823
--- /dev/null
+++ b/.agents/templates/analysis.template.json
@@ -0,0 +1,32 @@
+{
+ "task_id": "TASK-0001",
+ "issue_summary": "Short summary of the issue or feature request",
+ "affected_layers": ["Service", "pages"],
+ "affected_files": [
+ {
+ "path": "path/to/file.php",
+ "role": "Service that handles X"
+ }
+ ],
+ "existing_patterns": [
+ "Uses app-details-titlebar partial",
+ "Follows PRG pattern in similar actions"
+ ],
+ "related_tests": [
+ "tests/Service/XServiceTest.php"
+ ],
+ "security_surface": {
+ "authz_relevant": false,
+ "tenant_scope_relevant": false,
+ "input_handling": true,
+ "crypto_relevant": false,
+ "file_upload_relevant": false,
+ "notes": ""
+ },
+ "assumptions": [
+ "Assumption the Planner must resolve"
+ ],
+ "risks_discovered": [
+ "Risk discovered during analysis"
+ ]
+}
diff --git a/agent-system/templates/execution-report.template.json b/.agents/templates/execution-report.template.json
similarity index 80%
rename from agent-system/templates/execution-report.template.json
rename to .agents/templates/execution-report.template.json
index b9b84a2..b570898 100644
--- a/agent-system/templates/execution-report.template.json
+++ b/.agents/templates/execution-report.template.json
@@ -1,6 +1,6 @@
{
"task_id": "TASK-0001",
- "plan_ref": "workflows/TASK-0001/plan.json",
+ "plan_ref": ".agents/runs/TASK-0001/plan.json",
"status": "done",
"changed_files": [
{
@@ -10,14 +10,14 @@
],
"guard_evidence": [
{
- "guard_id": "GR-CORE-001",
+ "guard_id": "GR-CORE-003",
"status": "pass",
"evidence": "Short evidence with file or test reference."
}
],
"commands": [
{
- "cmd": "example command",
+ "cmd": "docker compose exec php vendor/bin/phpunit",
"result": "pass"
}
],
diff --git a/agent-system/templates/finalize.template.json b/.agents/templates/finalize.template.json
similarity index 58%
rename from agent-system/templates/finalize.template.json
rename to .agents/templates/finalize.template.json
index c58e2e6..7aa880c 100644
--- a/agent-system/templates/finalize.template.json
+++ b/.agents/templates/finalize.template.json
@@ -1,11 +1,12 @@
{
"task_id": "TASK-0001",
"ready_to_finalize": false,
- "guard_review": "pass",
+ "code_review": "pass",
+ "security_review": "pass",
"acceptance_review": "pass",
"ci_status": "unknown",
"final_action": "hold",
- "hold_reason": "Waiting for mandatory quality gates and review pass signals.",
+ "hold_reason": "Waiting for all three reviews and quality gates to pass.",
"commit_message": "",
"notes": ""
}
diff --git a/agent-system/templates/plan.template.json b/.agents/templates/plan.template.json
similarity index 87%
rename from agent-system/templates/plan.template.json
rename to .agents/templates/plan.template.json
index b9b42ac..c8c7cb7 100644
--- a/agent-system/templates/plan.template.json
+++ b/.agents/templates/plan.template.json
@@ -1,5 +1,6 @@
{
"task_id": "TASK-0001",
+ "analysis_ref": ".agents/runs/TASK-0001/analysis.json",
"summary": "Short summary of issue or feature",
"assumptions": [],
"scope": {
@@ -8,9 +9,9 @@
},
"guardrails": {
"required_guard_ids": [
- "GR-CORE-001",
"GR-CORE-003",
- "GR-CORE-005"
+ "GR-TEST-001",
+ "GR-SEC-008"
],
"required_quality_gate_ids": [
"QG-001",
@@ -30,7 +31,7 @@
"title": "Step title",
"description": "Step detail",
"guard_refs": [
- "GR-CORE-001"
+ "GR-CORE-003"
]
}
],
diff --git a/agent-system/templates/review-acceptance.template.json b/.agents/templates/review-acceptance.template.json
similarity index 100%
rename from agent-system/templates/review-acceptance.template.json
rename to .agents/templates/review-acceptance.template.json
diff --git a/agent-system/templates/review-guards.template.json b/.agents/templates/review-code.template.json
similarity index 84%
rename from agent-system/templates/review-guards.template.json
rename to .agents/templates/review-code.template.json
index f20e29b..7f836bc 100644
--- a/agent-system/templates/review-guards.template.json
+++ b/.agents/templates/review-code.template.json
@@ -2,9 +2,8 @@
"task_id": "TASK-0001",
"verdict": "pass",
"checked_guard_ids": [
- "GR-CORE-001",
"GR-CORE-003",
- "GR-CORE-005"
+ "GR-TEST-001"
],
"checked_quality_gate_ids": [
"QG-001",
diff --git a/.agents/templates/review-security.template.json b/.agents/templates/review-security.template.json
new file mode 100644
index 0000000..346f9a6
--- /dev/null
+++ b/.agents/templates/review-security.template.json
@@ -0,0 +1,9 @@
+{
+ "task_id": "TASK-0001",
+ "verdict": "pass",
+ "checked_guard_ids": [
+ "GR-SEC-008",
+ "GR-SEC-009"
+ ],
+ "findings": []
+}
diff --git a/.agents/workflow.md b/.agents/workflow.md
new file mode 100644
index 0000000..25dcff4
--- /dev/null
+++ b/.agents/workflow.md
@@ -0,0 +1,125 @@
+# Agent Workflow
+
+Last updated: 2026-03-19
+
+## Overview
+
+This project uses a 7-role workflow for issue and feature delivery.
+
+```
+Analyst → Planner → Executor → Code Reviewer ──┐
+ ├→ Finalizer
+ Security Reviewer ──────┤
+ Acceptance Tester ───────┘
+```
+
+Primary goals:
+- Front-load context gathering (Analyst) so downstream roles start informed
+- Separate code quality review from security review for focused attention
+- Explicit guard IDs (`GR-*`), gate IDs (`QG-*`), and success criteria (`SC-*`) across all roles
+- Fast retry loop from reviewers back to executor
+
+Source of truth:
+- Guard catalog (22 guards): `.agents/checks/guard-catalog.json`
+- Quality gates (9 gates): `.agents/checks/quality-gates.json`
+- Contracts: `.agents/contracts/`
+
+---
+
+## Roles
+
+### Analyst
+- **Input:** issue description or feature request
+- **Output:** `analysis.json` (see `.agents/contracts/analyst.schema.json`)
+- Gathers context: affected layers, files, patterns, tests, security surface
+- Does NOT propose solutions — only structures facts for the Planner
+
+### Planner
+- **Input:** `analysis.json`
+- **Output:** `plan.json` (see `.agents/contracts/planner.schema.json`)
+- Defines scope, guard selection, success criteria, implementation steps, risks
+- References concrete file paths from analysis — does not re-explore codebase
+
+### Executor
+- **Input:** approved `plan.json`
+- **Output:** `execution-report.json` (see `.agents/contracts/executor.schema.json`)
+- Implements plan, runs quality gates, provides guard evidence
+
+### Code Reviewer
+- **Input:** code diff + `execution-report.json`
+- **Output:** `review-code.json` (see `.agents/contracts/reviewer-code.schema.json`)
+- Scope: architecture, conventions, testing, UI standards (13 guards)
+- Guards: `GR-CORE-*`, `GR-TEST-*`, `GR-UI-*`
+
+### Security Reviewer
+- **Input:** code diff + `execution-report.json`
+- **Output:** `review-security.json` (see `.agents/contracts/reviewer-security.schema.json`)
+- Scope: authz, tenant scope, CSRF, input validation, crypto, file storage (9 guards)
+- Guards: `GR-SEC-*`
+- Any `critical` or `high` finding forces `fail` verdict
+
+### Acceptance Tester
+- **Input:** code diff + `plan.json`
+- **Output:** `review-acceptance.json` (see `.agents/contracts/reviewer-acceptance.schema.json`)
+- Verifies each `SC-*` success criterion is met
+
+### Finalizer
+- **Input:** `review-code.json` + `review-security.json` + `review-acceptance.json`
+- **Output:** `finalize.json` (see `.agents/contracts/finalizer.schema.json`)
+- All three reviews must pass. CI must be green. Otherwise: hold.
+
+---
+
+## State Machine
+
+```
+analyzed → planned → executing → reviewing → finalize → done
+ ↑ |
+ └────────────┘ (on any FAIL)
+```
+
+The reviewing state runs Code Reviewer, Security Reviewer, and Acceptance Tester.
+If any reviewer returns `FAIL`, state goes back to `executing` with explicit findings.
+
+## Handover Artifacts
+
+| Role | Artifact |
+|---|---|
+| Analyst | `analysis.json` |
+| Planner | `plan.json` |
+| Executor | `execution-report.json` |
+| Code Reviewer | `review-code.json` |
+| Security Reviewer | `review-security.json` |
+| Acceptance Tester | `review-acceptance.json` |
+| Finalizer | `finalize.json` |
+
+## Fail Loop Rule
+
+No free-text "please improve". Every fail must include:
+- `id`
+- `severity`
+- `file`
+- expected fix
+
+## Guard Assignment
+
+Guards are split by reviewer role (see `reviewer` field in guard-catalog.json):
+
+**Code Reviewer** (13 guards): GR-CORE-003, GR-CORE-007, GR-CORE-010, GR-CORE-011, GR-CORE-012, GR-TEST-001, GR-TEST-002, GR-UI-014, GR-UI-LIST, GR-UI-DETAIL, GR-UI-A11Y, GR-UI-I18N, GR-UI-REUSE
+
+**Security Reviewer** (9 guards): GR-SEC-001 through GR-SEC-009
+
+## What automated tests already enforce
+
+The following concerns are covered by QG-001/QG-003/QG-004 and do NOT need manual guard review:
+- Layering boundaries (CoreStarterkitContractTest)
+- No direct service instantiation in pages (structural rg checks)
+- No superglobals in pages (architecture tests)
+- PSR-4 namespace alignment (autoloader + QG-003)
+- PHP code style (QG-006)
+- Module namespace isolation (ModuleStructureContractTest)
+- Module UI via slots only (NoBookmarksHardcodingTest, NoAddressBookHardcodingTest)
+- Runtime fingerprint (web/index.php fail-fast)
+- Repository sanitizeLimitOffset (RepositoryInterfaceContractTest)
+- List init standard, legacy filter toggle, template RBAC (architecture tests)
+- Component lifecycle contract (architecture tests)
diff --git a/AGENTS.md b/AGENTS.md
deleted file mode 100644
index 6267049..0000000
--- a/AGENTS.md
+++ /dev/null
@@ -1,127 +0,0 @@
-# AGENTS.md
-
-Multi-tenant admin application built on MintyPHP. Manages tenants, users, departments, roles, permissions, address books, mail, CSV imports, scheduled jobs, and Microsoft Entra ID SSO.
-
-## Tech Stack
-
-- **Runtime:** PHP 8.5 (FPM) on MintyPHP framework (`mintyphp/core`)
-- **Web server:** Nginx 1.25 (Alpine)
-- **Database:** MariaDB 11.4
-- **Cache:** Memcached 1.6
-- **Frontend:** Vanilla JS (ES2022 modules), vanilla CSS with `@layer` system — no build step
-- **Key PHP libs:** PHPMailer 7, Dompdf 3, endroid/qr-code 6, league/commonmark 2
-- **Containerized:** Docker Compose (dev + prod variants)
-
-## Quick Commands
-
-```bash
-# Start dev environment
-docker compose up --build -d
-# App: http://localhost:8080 | phpMyAdmin: http://localhost:8081
-
-# Run PHPUnit tests (phpunit.xml configures bootstrap + testsuite)
-docker compose exec php vendor/bin/phpunit
-
-# Run PHPStan (level 5)
-docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress
-
-# Check unused Composer packages
-docker compose exec php vendor/bin/composer-unused
-```
-
-## Project Structure
-
-```
-lib/ # All backend PHP (namespace MintyPHP\)
- App/ # DI container + bootstrap (AppContainer, Container/Registrars/)
- Repository/ # SQL queries, prepared statements, filters/paging
- Service/ # Business logic, validation, orchestration
- Http/ # Auth guards, API auth, request helpers
- Input/ # Request input handling (RequestInput, FormErrors)
- Support/ # Crypto, flash, guard, search, helpers
-pages/ # Actions (controllers) — file-based routing
- pages/**/*.php # Action files (read input, check permissions, call services)
- pages/**/*.phtml # View files (rendering only, no DB calls)
-templates/ # Layouts and partials
- partials/ # Reusable UI components
- emails/ # Email HTML templates
- pdfs/ # PDF templates (Dompdf)
-config/ # App config (routes, assets, settings cache, themes)
-web/ # Document root (entry point, CSS, JS, static assets)
- js/core/ # DOM utilities, Grid.js factory
- js/components/ # Reusable UI components
- css/ # Layered CSS (base → components → layout → pages)
-storage/ # Uploaded files (branding/, imports/, tenants/, users/)
-db/init/init.sql # Full schema + seed data (no migration framework)
-db/updates/ # Idempotent SQL update scripts for existing installs
-tests/ # PHPUnit tests (namespace MintyPHP\Tests\)
-docs/ # German-language documentation (Markdown)
-i18n/ # Translation files (de, en)
-bin/ # CLI scripts (scheduler, doctor.php health check)
-tools/ # Dev tooling scripts
-docker/ # Dockerfiles, Nginx configs, PHP configs
-```
-
-## Architecture Rules
-
-### Strict Layering (enforce in all changes)
-
-1. **App** (`lib/App/`): DI container bootstrap. Registers all service/repository factories. No business logic.
-2. **Repository** (`lib/Repository/`): All SQL lives here. No HTTP, session, or rendering.
-3. **Service** (`lib/Service/`): Business rules and validation. No HTML. Four internal sub-types:
- - **Service** (`*Service.php`): Business logic + orchestration. Coordinates repositories and gateways.
- - **Gateway** (`*Gateway.php`): Adapter for a single external dependency (repository, settings, HTTP API, scope). No orchestration flow.
- - **Factory** (`*Factory.php`): Only place inside `lib/Service/` that may call `new` on services/gateways/repositories. Registers via DI container.
- - **Policy** (`*Policy.php` in `lib/Service/Access/`): Authorization decisions per resource type. Returns `AuthorizationDecision`. No HTTP, no business logic.
-4. **Action** (`pages/**/*.php`): Reads input, checks permissions + tenant scope, calls services, sets view vars.
-5. **View** (`pages/**/*.phtml`): Pure rendering. **No DB calls.** HTML escaping via `e(...)`.
-6. **Template** (`templates/`): Layouts and partials.
-
-### Routing
-
-- File-based: `pages/admin/users/edit($id).php` → URL parameter `id`
-- `...(none).phtml` → no template layout
-- `data().php` suffix → data/AJAX endpoints
-- Named aliases in `config/routes.php` with `public` flag for auth guard
-
-### PHP Conventions
-
-- PSR-4 autoload: `MintyPHP\` → `lib/`, `MintyPHP\Tests\` → `tests/`
-- All user-facing text uses `t('key')` translation helper
-- Permissions + tenant scope enforced in actions/services, never just in UI
-- Security/integration settings stay in DB, not in `config/settings.php` file cache
-
-### Frontend Conventions
-
-- Vanilla ES2022 modules, no bundler
-- CSS classes prefixed with `app-`
-- Defensive DOM checks (elements may not exist on every page)
-- No business logic in frontend
-- Assets served raw with `assetVersion()` cache-busting (filemtime-based)
-
-### UI Patterns
-
-- Edit pages: tabs → `Master data` | `Visibility` | optional `Danger zone`
-- Titlebar partial for primary actions (Save, Save & close)
-- Secondary actions in Aside block via `app-details-aside-actions.phtml`
-- Lists use Grid.js
-- All visible text wrapped in `t('...')`
-
-## Database
-
-- Schema defined in `db/init/init.sql` (applied once on container init)
-- No migration framework — schema changes for existing installs are idempotent SQL scripts
-- Settings stored in `settings` DB table (single source of truth); `config/settings.php` is a partial file cache for hot-path UI reads only
-
-## Quality Gates
-
-- **PHPStan level 5** — scans `config/`, `lib/`, `pages/`, `tests/`
-- **PHPUnit 11** — bootstrap: `tests/bootstrap.php`
-- **Frontend Smoke-Check** — browser console bleibt fehlerfrei in Kernflows
-- **composer-unused** — periodic check for unused dependencies
-
-## Environment
-
-- Config via `.env` (gitignored) — copy from `.env.example` for dev, `.env.prod.example` for prod
-- `config/config.php` (gitignored) — copy from `config/config.php.example`
-- `APP_CRYPTO_KEY` (64-char hex) needed for AES-256-GCM encryption of SSO secrets
diff --git a/AGENTS.md b/AGENTS.md
new file mode 120000
index 0000000..681311e
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1 @@
+CLAUDE.md
\ No newline at end of file
diff --git a/agent-system/README.md b/agent-system/README.md
deleted file mode 100644
index 343aca4..0000000
--- a/agent-system/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Agent System
-
-This folder contains the technical assets for the multi-role workflow.
-
-Structure:
-- `contracts/`: JSON schemas for role outputs
-- `templates/`: starter JSON payloads
-- `prompts/`: role-specific prompt baselines
-- `checks/`: guard catalog, quality gates, and review checklists
-- `runs/`: optional runtime artifacts (can stay untracked)
-
-References:
-- Planner standard: `tools/codex-skills/starterkit-planner/SKILL.md`
-- Guardrails: `tools/codex-skills/core-guardrails/SKILL.md`
-- Docs/Skills audit policy: `agent-system/checks/docs-skills-audit-policy.md`
diff --git a/agent-system/checks/docs-skills-audit-policy.md b/agent-system/checks/docs-skills-audit-policy.md
deleted file mode 100644
index 7e1de35..0000000
--- a/agent-system/checks/docs-skills-audit-policy.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Docs/Skills Audit Policy
-
-Letzte Aktualisierung: 2026-03-09
-
-## Ziel
-
-Einheitliche Einstufung und Ablage von Findings fuer Docs-/Skills-Audits.
-
-## Severity-Klassen
-
-- `P0`: Kritischer Produktions- oder Sicherheitsimpact.
-- `P1`: Hoher Architektur-/Compliance-Impact, kurzfristig zu beheben.
-- `P2`: Mittlerer Impact, kein unmittelbarer Betriebsblocker.
-- `P3`: Niedriger Impact, redaktionell oder kosmetisch.
-
-## Persistenzregeln
-
-- `P0` und `P1` muessen als eigener Run unter `agent-system/runs/` dokumentiert werden.
-- Naming fuer diese Runs: `DOCS-SKILLS-AUDIT-00x` (fortlaufend).
-- `P2` und `P3` werden kompakt im laufenden Audit-Protokoll gesammelt und spaeter gebuendelt umgesetzt.
-- Repo-weite Alt-Slug-/Link-Scans schliessen `agent-system/runs/**` standardmaessig aus.
-
-## Mindestinhalt pro grossem Finding-Run (`P0`/`P1`)
-
-- Finding-ID, Severity, betroffene Dateien.
-- Reproduzierbare Evidenz (Befehl/Output oder Dateiverweis).
-- Geplante Mitigation inkl. Guard-/Gate-Bezug.
-- Abschlussstatus (`open`, `resolved`, `accepted-risk`).
diff --git a/agent-system/checks/guard-catalog.json b/agent-system/checks/guard-catalog.json
deleted file mode 100644
index cd8f0dc..0000000
--- a/agent-system/checks/guard-catalog.json
+++ /dev/null
@@ -1,307 +0,0 @@
-{
- "version": "1.0.0",
- "source_skill": "tools/codex-skills/core-guardrails/SKILL.md",
- "guards": [
- {
- "id": "GR-CORE-001",
- "area": "core",
- "title": "Layering boundaries",
- "requirement": "MUST keep pages orchestration-only and business logic in services.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-002",
- "area": "core",
- "title": "No direct service instantiation in pages",
- "requirement": "MUST NOT instantiate Service, Gateway or Repository directly in pages.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-003",
- "area": "core",
- "title": "Request input contract",
- "requirement": "MUST read input via requestInput in pages and avoid superglobals for request data.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-004",
- "area": "core",
- "title": "API json body contract",
- "requirement": "MUST NOT use ApiResponse::readJsonBody in pages/api/v1.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-005",
- "area": "security",
- "title": "Server-side authz",
- "requirement": "MUST enforce authorization server-side.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-006",
- "area": "security",
- "title": "Server-side tenant scope",
- "requirement": "MUST enforce tenant scope server-side.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-007",
- "area": "api",
- "title": "OpenAPI sync",
- "requirement": "MUST update OpenAPI in same merge when API changes.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-UI-001",
- "area": "ui",
- "title": "List initialization standard",
- "requirement": "MUST initialize lists via initStandardListPage.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-002",
- "area": "ui",
- "title": "No legacy filter toggle markup",
- "requirement": "MUST NOT introduce legacy filter toggle attributes.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-003",
- "area": "ui",
- "title": "Row interaction standard",
- "requirement": "MUST preserve row action column plus enter and dblclick fallback behavior.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-004",
- "area": "ui",
- "title": "Page size standard",
- "requirement": "MUST preserve page size options and URL/localStorage/default precedence.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-005",
- "area": "ui",
- "title": "Detail page standard",
- "requirement": "MUST use standard detail page contracts for dirty-state and actions.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-006",
- "area": "ui",
- "title": "No inline confirm",
- "requirement": "MUST NOT use inline confirm handlers on standardized detail flows.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-007",
- "area": "ui",
- "title": "Shared partial usage",
- "requirement": "MUST use shared list and detail partials for standardized UI blocks.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-016",
- "area": "ui",
- "title": "Template structure discipline for new features",
- "requirement": "New pages MUST use the established layout system (templates/default.phtml or a named layout variant). UI blocks that appear in two or more pages MUST be extracted as a partial under templates/partials/ rather than duplicated inline. New partials MUST follow the naming convention app-{context}-{purpose}.phtml. Page-specific rendering belongs in pages/*.phtml, not in templates/. MUST NOT create ad-hoc layout HTML inside pages that bypasses the layout system.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-008",
- "area": "ui",
- "title": "Template RBAC contract",
- "requirement": "MUST use viewAuth in templates and avoid can calls.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-UI-009",
- "area": "ui",
- "title": "No a11y regression",
- "requirement": "MUST NOT introduce regressions in accessibility basics (labels, roles, keyboard focus) on touched flows.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
- },
- {
- "id": "GR-CORE-008",
- "area": "core",
- "title": "No direct superglobal access in pages",
- "requirement": "MUST NOT use $_SESSION, $_SERVER, $_COOKIE, $_ENV directly in pages; use framework abstractions (app(SessionStoreInterface::class), RequestContext, requestInput()) instead.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-SEC-001",
- "area": "security",
- "title": "CSRF verification on POST endpoints",
- "requirement": "MUST verify CSRF token on every POST endpoint in pages before processing the request body.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-SEC-002",
- "area": "security",
- "title": "No unredacted PII or secrets in logs and audit metadata",
- "requirement": "MUST NOT write PII (email, names, UUIDs) or secrets (tokens, passwords) to logs or audit metadata without prior redaction.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-SEC-003",
- "area": "security",
- "title": "SQL via Repository with prepared statements only",
- "requirement": "MUST NOT build SQL strings with user-controlled input; all queries MUST go through Repository classes using prepared statements.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-LANG-001",
- "area": "lang",
- "title": "PSR-4 namespace alignment",
- "requirement": "MUST align PHP namespace with directory path (MintyPHP\\ → lib/, MintyPHP\\Tests\\ → tests/); violations are caught by QG-003.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-LANG-002",
- "area": "lang",
- "title": "PHP style compliance",
- "requirement": "MUST pass composer cs:check without diff (php-cs-fixer dry-run); fix with composer cs:fix before merge.",
- "source": "tools/codex-skills/starterkit-php-style-ci/SKILL.md"
- },
- {
- "id": "GR-TEST-001",
- "area": "test",
- "title": "Tests ship with new logic",
- "requirement": "New or changed business logic in lib/Service/ MUST be accompanied by PHPUnit tests in the same merge covering the happy path and at least one failure or edge case. No new public method in a Service or Gateway without a test.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-TEST-002",
- "area": "test",
- "title": "Code must be unit-testable",
- "requirement": "New code MUST use constructor injection for all dependencies. MUST NOT introduce static side-effects in constructors, hidden global state, or direct DB/HTTP calls outside Repository/Gateway abstractions — patterns that make unit testing without mocks impossible.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-010",
- "area": "core",
- "title": "DB schema changes for existing installs must be idempotent SQL scripts in db/updates/",
- "requirement": "Any schema or data change for existing installations MUST be shipped as an idempotent SQL script in db/updates/ (e.g. using IF NOT EXISTS, ON DUPLICATE KEY, or guarded ALTER TABLE). MUST NOT modify db/init/init.sql only — existing containers will not re-run init.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-011",
- "area": "core",
- "title": "New settings belong in the DB settings table, not in config files",
- "requirement": "New application settings MUST be stored in the settings DB table as the single source of truth. config/settings.php is a partial hot-path cache only and MUST NOT be used as primary storage for new settings. Security, integration, and feature-flag settings MUST go through the settings gateway layer.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-012",
- "area": "core",
- "title": "POST-Redirect-GET after successful mutating actions",
- "requirement": "Actions that successfully mutate state (create, update, delete) MUST respond with a redirect (PRG pattern) rather than rendering a response directly. This prevents double-submit on browser reload. Flash messages MUST be set before the redirect via Flash::set().",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-CORE-009",
- "area": "core",
- "title": "Repository list queries must use RepoQuery::sanitizeLimitOffset",
- "requirement": "New Repository methods that return lists MUST use RepoQuery::sanitizeLimitOffset() to enforce LIMIT and OFFSET. Unbounded SELECT queries without LIMIT on potentially large tables are forbidden. The default maxLimit of 100 MUST NOT be raised without explicit justification reviewed and approved.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-SEC-006",
- "area": "security",
- "title": "User-uploaded files stored in storage/, never in web/",
- "requirement": "Files uploaded by users MUST be stored under storage/ (e.g. storage/users/{uuid}/, storage/tenants/{uuid}/, storage/imports/) and MUST NOT be written to web/ or any other publicly served directory. Storage paths MUST use non-guessable identifiers (UUID v4 or equivalent) to prevent enumeration. Files MUST be served through a PHP controller that enforces authorization, not directly by the web server. MUST validate file type (MIME + extension allowlist) before storing.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-SEC-005",
- "area": "security",
- "title": "Encryption only via lib/Support/Crypto.php",
- "requirement": "Any encryption or decryption of sensitive data MUST use the Crypto class (lib/Support/Crypto.php) which provides AES-256-GCM via APP_CRYPTO_KEY. MUST NOT use raw openssl_encrypt/openssl_decrypt, base64 encoding as a substitute for encryption, or any other hand-rolled crypto primitives.",
- "source": "tools/codex-skills/core-guardrails/references/boundaries-core.md"
- },
- {
- "id": "GR-SEC-004",
- "area": "security",
- "title": "No external CDN or remote asset loading",
- "requirement": "MUST NOT load JS, CSS, fonts, or any other assets from external CDNs (Cloudflare, jsDelivr, unpkg, Google Fonts, etc.) or remote URLs in templates, pages, or layout files. If a third-party library is required it MUST be vendored locally in the repository (e.g. web/js/vendor/ or web/css/vendor/) and served via assetVersion(). No