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