refactor: relocate agent-system/ to .agents/ with 7-role workflow restructure

Moves the agent workflow system from agent-system/ to .agents/ (dotfile convention).
Restructured from 5-role to 7-role pipeline: adds Analyst and splits Reviewer into
Code Reviewer + Security Reviewer. Removes all old workflow run artifacts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:23:04 +01:00
parent ef72b34c40
commit 4dd6d451f6
136 changed files with 1357 additions and 7890 deletions

View File

@@ -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

View File

@@ -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."
}
]
}

View File

@@ -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

View File

@@ -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",

View File

@@ -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
}

View File

@@ -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 }

View File

@@ -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" } }
}
}
],

View File

@@ -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" }
}
},

View File

@@ -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": {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -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`

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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`

View File

@@ -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`

View File

@@ -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 `<script type="module">...</script>` 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.

View File

@@ -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.

View File

@@ -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`

View File

@@ -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`

View File

@@ -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."

View File

@@ -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.

View File

@@ -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).

View File

@@ -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.

View File

@@ -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`

View File

@@ -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."

View File

@@ -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.

View File

@@ -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.

View File

@@ -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`

View File

@@ -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.

View File

@@ -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).

View File

@@ -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"
]
}

View File

@@ -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"
}
],

View File

@@ -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": ""
}

View File

@@ -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"
]
}
],

View File

@@ -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",

View File

@@ -0,0 +1,9 @@
{
"task_id": "TASK-0001",
"verdict": "pass",
"checked_guard_ids": [
"GR-SEC-008",
"GR-SEC-009"
],
"findings": []
}

125
.agents/workflow.md Normal file
View File

@@ -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)

127
AGENTS.md
View File

@@ -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

1
AGENTS.md Symbolic link
View File

@@ -0,0 +1 @@
CLAUDE.md

View File

@@ -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`

View File

@@ -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`).

View File

@@ -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 <script src='https://...'>, <link href='https://...'>, @import url('https://...'), or fetch() calls to external asset hosts.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-UI-014",
"area": "ui",
"title": "UI state completeness for new views and components",
"requirement": "New user-facing views or components that display data or trigger actions MUST handle all four states: loading (progress indicator while data is fetched), empty (explicit message when no data exists), error (user-visible error feedback when an action or data load fails), and success (confirmation after a completed action). All four states MUST be defined in the plan before implementation starts. Applies to new views and components; not required for small, isolated fixes to existing UI.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-UI-013",
"area": "ui",
"title": "A11y by design: new interactive components built accessible from the start",
"requirement": "New interactive UI components (buttons, links, forms, modals, dropdowns, custom controls) MUST use semantic HTML elements. MUST be fully keyboard-operable (Tab order, Enter/Space to activate, Escape to close overlays, no keyboard trap). MUST use ARIA attributes only where semantic HTML is insufficient — no ARIA-spam. MUST meet WCAG 2.1 AA color contrast for any newly introduced text or icon elements. These requirements apply at creation time, not as a post-hoc retrofit.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-UI-015",
"area": "ui",
"title": "i18n key completeness: all language files updated in the same merge",
"requirement": "Every new translation key added via t('key') MUST be present in ALL language files (i18n/default_de.json and i18n/default_en.json) in the same merge. A key present in one file but missing in another causes visible fallback output. MUST NOT leave placeholder or empty string values without a TODO comment.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-UI-010",
"area": "ui",
"title": "i18n: all visible text must use t()",
"requirement": "MUST wrap all user-visible strings in phtml views with t('key'). MUST NOT hardcode display strings in templates or partials.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-UI-011",
"area": "ui",
"title": "JS reuse-first: prefer existing exports over new functions",
"requirement": "MUST check web/js/core/ and web/js/components/ for existing exported functions before adding a new one. A new export is only justified when no existing function covers the use-case after review. MUST NOT duplicate logic already present in app-dom.js, app-grid-factory.js, app-detail-page-factory.js, or other core modules.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-UI-012",
"area": "ui",
"title": "CSS reuse-first: prefer existing classes over new rules",
"requirement": "MUST check existing CSS layers (base, components, layout, pages) for suitable classes before writing new rules. MUST use existing app-* component classes as the basis and only add additive overrides. MUST NOT duplicate declarations already defined in shared component or layout files.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
},
{
"id": "GR-MOD-001",
"area": "core",
"title": "Module classes must use MintyPHP\\Module\\ namespace",
"requirement": "All PHP classes under modules/<id>/lib/ MUST use the MintyPHP\\Module\\<Name>\\ namespace. MUST NOT use core namespaces (MintyPHP\\Service\\, MintyPHP\\Repository\\, MintyPHP\\Support\\, MintyPHP\\Http\\). Enforced by ModuleStructureContractTest::testModuleClassesUseModuleNamespace.",
"source": "docs/reference-module-manifest-contract.md"
},
{
"id": "GR-MOD-002",
"area": "core",
"title": "Module runtime fingerprint must be current",
"requirement": "After any module/manifest/APP_ENABLED_MODULES change, MUST run php bin/module-runtime-sync.php before serving requests. web/index.php validates the runtime fingerprint and fails fast if stale. MUST NOT bypass or remove the fingerprint check.",
"source": "docs/reference-module-manifest-contract.md"
},
{
"id": "GR-MOD-003",
"area": "ui",
"title": "Module UI only via platform slots",
"requirement": "Module UI contributions MUST use platform slots (aside.tab_panel, topbar.right_item, layout.head_style, layout.body_end_template, runtime.component, search.resource_item). MUST NOT add module-specific markup to core templates. Enforced by NoBookmarksHardcodingTest and NoAddressBookHardcodingTest.",
"source": "docs/reference-module-manifest-contract.md"
},
{
"id": "GR-SEC-007",
"area": "security",
"title": "API requests must not start PHP sessions",
"requirement": "API requests (api/ prefix) MUST NOT trigger Session::start(), remember-me cookie handling, or session timeout redirects. API authentication is handled by ApiBootstrap.php with Bearer tokens. web/index.php enforces this via channel detection before session initialization.",
"source": "docs/reference-module-manifest-contract.md"
},
{
"id": "GR-UI-017",
"area": "ui",
"title": "Component lifecycle contract for migrated UI modules",
"requirement": "Components migrated to the lifecycle runtime MUST expose an init(root, config) entrypoint that returns an API with destroy(). Migrated component modules MUST NOT auto-initialize themselves via DOMContentLoaded/module side effects and MUST NOT retain legacy wrapper entrypoints or init alias exports. Event listeners, timers, and observers created during init MUST be cleaned up in destroy(). Runtime configuration MUST come from page-config payloads (readPageConfig) instead of scattered ad-hoc globals. Page-level initialization MUST be loaded via web/js/pages/* entry modules, not js/components/* script loads. Host-bound UI components (including tabs) MUST provide explicit data-app-component hosts in markup and remain root-strict (no document.querySelector fallback paths). Global utilities without a natural host MAY be runtime-registered with scope=global and MUST still satisfy the same lifecycle/destroy contract. Project-specific custom window.* globals are forbidden; cross-component communication MUST use core channel modules. Migrated UI storage MUST use the shared namespaced storage contract without legacy key fallback/sync paths.",
"source": "tools/codex-skills/core-guardrails/references/boundaries-ui.md"
}
]
}

View File

@@ -1,66 +0,0 @@
# Guard Checklist
Use this checklist for the guard reviewer.
Canonical guard IDs live in `agent-system/checks/guard-catalog.json`.
## Architecture
- `GR-CORE-001` no boundary violations against project layering
- `GR-CORE-002` no direct bypass of shared factories/services where prohibited
- `GR-CORE-003` request input contract is respected (requestInput(), no $_POST/$_GET/$_FILES/REQUEST_METHOD)
- `GR-CORE-004` API json body contract is respected
- `GR-CORE-008` no direct superglobal access in pages ($_SESSION, $_SERVER, $_COOKIE, $_ENV)
- `GR-CORE-009` new Repository list methods use RepoQuery::sanitizeLimitOffset(); no unbounded SELECTs on large tables; maxLimit 100 not raised without justification
- `GR-CORE-010` DB schema changes for existing installs shipped as idempotent SQL scripts in db/updates/; db/init/init.sql alone is not sufficient
- `GR-CORE-011` new settings stored in DB settings table via settings gateway; config/settings.php is hot-path cache only, not primary storage
- `GR-CORE-012` successful mutating POSTs respond with redirect (PRG); flash messages set before redirect via Flash::set()
## Security
- `GR-CORE-005` auth and permission checks stay intact
- `GR-CORE-006` tenant-scope checks stay intact
- `GR-SEC-001` CSRF token verified on every POST endpoint before request body processing
- `GR-SEC-002` no unredacted PII or secrets in logs and audit metadata
- `GR-SEC-003` all SQL goes through Repository with prepared statements; no string-interpolated queries
- `GR-SEC-004` no external CDN or remote asset loading; all JS/CSS/fonts must be vendored locally and served via assetVersion()
- `GR-SEC-005` encryption only via lib/Support/Crypto.php (AES-256-GCM); no raw openssl_encrypt, no base64-as-encryption, no hand-rolled crypto
- `GR-SEC-006` user-uploaded files stored under storage/{type}/{uuid}/ only; never written to web/; served via PHP controller with authz check; MIME + extension validated before storage
## Frontend and UX
- `GR-UI-001` list initialization standard is preserved
- `GR-UI-002` no legacy filter toggle markup introduced
- `GR-UI-003` row interaction standard is preserved where applicable
- `GR-UI-004` page size standard is preserved where applicable
- `GR-UI-005` detail page standard is preserved where applicable
- `GR-UI-006` no inline confirm hacks
- `GR-UI-007` shared partial usage remains consistent (existing partials reused where applicable)
- `GR-UI-016` template structure discipline: new pages use established layout system; blocks reused in ≥2 pages extracted as partial in templates/partials/ with naming app-{context}-{purpose}.phtml; no ad-hoc layout HTML in pages that bypasses the layout system
- `GR-UI-008` template RBAC contract remains consistent
- `GR-UI-009` no regression in a11y basics (labels, roles, keyboard focus) for touched flows
- `GR-UI-013` a11y by design: new interactive components use semantic HTML, are keyboard-operable (Tab/Enter/Escape), use ARIA only where necessary, meet WCAG 2.1 AA contrast
- `GR-UI-014` UI state completeness: new views/components define and implement loading, empty, error, and success states before implementation starts
- `GR-UI-010` all user-visible strings in phtml views wrapped in t(); no hardcoded display strings
- `GR-UI-015` new t() keys present in all language files (de + en) in the same merge; no key missing in any language file
- `GR-UI-011` JS reuse-first: existing exports in web/js/core/ and web/js/components/ checked before adding a new function; no logic duplication from core modules
- `GR-UI-012` CSS reuse-first: existing app-* classes and layer rules checked before writing new declarations; only additive overrides on top of shared components
- `GR-UI-017` lifecycle runtime contract for migrated components is enforced (init(root, config) + destroy(), no component-level auto-init side effects, no legacy wrappers/init alias exports, cleanup of listeners/timers/observers, runtime config via page-config, page-level JS only via `web/js/pages/*` modules, host-bound components incl. tabs with `data-app-component` and root-strict selectors, global utilities only via explicit runtime-global registration, no project-specific custom `window.*` globals, namespaced UI storage without legacy fallback/sync paths)
## Testing
- `GR-TEST-001` new or changed Service/Gateway logic has accompanying PHPUnit tests (happy path + at least one failure/edge case) in the same merge
- `GR-TEST-002` new code uses constructor injection; no static side-effects, hidden global state, or untestable DB/HTTP calls outside Repository/Gateway abstractions
## Language and Style
- `GR-LANG-001` PSR-4 namespace/directory alignment is intact (verified by QG-003)
- `GR-LANG-002` PHP style passes composer cs:check without diff for touched files
## Quality Gates
- `QG-001` to `QG-003` are run unless explicitly out of scope
- `QG-004` fast structural checks reported when relevant
- `QG-005` JS smoke performed for JS-touching changes; no new unhandled runtime errors
- `QG-006` PHP style check run for any PHP-touching changes
- `QG-007` composer-unused run periodically; any newly unused package flagged to reviewer
References:
- `tools/codex-skills/core-guardrails/references/boundaries-core.md`
- `tools/codex-skills/core-guardrails/references/boundaries-ui.md`
- `tools/codex-skills/core-guardrails/references/quality-gates.md`
- `tools/codex-skills/starterkit-grid-standards/SKILL.md`
- `tools/codex-skills/starterkit-php-style-ci/SKILL.md`

View File

@@ -1,88 +0,0 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "Guard Reviewer Output",
"type": "object",
"required": ["task_id", "verdict", "checked_guard_ids", "checked_quality_gate_ids", "findings"],
"properties": {
"task_id": { "type": "string", "minLength": 1 },
"audit_policy_ref": { "type": "string" },
"verdict": { "type": "string", "enum": ["pass", "fail"] },
"checked_guard_ids": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"pattern": "^GR-[A-Z]+-[0-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"] },
"priority": { "type": "string", "enum": ["P0", "P1", "P2", "P3"] },
"rule_ref": {
"type": "string",
"pattern": "^(GR-[A-Z]+-[0-9]{3}|QG-[0-9]{3})$"
},
"summary": { "type": "string", "minLength": 1 },
"file": { "type": "string", "minLength": 1 },
"fix_required": { "type": "string" },
"run_artifact_id": {
"type": "string",
"pattern": "^DOCS-SKILLS-AUDIT-[0-9]{3}$"
}
},
"additionalProperties": false
}
}
},
"allOf": [
{
"if": {
"properties": {
"task_id": { "pattern": "^DOCS-SKILLS-AUDIT-[0-9]{3}$" }
},
"required": ["task_id"]
},
"then": {
"required": ["audit_policy_ref"],
"properties": {
"audit_policy_ref": { "const": "agent-system/checks/docs-skills-audit-policy.md" },
"findings": {
"items": {
"allOf": [
{
"required": ["priority"]
},
{
"if": {
"properties": {
"priority": { "enum": ["P0", "P1"] }
},
"required": ["priority"]
},
"then": {
"required": ["run_artifact_id"]
}
}
]
}
}
}
}
}
],
"additionalProperties": false
}

View File

@@ -1,26 +0,0 @@
# Executor Prompt Baseline
Goal:
- implement the approved plan with minimal scope drift
Required references:
- `tools/codex-skills/core-guardrails/SKILL.md`
- `tools/codex-skills/starterkit-grid-standards/SKILL.md` (for UI/list tasks)
- `tools/codex-skills/starterkit-php-style-ci/SKILL.md` (for PHP style checks)
- `agent-system/checks/guard-catalog.json`
- `agent-system/checks/quality-gates.json`
Input:
- `plan.json`
Output:
- valid JSON by `agent-system/contracts/executor.schema.json`
Rules:
- only implement items in plan scope
- run relevant checks and include command results
- provide guard evidence for each required guard ID
- report quality gate results by gate ID
- list each changed file with a short reason
- if blocked, set status to `blocked` and list exact blockers
- for docs/skills audits, run and report `QG-008` and `QG-009`

View File

@@ -1,18 +0,0 @@
# Finalizer Prompt Baseline
Goal:
- finalize only when both reviews are pass and checks are green
Input:
- `execution-report.json`
- `review-guards.json`
- `review-acceptance.json`
Output:
- valid JSON by `agent-system/contracts/finalizer.schema.json`
Rules:
- do not finalize if any reviewer verdict is `fail`
- do not finalize unless `guard_review=pass`, `acceptance_review=pass`, and `ci_status=pass`
- set `final_action` to `hold` and populate `hold_reason` (non-empty string) whenever action is `hold`
- `hold_reason` is required by schema when `final_action` is `hold`

View File

@@ -1,29 +0,0 @@
# Planner Prompt Baseline
Goal:
- transform issue input into a decision-complete plan
Required references:
- `tools/codex-skills/starterkit-planner/SKILL.md`
- `tools/codex-skills/core-guardrails/SKILL.md`
- `tools/codex-skills/starterkit-grid-standards/SKILL.md` (for UI/list tasks)
- `agent-system/checks/guard-catalog.json`
- `agent-system/checks/quality-gates.json`
Output:
- valid JSON by `agent-system/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
- select required quality gate IDs from quality gates list
- include at least one risk with mitigation
- keep implementation steps actionable and ordered
- for docs/skills audits, use task IDs `DOCS-SKILLS-AUDIT-xxx` and include `QG-008` + `QG-009` in required quality gates
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 (e.g. "app-details-titlebar", "initStandardListPage")
- populate `ux_notes.ui_states_required`: declare all four states that the new view/component must handle — loading, empty, error, success (GR-UI-014); omit only with explicit justification
- populate `ux_notes.a11y_touchpoints`: list every new interactive element and confirm it will be keyboard-operable and use semantic HTML (GR-UI-013)
- if any item in `ux_notes` is not applicable, set it to an empty array `[]` — do not omit the field

View File

@@ -1,17 +0,0 @@
# Reviewer Acceptance Prompt Baseline
Goal:
- verify the feature or issue is implemented exactly as requested
Required references:
- `plan.json`
- `agent-system/checks/acceptance-checklist.md`
Output:
- valid JSON by `agent-system/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`

View File

@@ -1,25 +0,0 @@
# Reviewer Guards Prompt Baseline
Goal:
- verify compliance with guardrails, architecture, and security expectations
Required references:
- `tools/codex-skills/core-guardrails/SKILL.md`
- `tools/codex-skills/starterkit-grid-standards/SKILL.md` (for UI/list tasks)
- `tools/codex-skills/starterkit-php-style-ci/SKILL.md` (for PHP style checks)
- `agent-system/checks/guard-checklist.md`
- `agent-system/checks/guard-catalog.json`
- `agent-system/checks/quality-gates.json`
- `agent-system/checks/docs-skills-audit-policy.md` (for docs/skills audit tasks)
Output:
- valid JSON by `agent-system/contracts/reviewer-guards.schema.json`
Rules:
- findings must reference a concrete guard or gate ID
- findings must include file path
- use `fail` only when changes are required before finalize
- if `task_id` matches `DOCS-SKILLS-AUDIT-xxx`:
- set `audit_policy_ref` to `agent-system/checks/docs-skills-audit-policy.md`
- classify every finding with `priority` in `P0|P1|P2|P3`
- for `P0` and `P1`, set `run_artifact_id` with the same pattern (`DOCS-SKILLS-AUDIT-xxx`)

View File

@@ -1,346 +0,0 @@
{
"task_id": "ARCH-GATEWAY-COMPLIANCE-001",
"plan_ref": "agent-system/runs/ARCH-GATEWAY-COMPLIANCE-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "lib/Service/Access/PermissionGateway.php",
"summary": "DELETED — pure 9-method pass-through to PermissionService. All ~40 callers now use PermissionService directly."
},
{
"path": "lib/Service/Auth/AuthSavedFilterGateway.php",
"summary": "DELETED — 1-method pass-through to UserSavedFilterService. Caller inlined."
},
{
"path": "lib/Service/Auth/AuthTenantSsoGateway.php",
"summary": "DELETED — 4-method pass-through to TenantSsoService (same Auth domain). Callers inlined."
},
{
"path": "lib/Service/Auth/AuthAvatarGateway.php",
"summary": "DELETED — 2-method pass-through to UserAvatarService. Callers inlined."
},
{
"path": "lib/Service/AddressBook/AddressBookDirectoryGateway.php",
"summary": "DELETED — 4-dep facade violating single-dep contract. Orchestration moved to AddressBookService with 4 direct deps."
},
{
"path": "lib/Service/Settings/ThemeConfigService.php",
"summary": "DELETED — renamed to ThemeConfigGateway (config reader = gateway pattern)."
},
{
"path": "lib/Service/Settings/ThemeConfigGateway.php",
"summary": "CREATED — renamed from ThemeConfigService. Identical logic, correct naming."
},
{
"path": "lib/Service/Settings/SettingsAppGateway.php",
"summary": "S4: ThemeConfigService type hint changed to ThemeConfigGateway. Constructor now depends on SettingsMetadataGateway + ThemeConfigGateway — both Gateways, zero Service deps. Violation resolved."
},
{
"path": "lib/Service/Auth/AuthPermissionGateway.php",
"summary": "DELETED — 2-method pass-through to PermissionService. Callers now inject PermissionService directly."
},
{
"path": "lib/Service/User/UserPermissionGateway.php",
"summary": "DELETED — 1-method pass-through to PermissionService. Caller now injects PermissionService directly."
},
{
"path": "lib/Service/AddressBook/AddressBookAvatarGateway.php",
"summary": "DELETED — 1-method pass-through to UserAvatarService. Caller now injects UserAvatarService directly."
},
{
"path": "lib/Service/AddressBook/AddressBookCustomFieldGateway.php",
"summary": "DELETED (Re-Run RG-001) — 2-method gateway wrapping UserCustomFieldValueService + TenantCustomFieldOptionRepository. Calls inlined into AddressBookService."
},
{
"path": "lib/Service/Auth/AuthScopeGateway.php",
"summary": "DELETED (Re-Run RG-004) — pure pass-through to TenantScopeService. All callers now inject TenantScopeService directly."
},
{
"path": "lib/Service/Directory/DirectoryScopeGateway.php",
"summary": "DELETED (Re-Run RG-005) — pure pass-through to TenantScopeService. All callers now inject TenantScopeService directly."
},
{
"path": "lib/Service/User/UserScopeGateway.php",
"summary": "DELETED (Re-Run RG-006) — pure pass-through to TenantScopeService. All callers now inject TenantScopeService directly."
},
{
"path": "lib/Service/Import/Profile/DepartmentImportGateway.php",
"summary": "Re-Run RG-002: Removed DepartmentService dep. Now repo-only gateway (DepartmentRepositoryInterface, TenantRepositoryInterface)."
},
{
"path": "lib/Service/Import/Profile/DepartmentImportProfile.php",
"summary": "Re-Run RG-002: Added direct DepartmentService dep. Calls createFromAdmin() on service instead of gateway."
},
{
"path": "lib/Service/Import/Profile/UserImportGateway.php",
"summary": "Re-Run RG-003: Removed UserAccountService dep. Now repo-only gateway (4 repository interfaces)."
},
{
"path": "lib/Service/Import/Profile/UserImportProfile.php",
"summary": "Re-Run RG-003: Added direct UserAccountService dep. Calls createFromAdmin() on service instead of gateway."
},
{
"path": "lib/Service/AddressBook/AddressBookService.php",
"summary": "Re-Run RG-001+005: Replaced AddressBookCustomFieldGateway with UserCustomFieldValueService. Replaced DirectoryScopeGateway with TenantScopeService."
},
{
"path": "lib/Service/AddressBook/AddressBookServicesFactory.php",
"summary": "Re-Run: Added TenantScopeService constructor dep. Removed AddressBookCustomFieldGateway creation."
},
{
"path": "lib/Service/Auth/ApiTokenService.php",
"summary": "Re-Run RG-004: Changed dep AuthScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Auth/ApiTokenEndpointService.php",
"summary": "Re-Run RG-004: Changed dep AuthScopeGateway → TenantScopeService."
},
{
"path": "lib/Http/ApiAuth.php",
"summary": "Re-Run RG-004: Changed dep AuthScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Auth/AuthGatewayFactory.php",
"summary": "Re-Run RG-004: Removed createAuthScopeGateway(). Added getTenantScopeService() accessor."
},
{
"path": "lib/Service/Auth/AuthServicesFactory.php",
"summary": "Re-Run RG-004: Removed createAuthScopeGateway() delegate. ApiTokenService gets TenantScopeService via authGatewayFactory."
},
{
"path": "lib/Service/Org/DepartmentService.php",
"summary": "Re-Run RG-005: Changed dep DirectoryScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Access/UserAuthorizationPolicy.php",
"summary": "Re-Run RG-005: Changed dep DirectoryScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Access/TenantAuthorizationPolicy.php",
"summary": "Re-Run RG-005: Changed dep DirectoryScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Access/DepartmentAuthorizationPolicy.php",
"summary": "Re-Run RG-005: Changed dep DirectoryScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Access/AccessPolicyFactory.php",
"summary": "Re-Run RG-005: Changed dep DirectoryScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Directory/DirectoryGatewayFactory.php",
"summary": "Re-Run RG-005: Removed createDirectoryScopeGateway(). Added getTenantScopeService() accessor."
},
{
"path": "lib/Service/Directory/DirectoryServicesFactory.php",
"summary": "Re-Run RG-005: DepartmentService gets TenantScopeService from directoryGatewayFactory. createDirectoryScopeGateway() → getTenantScopeService()."
},
{
"path": "lib/Service/User/UserAccountService.php",
"summary": "Re-Run RG-006: Changed dep UserScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/User/UserTenantContextService.php",
"summary": "Re-Run RG-006: Changed dep UserScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/CustomField/UserCustomFieldValueService.php",
"summary": "Re-Run RG-006: Changed dep UserScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/CustomField/CustomFieldServicesFactory.php",
"summary": "Re-Run RG-006: Changed dep UserScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/User/UserGatewayFactory.php",
"summary": "Re-Run RG-006: Removed createUserScopeGateway(). Added getTenantScopeService() accessor."
},
{
"path": "lib/Service/User/UserServicesFactory.php",
"summary": "Re-Run RG-006: UserAccountService and UserTenantContextService get TenantScopeService from userGatewayFactory. Removed createUserScopeGateway() delegate."
},
{
"path": "lib/Service/Import/ImportService.php",
"summary": "Re-Run RG-006: Changed dep UserScopeGateway → TenantScopeService."
},
{
"path": "lib/Service/Import/ImportServicesFactory.php",
"summary": "Re-Run RG-002+003+006: Added TenantScopeService constructor dep. Updated UserImportProfile + DepartmentImportProfile construction. ImportService gets TenantScopeService."
},
{
"path": "lib/App/Container/Registrars/AuthRegistrar.php",
"summary": "Re-Run RG-004: Removed AuthScopeGateway registration. ApiTokenEndpointService gets TenantScopeService."
},
{
"path": "lib/App/Container/Registrars/DirectoryRegistrar.php",
"summary": "Re-Run RG-005: Removed DirectoryScopeGateway registration."
},
{
"path": "lib/App/Container/Registrars/UserRegistrar.php",
"summary": "Re-Run RG-006: Removed UserScopeGateway registration."
},
{
"path": "lib/App/Container/Registrars/ServiceFactoryRegistrar.php",
"summary": "Re-Run RG-005+006: Changed DirectoryScopeGateway → TenantScopeService for AccessPolicyFactory. Changed UserScopeGateway → TenantScopeService for CustomFieldServicesFactory. Added TenantScopeService to ImportServicesFactory and AddressBookServicesFactory wiring."
},
{
"path": "lib/Support/helpers/app.php",
"summary": "Re-Run RG-004: ApiAuth::configure() resolver changed AuthScopeGateway → TenantScopeService."
},
{
"path": "pages/admin/users/index().php",
"summary": "Re-Run RG-005: Changed DirectoryScopeGateway → TenantScopeService (3 occurrences)."
},
{
"path": "pages/admin/users/create().php",
"summary": "Re-Run RG-005: Changed DirectoryScopeGateway → TenantScopeService."
},
{
"path": "pages/admin/users/edit($id).php",
"summary": "Re-Run RG-005: Changed DirectoryScopeGateway → TenantScopeService."
},
{
"path": "pages/admin/users/access-pdf-bulk().php",
"summary": "Re-Run RG-005: Changed DirectoryScopeGateway → TenantScopeService."
},
{
"path": "pages/admin/departments/create().php",
"summary": "Re-Run RG-005: Changed DirectoryScopeGateway → TenantScopeService."
},
{
"path": "pages/admin/departments/edit($id).php",
"summary": "Re-Run RG-005: Changed DirectoryScopeGateway → TenantScopeService."
},
{
"path": "tests/Service/AddressBook/AddressBookServiceTest.php",
"summary": "Re-Run: Mocks updated — DirectoryScopeGateway → TenantScopeService, AddressBookCustomFieldGateway → UserCustomFieldValueService."
},
{
"path": "tests/Service/Access/UserAuthorizationPolicyTest.php",
"summary": "Re-Run: Mocks updated — DirectoryScopeGateway → TenantScopeService."
},
{
"path": "tests/Service/Access/TenantAuthorizationPolicyTest.php",
"summary": "Re-Run: Mocks updated — DirectoryScopeGateway → TenantScopeService."
},
{
"path": "tests/Service/Access/DepartmentAuthorizationPolicyTest.php",
"summary": "Re-Run: Mocks updated — DirectoryScopeGateway → TenantScopeService."
},
{
"path": "tests/Service/User/UserAccountServiceTest.php",
"summary": "Re-Run: Mocks updated — UserScopeGateway → TenantScopeService."
},
{
"path": "tests/Service/Import/ImportServiceTest.php",
"summary": "Re-Run: Mocks updated — UserScopeGateway → TenantScopeService."
}
],
"guard_evidence": [
{
"guard_id": "GR-CORE-001",
"status": "pass",
"evidence": "All gateway violations resolved. Initial run: 9 gateways fixed. Re-Run: 7 additional findings (RG-001..RG-007) fixed — AddressBookCustomFieldGateway deleted, DepartmentImportGateway/UserImportGateway stripped to repo-only, AuthScopeGateway/DirectoryScopeGateway/UserScopeGateway deleted (pure pass-throughs to TenantScopeService). Zero Gateway→Service injections remain: rg scan of all *Gateway.php constructors finds 0 Service imports."
},
{
"guard_id": "GR-CORE-002",
"status": "pass",
"evidence": "Strict layering preserved. Repositories stay in Repository layer, Services in Service layer. No new cross-layer violations. DI container registrars updated to match new wiring. No circular dependencies introduced."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "591 tests run, 0 errors introduced by this task. 1 pre-existing failure (TranslationKeysTest) verified on clean branch via git stash. All task-related tests green."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "Test modifications are strictly wiring-level, not behavioral: (1) Mock type hints updated where deleted gateways were replaced by their underlying dependency (e.g. DirectoryScopeGateway→TenantScopeService). (2) Mock method names updated where AddressBookDirectoryGateway facade methods (listTenants, listDepartments, listActiveRoles, isStrictScope) were replaced by direct repository/service calls with their native method names (list, listByTenantIds, listActive, isStrict). (3) One removed mock (listOptionsByDefinitionIds) where the gateway was deleted and the call was inlined. All test assertions and behavioral expectations remain identical — zero behavioral regressions."
},
{
"guard_id": "GR-LANG-001",
"status": "pass",
"evidence": "All PHP files follow PSR-4 namespace conventions. No new use-statement conflicts. Import ordering fixed by php-cs-fixer."
},
{
"guard_id": "GR-LANG-002",
"status": "pass",
"evidence": "PHPStan level 5: 0 new errors from this task. 4 pre-existing errors in tests/Architecture/AuthzUiContractTest.php (nullable offset warnings) verified present on clean branch before task changes."
}
],
"commands": [
{ "cmd": "docker compose exec php vendor/bin/phpunit --no-coverage", "result": "pass" },
{
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/php-cs-fixer fix --dry-run --config=.php-cs-fixer.dist.php",
"result": "pass"
},
{
"cmd": "rg 'AuthScopeGateway|DirectoryScopeGateway|UserScopeGateway|AddressBookCustomFieldGateway' lib/ pages/ tests/ --type php",
"result": "pass"
},
{
"cmd": "grep -r Gateway→Service injection scan across all *Gateway.php constructors",
"result": "pass"
},
{
"cmd": "git stash && docker compose exec php vendor/bin/phpunit --filter TranslationKeysTest (verify pre-existing); git stash pop",
"result": "fail_preexisting",
"notes": "TranslationKeysTest fails identically on clean branch — confirmed pre-existing, not caused by this task."
},
{
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress | grep AuthzUiContractTest",
"result": "fail_preexisting",
"notes": "4 errors all in tests/Architecture/AuthzUiContractTest.php — nullable offset warnings. Confirmed pre-existing."
}
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "591 tests, 0 errors introduced by this task. 1 pre-existing failure in TranslationKeysTest (missing i18n key 'No active roles are configured yet.') — verified pre-existing: same failure reproduces on clean branch (git stash → phpunit → same failure). This task introduced zero test failures."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "0 new PHPStan errors introduced by this task. 4 pre-existing errors exist in tests/Architecture/AuthzUiContractTest.php (nullable offset warnings) — these are unrelated to gateway refactoring and present on the clean branch before any task changes. This task introduced zero PHPStan errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Architecture contract preserved. No gateway wraps a Service class."
},
{
"gate_id": "QG-004",
"result": "pass",
"notes": "Zero structural matches for AuthScopeGateway, DirectoryScopeGateway, UserScopeGateway, AddressBookCustomFieldGateway across lib/, tests/, pages/. All *Gateway.php constructors verified: zero Service imports remain."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "php-cs-fixer fix --dry-run: 0 of 522 files need fixing."
}
],
"test_results": [
{ "name": "tests/Service/Access/UserAuthorizationPolicyTest.php", "result": "pass" },
{ "name": "tests/Service/Access/TenantAuthorizationPolicyTest.php", "result": "pass" },
{ "name": "tests/Service/Access/DepartmentAuthorizationPolicyTest.php", "result": "pass" },
{ "name": "tests/Service/Access/RoleAuthorizationPolicyTest.php", "result": "pass" },
{ "name": "tests/Service/Access/PermissionAuthorizationPolicyTest.php", "result": "pass" },
{ "name": "tests/Service/Access/SettingsAuthorizationPolicyTest.php", "result": "pass" },
{ "name": "tests/Service/AddressBook/AddressBookServiceTest.php", "result": "pass" },
{ "name": "tests/Service/Auth/AuthServiceTest.php", "result": "pass" },
{ "name": "tests/Service/Auth/AuthSessionTenantContextServiceTest.php", "result": "pass" },
{ "name": "tests/Service/Auth/RememberMeServiceTest.php", "result": "pass" },
{ "name": "tests/Service/Import/ImportServiceTest.php", "result": "pass" },
{ "name": "tests/Service/Tenant/TenantScopeServiceTest.php", "result": "pass" },
{ "name": "tests/Service/User/UserAccountServiceTest.php", "result": "pass" },
{ "name": "tests/Service/Settings/SettingsAppGatewayTest.php", "result": "pass" },
{ "name": "tests/Support/ThemeResolutionTest.php", "result": "pass" },
{ "name": "tests/Architecture/ContainerFactoryContractTest.php", "result": "pass" }
],
"open_items": []
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "ARCH-GATEWAY-COMPLIANCE-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "refactor(arch): enforce gateway compliance and remove service-wrapping gateways",
"notes": "Guard review pass, acceptance review pass, and reported quality gates pass; remaining CI findings are documented as pre-existing and unrelated to this task."
}

View File

@@ -1,152 +0,0 @@
{
"task_id": "ARCH-GATEWAY-COMPLIANCE-001",
"summary": "Refactor 6 Gateway classes that violate the Gateway contract by wrapping Services instead of adapting single external dependencies (repository, settings, HTTP API, scope).",
"assumptions": [
"Gateway definition: adapter for a single external dependency. No orchestration, no Service injection.",
"Gateways that are pure pass-throughs to a Service should either be collapsed into the calling Service or re-wired to depend on the Repository/external dependency directly.",
"Existing tests for Settings gateways will guide the refactoring pattern."
],
"scope": {
"in": [
"lib/Service/Access/PermissionGateway.php — wraps PermissionService, should depend on PermissionRepository",
"lib/Service/Auth/AuthAvatarGateway.php — wraps UserAvatarService, should depend on Repository or be collapsed",
"lib/Service/Settings/SettingsAppGateway.php — mixes Gateway + ThemeConfigService dependency",
"lib/Service/AddressBook/AddressBookDirectoryGateway.php — orchestrates 3 Services (TenantService, DepartmentService, RoleService)",
"lib/Service/Auth/AuthSavedFilterGateway.php — wraps UserSavedFilterService",
"lib/Service/Auth/AuthTenantSsoGateway.php — wraps TenantSsoService",
"All Factory registrars that wire the above gateways",
"All callers of the above gateways (update injection sites)"
],
"out": [
"Settings gateways (SettingsMetadataGateway, SettingsSmtpGateway etc.) — already correctly wired",
"Auth gateways that depend on repositories/scope (AuthScopeGateway, AuthCryptoGateway) — compliant",
"New feature work or UI changes",
"Test coverage for unrelated components"
]
},
"guardrails": {
"required_guard_ids": [
"GR-CORE-001",
"GR-CORE-002",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-001",
"GR-LANG-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-004",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "No Gateway in lib/Service/ injects a *Service class. All Gateway constructors receive only Repository interfaces, other Gateways, or framework abstractions."
},
{
"id": "SC-002",
"criterion": "All 6 identified gateway files are either refactored to depend on repositories/externals or collapsed into their parent Service."
},
{
"id": "SC-003",
"criterion": "PHPUnit (QG-001), PHPStan (QG-002), and Architecture Contract (QG-003) pass green."
},
{
"id": "SC-004",
"criterion": "Structural rg checks (QG-004) show zero new violations for Service instantiation in gateways."
},
{
"id": "SC-005",
"criterion": "Existing tests for callers of refactored gateways still pass with no behavioral regression; wiring-level test adjustments (mock type/method updates) are allowed."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Audit and classify each gateway violation",
"description": "For each of the 6 gateways, determine the correct fix: (A) re-wire to Repository dependency, (B) collapse into parent Service, or (C) split into multiple single-dependency gateways. Document decision per gateway.",
"guard_refs": ["GR-CORE-001"]
},
{
"id": "S2",
"title": "Refactor PermissionGateway",
"description": "Replace PermissionService dependency with PermissionRepository (and RolePermissionRepository if needed). Update PermissionGateway methods to call repository directly. Update Factory registrar.",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S3",
"title": "Refactor AuthAvatarGateway",
"description": "Replace UserAvatarService dependency with the underlying repository or collapse gateway into AuthService. Update Factory registrar and all callers.",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S4",
"title": "Refactor SettingsAppGateway",
"description": "Remove ThemeConfigService dependency. If theme resolution is needed, inject the underlying repository/settings gateway instead. Keep SettingsMetadataGateway dependency (compliant).",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S5",
"title": "Refactor AddressBookDirectoryGateway",
"description": "This gateway orchestrates 3 Services — split into single-dependency gateways or move orchestration into AddressBookService. Remove redundant pass-through methods.",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S6",
"title": "Refactor AuthSavedFilterGateway and AuthTenantSsoGateway",
"description": "Replace Service dependencies with Repository interfaces. Both are thin wrappers — re-wire to underlying repositories.",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S7",
"title": "Update Factory registrars and DI wiring",
"description": "Update all Container/Registrars/ files that wire the refactored gateways. Ensure no circular dependencies introduced.",
"guard_refs": ["GR-CORE-002", "GR-LANG-001"]
},
{
"id": "S8",
"title": "Add/update tests for refactored gateways",
"description": "Write unit tests for each refactored gateway verifying the new dependency wiring and behavior preservation.",
"guard_refs": ["GR-TEST-001", "GR-TEST-002"]
},
{
"id": "S9",
"title": "Run all quality gates",
"description": "Execute QG-001 through QG-006. Fix any regressions.",
"guard_refs": ["GR-LANG-002"]
}
],
"tests": [
"tests/Service/Access/PermissionGatewayTest.php — verify gateway uses repository directly",
"tests/Service/Auth/AuthAvatarGatewayTest.php — verify refactored dependency",
"tests/Service/Settings/SettingsAppGatewayTest.php — update existing test for removed ThemeConfigService dep",
"tests/Service/AddressBook/AddressBookDirectoryGatewayTest.php — verify single-dependency contract",
"tests/Service/Auth/AuthSavedFilterGatewayTest.php — verify refactored dependency",
"tests/Service/Auth/AuthTenantSsoGatewayTest.php — verify refactored dependency",
"tests/Architecture/CoreStarterkitContractTest.php — must stay green"
],
"acceptance_checks": [
"SC-001: grep -r 'Service' lib/Service/**/*Gateway.php constructors shows zero Service type-hints (only Repository, Gateway, framework interfaces)",
"SC-002: All 6 files listed in scope are either refactored or removed with functionality merged upstream",
"SC-003: docker compose exec php vendor/bin/phpunit exits 0 AND docker compose exec php vendor/bin/phpstan analyse exits 0",
"SC-004: QG-004 structural rg checks return 0 for gateway service-injection pattern",
"SC-005: Full test suite green with no behavioral regression; wiring-level test updates are allowed"
],
"risks": [
{
"risk": "Circular dependency introduced when gateways switch from Service to Repository dependency",
"mitigation": "Map full dependency graph before refactoring. Use PHPStan to detect cycles. Refactor one gateway at a time with QG-001+QG-002 after each."
},
{
"risk": "AddressBookDirectoryGateway split creates too many small gateways",
"mitigation": "Prefer moving orchestration into AddressBookService over splitting. Only split if single-responsibility is clearly better."
},
{
"risk": "Behavioral regression in callers that depend on gateway method signatures",
"mitigation": "Keep method signatures stable (same public API). Only change internal wiring. Existing caller tests catch regressions."
}
]
}

View File

@@ -1,43 +0,0 @@
{
"task_id": "ARCH-GATEWAY-COMPLIANCE-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004",
"SC-005"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "No Gateway in lib/Service/ injects a *Service class. All Gateway constructors receive only Repository interfaces, other Gateways, or framework abstractions.",
"result": "pass",
"evidence": "execution-report QG-004 and GR-CORE-001 both state zero Gateway->Service constructor injections remain after refactor."
},
{
"criterion_id": "SC-002",
"criterion": "All 6 identified gateway files are either refactored to depend on repositories/externals or collapsed into their parent Service.",
"result": "pass",
"evidence": "execution-report changed_files contains explicit evidence for all 6 scoped files, including lib/Service/Settings/SettingsAppGateway.php with ThemeConfigService->ThemeConfigGateway dependency update, plus the other five gateway files removed/collapsed."
},
{
"criterion_id": "SC-003",
"criterion": "PHPUnit (QG-001), PHPStan (QG-002), and Architecture Contract (QG-003) pass green.",
"result": "pass",
"evidence": "execution-report quality_gate_results mark QG-001/QG-002/QG-003 as pass and document that remaining failures are pre-existing on clean branch (TranslationKeysTest and AuthzUiContractTest), i.e. zero errors introduced by this task."
},
{
"criterion_id": "SC-004",
"criterion": "Structural rg checks (QG-004) show zero new violations for Service instantiation in gateways.",
"result": "pass",
"evidence": "execution-report QG-004 notes and commands report zero structural matches for violating gateway patterns and zero Service imports in all *Gateway.php constructors."
},
{
"criterion_id": "SC-005",
"criterion": "Existing tests for callers of refactored gateways still pass with no behavioral regression; wiring-level test adjustments (mock type/method updates) are allowed.",
"result": "pass",
"evidence": "execution-report GR-TEST-002 states all test changes are wiring-level only (mock type/method updates and one removed inlined-gateway mock) while assertions/behavior remain identical; related tests and suite execution remain green for task-induced behavior."
}
]
}

View File

@@ -1,20 +0,0 @@
{
"task_id": "ARCH-GATEWAY-COMPLIANCE-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-CORE-001",
"GR-CORE-002",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-001",
"GR-LANG-002"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-004",
"QG-006"
],
"findings": []
}

View File

@@ -1,109 +0,0 @@
{
"task_id": "AUTH-LOGIN-A11Y-REGRESSION-001",
"plan_ref": "agent-system/runs/AUTH-LOGIN-A11Y-REGRESSION-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "pages/auth/login(login).phtml",
"summary": "In der choose_tenant Stage wurde eine semantische legend mit Klasse login-tenant-select-label ergaenzt, um die Fieldset-Gruppenbezeichnung und den erwarteten A11y-Marker wiederherzustellen."
},
{
"path": "agent-system/runs/AUTH-LOGIN-A11Y-REGRESSION-001/plan.json",
"summary": "Neuer isolierter Run-Plan fuer den Auth-Login-A11y-Regression-Fix angelegt."
}
],
"guard_evidence": [
{
"guard_id": "GR-UI-009",
"status": "pass",
"evidence": "Der fehlende Marker fuer die Tenant-Auswahl wurde im bestehenden Fieldset-Kontext wiederhergestellt; AuthLoginAccessibilityContractTest ist gruen."
},
{
"guard_id": "GR-UI-013",
"status": "pass",
"evidence": "Die Wiederherstellung erfolgte semantisch als legend innerhalb des Fieldsets, ohne ARIA-Hacks oder nicht-semantische Ersatzstrukturen."
},
{
"guard_id": "GR-UI-010",
"status": "pass",
"evidence": "Der eingefuegte Label-Text nutzt t('Select tenant') und fuegt keine hartkodierte UI-Zeichenkette ein."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "Der betroffene Contract-Test und die komplette Test-Suite wurden ausgefuehrt; der vorherige Failure ist behoben und QG-001 ist wieder gruen."
}
],
"commands": [
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
"result": "pass"
},
{
"cmd": "docker compose exec php composer cs:check",
"result": "pass"
}
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "PHPUnit full: 454 tests, 10879 assertions, warnings/deprecations vorhanden aber keine Failures."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "PHPStan: [OK] No errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "CoreStarterkitContractTest: OK (11 tests, 6137 assertions)."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "composer cs:check: 0 von 514 Dateien fixbar."
}
],
"test_results": [
{
"name": "AuthLoginAccessibilityContractTest",
"result": "pass",
"notes": "OK (4 tests, 85 assertions)."
},
{
"name": "PHPUnit Full",
"result": "pass",
"notes": "OK, but there were issues: Warnings 1, Deprecations 1."
},
{
"name": "PHPStan",
"result": "pass",
"notes": "No errors."
},
{
"name": "CoreStarterkitContractTest",
"result": "pass",
"notes": "OK (11 tests, 6137 assertions)."
},
{
"name": "PHP CS Check",
"result": "pass",
"notes": "0/514 files fixable."
}
],
"open_items": []
}

View File

@@ -1,128 +0,0 @@
{
"task_id": "AUTH-LOGIN-A11Y-REGRESSION-001",
"summary": "Isolierter Fix fuer den QG-001 Blocker im Auth-Login-Flow: fehlender Tenant-Label-Marker login-tenant-select-label in der choose_tenant-Stage wiederherstellen, Accessibility-Semantik intakt halten und Full PHPUnit wieder gruen bekommen.",
"assumptions": [
"Der Fehler ist eine Regression im Markup von pages/auth/login(login).phtml und nicht in Service- oder Auth-Logik.",
"Der bestehende Contract-Test AuthLoginAccessibilityContractTest bleibt bestehen und wird nicht abgeschwaecht.",
"Es sind keine DB- oder Backend-Flow-Aenderungen erforderlich."
],
"scope": {
"in": [
"Tenant-Auswahl-Markup in pages/auth/login(login).phtml (choose_tenant stage)",
"Bei Bedarf minimale, additive CSS-Korrektur in web/css/pages/app-login.css fuer legend/label Darstellung",
"Verifikation ueber bestehenden Architecture-Contract-Test und Full PHPUnit"
],
"out": [
"Theme-Flow oder Tenant-Switch Logik ausserhalb Auth-Login",
"Aenderungen an Auth-Service-Businesslogik in lib/Service/Auth",
"Neues UI-Redesign oder Umbau des gesamten Auth-Login-Bereichs"
]
},
"guardrails": {
"required_guard_ids": [
"GR-UI-009",
"GR-UI-013",
"GR-UI-010",
"GR-TEST-001"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "Die choose_tenant-Stage enthaelt wieder eine klare textuelle Gruppenbezeichnung mit dem Marker login-tenant-select-label im Fieldset-Kontext."
},
{
"id": "SC-002",
"criterion": "AuthLoginAccessibilityContractTest ist gruen ohne Abschwaechung der Assertions."
},
{
"id": "SC-003",
"criterion": "QG-001 (Full PHPUnit) laeuft wieder erfolgreich ohne diesen Fail."
},
{
"id": "SC-004",
"criterion": "Keine Regression bei PHPStan/CoreStarterkitContract/CS-Check."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Regression reproduzieren und Ursache fixieren",
"description": "Aktuellen Failure in AuthLoginAccessibilityContractTest bestaetigen und die fehlende Marker-Stelle im choose_tenant Fieldset lokalisieren.",
"guard_refs": [
"GR-TEST-001",
"GR-UI-009"
]
},
{
"id": "S2",
"title": "Tenant-Label semantisch wiederherstellen",
"description": "In pages/auth/login(login).phtml innerhalb von fieldset.login-tenant-fieldset eine textuelle Gruppenbezeichnung mit Klasse login-tenant-select-label einfuegen (bevorzugt als legend), i18n-konform mit t().",
"guard_refs": [
"GR-UI-013",
"GR-UI-010"
]
},
{
"id": "S3",
"title": "Darstellung stabilisieren",
"description": "Nur falls notwendig minimale additive CSS-Anpassung fuer legend/label vornehmen, ohne bestehende Tastatur- oder Screenreader-Semantik zu verschlechtern.",
"guard_refs": [
"GR-UI-009",
"GR-UI-013"
]
},
{
"id": "S4",
"title": "Gates ausfuehren und evidenzbasiert abschliessen",
"description": "QG-001, QG-002, QG-003 und QG-006 ausfuehren und in execution-report dokumentieren.",
"guard_refs": [
"GR-TEST-001",
"GR-UI-009"
]
}
],
"tests": [
"docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php --no-progress",
"docker compose exec php vendor/bin/phpunit",
"docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
"docker compose exec php composer cs:check"
],
"acceptance_checks": [
"SC-001: login-tenant-select-label ist in pages/auth/login(login).phtml im choose_tenant-Fieldset vorhanden und textuell befuellt.",
"SC-002: AuthLoginAccessibilityContractTest meldet PASS.",
"SC-003: Full PHPUnit (QG-001) meldet PASS ohne den bisherigen Tenant-Label-Fehler.",
"SC-004: QG-002, QG-003 und QG-006 melden PASS."
],
"ux_notes": {
"affected_patterns": [
"Auth tenant selection fieldset in login choose_tenant stage"
],
"ui_states_required": [
"error",
"success"
],
"a11y_touchpoints": [
"Fieldset/legend semantics for tenant radio group",
"Text label visibility for tenant selection group",
"Keyboard-operable radio selection unchanged"
]
},
"risks": [
{
"risk": "Visuelle Nebenwirkung durch legend-Einfuehrung im bestehenden Login-Layout.",
"mitigation": "Nur minimale additive CSS-Anpassung, keine strukturellen Layout-Umbauten."
},
{
"risk": "Scheinbarer Fix durch Test-Anpassung statt Markup-Korrektur.",
"mitigation": "Contract-Test nicht abschwaechen; Marker semantisch im Template wiederherstellen."
}
]
}

View File

@@ -1,17 +0,0 @@
{
"task_id": "AUTH-LOGIN-A11Y-REGRESSION-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-UI-009",
"GR-UI-013",
"GR-UI-010",
"GR-TEST-001"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
],
"findings": []
}

View File

@@ -1,167 +0,0 @@
{
"task_id": "AUTH-LOGIN-ACCESSIBILITY-001",
"plan_ref": "agent-system/runs/AUTH-LOGIN-ACCESSIBILITY-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "pages/auth/login(login).phtml",
"summary": "A11y-Semantik im Login-Flow verbessert: Error-Notice als assertive Live-Region, Feld-Fehlerverknuepfung via aria-describedby/aria-invalid, Tenant-Auswahl auf fieldset/legend umgestellt, sichtbare Tenant-Bezeichnung ergaenzt, Microsoft-CTA als semantischer Link ohne role=button umgesetzt; zusaetzlich required-Attribut auf Tenant-Radios entfernt, um doppelte Sternchen-Markierung durch globale Required-Label-CSS zu beseitigen."
},
{
"path": "pages/auth/register(login).phtml",
"summary": "Back-Link auf semantischen Link ohne role-Hack umgestellt, Error-Notice als Live-Region ergaenzt und Formularfelder bei Fehlern mit aria-invalid/aria-describedby angebunden; Passwort-Hinweisbereich per ID referenzierbar gemacht."
},
{
"path": "pages/auth/forgot(login).phtml",
"summary": "Error-Notice auf role=alert + aria-live=assertive angehoben und E-Mail-Feld fuer Fehleransage per aria-describedby/aria-invalid verdrahtet."
},
{
"path": "pages/auth/reset(login).phtml",
"summary": "Reset-Form um Error-Live-Region, aria-invalid/aria-describedby und referenzierbaren Passwort-Hinweiscontainer erweitert; Passwortfelder mit autocomplete=new-password versehen."
},
{
"path": "pages/auth/verify(login).phtml",
"summary": "Verify-Code-Form fuer Screenreader-Fehleransagen erweitert (assertive Live-Region, aria-describedby/aria-invalid, one-time-code autocomplete)."
},
{
"path": "pages/auth/verify-email(login).phtml",
"summary": "Verify-Email-Form mit assertiver Error-Live-Region und Feldverknuepfungen verbessert; inline style entfernt und auf Klassenstruktur migriert."
},
{
"path": "web/css/pages/app-login.css",
"summary": "Styles fuer neue semantische Login-Struktur ergaenzt (tenant fieldset/labels/text, Microsoft-Link als Block-CTA, secondary action spacing) und role=button-abhaengigen Selektor entfernt."
},
{
"path": "tests/Architecture/AuthLoginAccessibilityContractTest.php",
"summary": "Neuer Architecture-Contract-Test fuer Auth-Login-A11y-Basics: kein role=button-Link, Error-Notices als assertive Live-Region, semantische Tenant-Auswahl und Feld-Fehlerverknuepfung."
}
],
"guard_evidence": [
{
"guard_id": "GR-UI-009",
"status": "pass",
"evidence": "Keine A11y-Regression auf den angefassten Auth-Views: role=button entfernt, Fehlerboxen als role=alert + aria-live=assertive umgesetzt, Inputs bei Fehlern per aria-describedby/aria-invalid verbunden. Der AuthLoginAccessibilityContractTest laeuft gruen (Exit 0)."
},
{
"guard_id": "GR-UI-013",
"status": "pass",
"evidence": "Interaktive Login-Komponenten sind semantisch aufgebaut: Tenant-Auswahl nutzt fieldset/legend mit nativen Radio-Inputs; Microsoft-Login bleibt semantischer Link ohne ARIA-Rollenhack; Tastaturbedienung bleibt ueber native Controls erhalten."
},
{
"guard_id": "GR-UI-014",
"status": "pass",
"evidence": "Die im Plan geforderten UI-Zustaende sind fuer den Auth-Login-Flow nachvollziehbar abgebildet: loading (Form-Submit mit laufendem Seitenwechsel im mehrstufigen Flow), empty (Hinweisbox 'No login method is available for this tenant'), error (assertive Error-Live-Regionen in allen Auth-Formularen), success (Flash-Stack in templates/partials/app-flash.phtml mit Success-Variant auf Login-Layout)."
},
{
"guard_id": "GR-UI-010",
"status": "pass",
"evidence": "Alle neu genutzten sichtbaren Texte bleiben ueber bestehende t()-Aufrufe abgedeckt; es wurden keine neuen hardcodierten UI-Strings eingefuehrt."
},
{
"guard_id": "GR-UI-015",
"status": "pass",
"evidence": "Es wurden keine neuen i18n-Keys eingefuehrt; daher keine Delta-Anforderungen fuer default_de.json/default_en.json."
},
{
"guard_id": "GR-CORE-005",
"status": "pass",
"evidence": "Serverseitige AuthZ-/Tenant-Entscheidungen in pages/auth/login().php und den Auth-Services wurden nicht veraendert; Aenderungen betreffen nur Rendering/A11y-Markup und Login-Page-CSS."
},
{
"guard_id": "GR-SEC-001",
"status": "pass",
"evidence": "POST-Formulare enthalten weiterhin Session::getCsrfInput(); serverseitige CSRF-Pruefung in pages/auth/login().php (Session::checkCsrfToken()) blieb unveraendert."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "Der neue Test ist ein statischer Contract-Test auf Dateiinhalt (keine versteckten Side-Effects, keine neuen globalen Abhaengigkeiten) und laeuft reproduzierbar in CI (Exit 0)."
}
],
"commands": [
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php composer cs:check",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit --no-progress",
"result": "pass"
},
{
"cmd": "Manual browser smoke (user-run) on /login, /register, /password/forgot, /verify-email incl. keyboard/focus/error announcement/devtools check",
"result": "pass"
}
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "Exit code 0. PHPUnit full: 433 tests, 10794 assertions, 1 warning, 1 deprecation."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "Exit code 0. PHPStan: [OK] No errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Exit code 0. CoreStarterkitContractTest: 11 tests, 6122 assertions."
},
{
"gate_id": "QG-005",
"result": "pass",
"notes": "Manueller Browser-Smoke wurde durchgefuehrt und als funktionsfaehig rueckgemeldet; gemeldeter Darstellungsfehler (doppelte Sternchen bei Tenant-Radio) wurde im Scope behoben."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "Exit code 0. composer cs:check: 0 von 512 Dateien fixbar."
}
],
"test_results": [
{
"name": "AuthLoginAccessibilityContractTest",
"result": "pass",
"notes": "Exit code 0. 4 tests, 85 assertions."
},
{
"name": "CoreStarterkitContractTest",
"result": "pass",
"notes": "Exit code 0. 11 tests, 6122 assertions."
},
{
"name": "PHPStan",
"result": "pass",
"notes": "Exit code 0. No errors."
},
{
"name": "PHP CS Check",
"result": "pass",
"notes": "Exit code 0. 0/512 files fixable."
},
{
"name": "PHPUnit Full",
"result": "pass",
"notes": "Exit code 0. 433 tests, 10794 assertions, 1 warning, 1 deprecation."
},
{
"name": "Manual Browser Smoke (Auth Login Flow)",
"result": "pass",
"notes": "User feedback: keyboard/focus/error behavior functional; one styling issue found and fixed in tenant radio presentation."
}
],
"open_items": []
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "AUTH-LOGIN-ACCESSIBILITY-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "fix(auth): improve login accessibility semantics and add auth accessibility contract test",
"notes": "Guard review pass, acceptance review pass, and all reported quality gates/CI checks are pass."
}

View File

@@ -1,198 +0,0 @@
{
"task_id": "AUTH-LOGIN-ACCESSIBILITY-001",
"summary": "Pruefe und verbessere den gesamten Auth-Login-Bereich auf offensichtliche Accessibility- und HTML-Grundlagenfehler (Semantik, Labels, Fehlermeldungen, Keyboard-Bedienbarkeit) und sichere die Verbesserungen ueber automatisierte Contract-Checks und verpflichtende Quality Gates ab.",
"assumptions": [
"Scope umfasst den gesamten Auth-Login-Flow (Login, Register, Passwort-Reset, Verify-Views) inklusive login-spezifischem Layout/CSS.",
"Backend-Authentifizierungslogik und Sicherheitsentscheidungen bleiben unveraendert; Fokus liegt auf HTML/A11y-Basics und deren Tests.",
"Bei neu eingefuehrten oder angepassten UI-Texten werden i18n-Keys in de+en im selben Merge gepflegt."
],
"scope": {
"in": [
"pages/auth/login().php",
"pages/auth/login(login).phtml",
"pages/auth/register(login).phtml",
"pages/auth/forgot(login).phtml",
"pages/auth/reset(login).phtml",
"pages/auth/verify(login).phtml",
"pages/auth/verify-email(login).phtml",
"templates/login.phtml",
"web/css/pages/app-login.css",
"tests/Architecture (neuer Auth-Login-A11y-Contract-Test)"
],
"out": [
"Aenderungen an Auth-Businesslogik in lib/Service/Auth",
"SSO/OIDC-Flow-Implementierungslogik ausserhalb von Markup-Zugaenglichkeit",
"Redesign ausserhalb notwendiger Accessibility/HTML-Korrekturen",
"Nicht-Auth-Bereiche in pages/admin und sonstigen Modulen"
]
},
"guardrails": {
"required_guard_ids": [
"GR-UI-009",
"GR-UI-013",
"GR-UI-014",
"GR-UI-010",
"GR-UI-015",
"GR-CORE-005",
"GR-SEC-001",
"GR-TEST-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-005",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "Alle im Scope liegenden Auth-Login-Views sind gegen eine feste HTML/A11y-Baseline geprueft (Form-Semantik, Labels, role/ARIA, Fokus, Keyboard) und die identifizierten offensichtlichen Verstosse sind behoben."
},
{
"id": "SC-002",
"criterion": "Interaktive Elemente im Auth-Login-Flow nutzen semantisch korrekte HTML-Elemente (keine Link-als-Button-Fehlverwendung) und bleiben vollstaendig per Tastatur bedienbar."
},
{
"id": "SC-003",
"criterion": "Fehlermeldungen und Hilfetexte in den Login-relevanten Formularen sind fuer Screenreader verstaendlich angebunden (z. B. role/aria-live/aria-describedby) und mit sichtbaren Labels konsistent."
},
{
"id": "SC-004",
"criterion": "Die benoetigten UI-Zustaende loading, empty, error und success sind fuer den Auth-Login-Flow explizit spezifiziert und im betroffenen UI-Verhalten nachvollziehbar umgesetzt."
},
{
"id": "SC-005",
"criterion": "Ein neuer Architektur-/Contract-Test deckt die vereinbarten offensichtlichen Login-HTML-A11y-Regeln ab und schlaegt bei Regressionen reproduzierbar fehl."
},
{
"id": "SC-006",
"criterion": "Alle geforderten Quality Gates (QG-001, QG-002, QG-003, QG-005, QG-006) sind mit nachvollziehbarer Evidenz erfolgreich abgeschlossen."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "A11y-Baseline und Findings-Matrix fuer Auth-Login erstellen",
"description": "Fuehre einen strukturierten Review aller Scope-Templates durch und erfasse pro View konkrete HTML/A11y-Befunde (Element-Semantik, Label-Zuordnung, Error-Ausgabe, Tastaturpfad, problematische role/ARIA-Nutzung) als umsetzbare Checkliste.",
"guard_refs": [
"GR-UI-009",
"GR-UI-013"
]
},
{
"id": "S2",
"title": "Semantische HTML-Korrekturen in Auth-Views umsetzen",
"description": "Korrigiere offensichtliche Markup-Probleme in den Auth-Views: semantisch passende Controls statt role-Hacks, valide Formularstruktur mit eindeutigen Labels/IDs, und Entfernung von a11y-kritischen Inline-Mustern zugunsten sauberer Struktur.",
"guard_refs": [
"GR-UI-013",
"GR-UI-009",
"GR-UI-010"
]
},
{
"id": "S3",
"title": "Form-Feedback und Zustandskommunikation barrierefrei machen",
"description": "Standardisiere Error-/Status-Ausgaben fuer Screenreader und Keyboard-Nutzer (z. B. role=alert oder aria-live, konsistente describedby-Verknuepfungen, klare Fokusfuehrung nach Fehlern) fuer Login-, Register-, Verify- und Reset-Formulare.",
"guard_refs": [
"GR-UI-009",
"GR-UI-013",
"GR-UI-014"
]
},
{
"id": "S4",
"title": "Tenant-Auswahl und alternative Login-Methoden absichern",
"description": "Pruefe und verbessere die Tenant-Auswahl sowie lokale/Microsoft-Login-Optionen hinsichtlich Radiogroup-Semantik, Tastaturbedienung, Fokus-Feedback und verstaendlicher textueller Bezeichnung auch bei rein visuellen Darstellungen.",
"guard_refs": [
"GR-UI-009",
"GR-UI-013",
"GR-CORE-005"
]
},
{
"id": "S5",
"title": "Automatischen Auth-Login-A11y-Contract-Test einfuehren",
"description": "Erstelle in tests/Architecture einen fokussierten Contract-Test fuer offensichtliche Login-HTML-A11y-Regeln (u. a. keine role=button-Links im Scope, erwartete Error-Live-Regionen, grundlegende Formular-Semantik), damit Regressionen im CI-Lauf sichtbar werden.",
"guard_refs": [
"GR-UI-009",
"GR-UI-013",
"GR-TEST-002"
]
},
{
"id": "S6",
"title": "i18n und Sicherheitsinvarianten nachziehen",
"description": "Stelle sicher, dass alle neu eingefuehrten sichtbaren Texte ueber t() laufen, neue Keys in de/en vorhanden sind und bestehende Login-Sicherheitsinvarianten (insbesondere CSRF-Schutz auf POST-Flows) unberuehrt bleiben.",
"guard_refs": [
"GR-UI-010",
"GR-UI-015",
"GR-SEC-001"
]
},
{
"id": "S7",
"title": "Quality Gates und manuelle Accessibility-Smokes durchfuehren",
"description": "Fuehre QG-001, QG-002, QG-003, QG-005 und QG-006 aus; dokumentiere Testevidenz inkl. Keyboard-Only-Smoke fuer alle Auth-Login-Stages und bestaetige, dass keine A11y-Regression in den geaenderten Views verbleibt.",
"guard_refs": [
"GR-UI-009",
"GR-UI-013"
]
}
],
"tests": [
"Neuer Architekturtest: tests/Architecture/AuthLoginAccessibilityContractTest.php (Scope-basierte HTML/A11y-Basics-Regeln)",
"Bestehende Architekturtests inkl. tests/Architecture/CoreStarterkitContractTest.php",
"QG-001: docker compose exec php vendor/bin/phpunit",
"QG-002: docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"QG-003: docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
"QG-005: manueller Browser-Smoke (Tab-Reihenfolge, Enter/Space-Aktivierung, Fokus-Sichtbarkeit, Fehleransage auf Auth-Login-Views)",
"QG-006: docker compose exec php composer cs:check"
],
"acceptance_checks": [
"SC-001: Pro Scope-Datei liegt ein abgearbeiteter A11y-Check vor und alle als offensichtlich eingestuften HTML/A11y-Verstoesse sind entfernt.",
"SC-002: Im Scope existiert kein verbleibendes role=\"button\" auf Links fuer button-aehnliche Interaktionen; Keyboard-Navigation durchgaengig pruefbar.",
"SC-003: Error-Ausgaben in Auth-Formularen sind als Live-Region/Alert nutzbar und relevante Inputs sind ueber Label/DescribedBy eindeutig verknuepft.",
"SC-004: Loading-, Empty-, Error- und Success-Zustaende sind fuer den Auth-Login-Flow dokumentiert und im Verhalten/Markup nachvollziehbar abgebildet.",
"SC-005: Der neue AuthLoginAccessibilityContractTest ist implementiert, laeuft gruen und faellt bei gezielter Regelverletzung reproduzierbar aus.",
"SC-006: QG-001, QG-002, QG-003, QG-005 und QG-006 sind mit PASS und kurzer Evidenz dokumentiert."
],
"ux_notes": {
"affected_patterns": [
"Auth login layout in templates/login.phtml",
"Auth form cards in pages/auth/*(login).phtml",
"Tenant selection pattern (login-tenant-choice-grid)",
"Notice/error pattern (notice[data-variant])",
"Password hint pattern (data-password-hints)"
],
"ui_states_required": [
"loading",
"empty",
"error",
"success"
],
"a11y_touchpoints": [
"Email-, Password-, Code- und Name-Inputs in Auth-Formularen",
"Tenant-Radioauswahlkarten inklusive Fokuszustand",
"Remember-me Checkbox",
"Continue/Login/Register/Verify/Reset/Back Buttons",
"Microsoft Sign-in Call-to-Action als semantischer Link-Button",
"Fehler- und Hinweisboxen als Screenreader-relevante Live-Regionen"
]
},
"risks": [
{
"risk": "Markup-Korrekturen im Auth-Bereich koennen unbeabsichtigt Flow- oder Styling-Regressions verursachen.",
"mitigation": "Aenderungen auf semantische Minimaldeltas begrenzen, pro Stage Keyboard-Smoke durchfuehren und alle geforderten Quality Gates inkl. Full PHPUnit laufen lassen."
},
{
"risk": "Zu strenge oder unpraezise Contract-Test-Regeln koennen false positives erzeugen und CI instabil machen.",
"mitigation": "Regeln eng auf offensichtliche, objektiv pruefbare Basics begrenzen und Testmeldungen mit klaren Dateihinweisen formulieren."
},
{
"risk": "Neue Hilfs- oder Status-Texte ohne vollstaendige i18n-Pflege fuehren zu inkonsistenter UI.",
"mitigation": "Jede neue t()-Key-Einfuehrung parallel in default_de.json und default_en.json pruefen und im Review explizit abhaken."
}
]
}

View File

@@ -1,50 +0,0 @@
{
"task_id": "AUTH-LOGIN-ACCESSIBILITY-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004",
"SC-005",
"SC-006"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "Alle im Scope liegenden Auth-Login-Views sind gegen eine feste HTML/A11y-Baseline geprueft (Form-Semantik, Labels, role/ARIA, Fokus, Keyboard) und die identifizierten offensichtlichen Verstosse sind behoben.",
"result": "pass",
"evidence": "execution-report.json dokumentiert A11y-Korrekturen in allen Scope-Views (login/register/forgot/reset/verify/verify-email) sowie Login-CSS; Guard-Evidence fuer GR-UI-009 und GR-UI-013 ist PASS und beschreibt entfernte role-Hacks, semantische Struktur und Keyboard-taugliche native Controls."
},
{
"criterion_id": "SC-002",
"criterion": "Interaktive Elemente im Auth-Login-Flow nutzen semantisch korrekte HTML-Elemente (keine Link-als-Button-Fehlverwendung) und bleiben vollstaendig per Tastatur bedienbar.",
"result": "pass",
"evidence": "changed_files nennt die Umstellung der Microsoft-CTA auf semantischen Link ohne role=button sowie fieldset/legend-Radiostruktur fuer Tenant-Auswahl; neuer AuthLoginAccessibilityContractTest prueft explizit 'kein role=button-Link' und lief laut test_results/commands mit PASS."
},
{
"criterion_id": "SC-003",
"criterion": "Fehlermeldungen und Hilfetexte in den Login-relevanten Formularen sind fuer Screenreader verstaendlich angebunden (z. B. role/aria-live/aria-describedby) und mit sichtbaren Labels konsistent.",
"result": "pass",
"evidence": "changed_files beschreibt fuer alle Auth-Formulare Error-Notices als role=alert mit aria-live=assertive sowie aria-describedby/aria-invalid-Verknuepfungen an Feldern; Guard-Evidence GR-UI-009 bestaetigt diese Screenreader-Anbindung als umgesetzt."
},
{
"criterion_id": "SC-004",
"criterion": "Die benoetigten UI-Zustaende loading, empty, error und success sind fuer den Auth-Login-Flow explizit spezifiziert und im betroffenen UI-Verhalten nachvollziehbar umgesetzt.",
"result": "pass",
"evidence": "Plan fordert die vier Zustaende explizit; execution-report.json weist unter guard_evidence GR-UI-014 PASS aus und benennt loading, empty ('No login method is available for this tenant'), error (assertive Error-Live-Regionen) und success (Flash-Variant im Login-Layout) als nachvollziehbar abgebildet."
},
{
"criterion_id": "SC-005",
"criterion": "Ein neuer Architektur-/Contract-Test deckt die vereinbarten offensichtlichen Login-HTML-A11y-Regeln ab und schlaegt bei Regressionen reproduzierbar fehl.",
"result": "pass",
"evidence": "execution-report.json fuehrt tests/Architecture/AuthLoginAccessibilityContractTest.php als neu eingefuehrt auf; test_results zeigt PASS (4 Tests, 85 Assertions), und GR-TEST-002 dokumentiert den Test als reproduzierbaren CI-Contract fuer die vereinbarten A11y-Basics."
},
{
"criterion_id": "SC-006",
"criterion": "Alle geforderten Quality Gates (QG-001, QG-002, QG-003, QG-005, QG-006) sind mit nachvollziehbarer Evidenz erfolgreich abgeschlossen.",
"result": "pass",
"evidence": "quality_gate_results in execution-report.json weist QG-001, QG-002, QG-003, QG-005 und QG-006 jeweils mit result=pass und Notes aus; korrespondierende commands/test_results sind ebenfalls PASS."
}
]
}

View File

@@ -1,22 +0,0 @@
{
"task_id": "AUTH-LOGIN-ACCESSIBILITY-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-UI-009",
"GR-UI-013",
"GR-UI-014",
"GR-UI-010",
"GR-UI-015",
"GR-CORE-005",
"GR-SEC-001",
"GR-TEST-002"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-005",
"QG-006"
],
"findings": []
}

View File

@@ -1,261 +0,0 @@
{
"task_id": "AUTH-LOGIN-REVIEW-001",
"plan_ref": "agent-system/runs/AUTH-LOGIN-REVIEW-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "tests/Service/Auth/AuthServiceTest.php",
"summary": "New test class (17 tests): canLoginToTenant (boundary + happy/sad), refreshSessionAuthState (user missing/not found/inactive/version match/version mismatch/no tenant), loginUserById (zero id/not found/inactive/success/no tenant/preferred tenant), login email-not-verified branch. All constructor-injected mocks."
},
{
"path": "tests/Service/Auth/RememberMeServiceTest.php",
"summary": "New test class (16 tests): rememberUser (token+cookie creation), autoLoginFromCookie (all 11 branches), forgetCurrentUser (with token/no cookie), forgetAllForUser, expireAllTokensByAdmin. Anonymous-class stub for RequestRuntimeInterface (PHPUnit 12 constraint). CS-Fixer formatting applied."
},
{
"path": "lib/Service/Auth/RememberMeService.php",
"summary": "Import order fix only (ordered_imports): moved MintyPHP\\I18n after MintyPHP\\Http\\SessionStoreInterface to satisfy php-cs-fixer."
},
{
"path": "lib/Service/Audit/FrontendTelemetryIngestService.php",
"summary": "Removed redundant ?? fallbacks on PHPDoc-typed array offsets (lines 456, 457, 484) to resolve 3 PHPStan level-5 errors. No behavioral change."
},
{
"path": "lib/App/Container/Registrars/SettingsRegistrar.php",
"summary": "CS auto-fix: ordered_imports (SettingServicesFactory alphabetical position)."
},
{
"path": "lib/Service/Auth/AuthGatewayFactory.php",
"summary": "CS auto-fix: ordered_imports (SettingServicesFactory alphabetical position)."
},
{
"path": "lib/Service/User/UserGatewayFactory.php",
"summary": "CS auto-fix: ordered_imports (SettingServicesFactory alphabetical position)."
},
{
"path": "lib/Service/Audit/ApiAuditService.php",
"summary": "CS auto-fix: ordered_imports (RequestContext before RequestRuntimeInterface)."
},
{
"path": "lib/Service/Import/ImportStateStoreService.php",
"summary": "CS auto-fix: braces_position (constructor closing paren + opening brace on same line)."
},
{
"path": "pages/admin/frontend-telemetry/ingest().php",
"summary": "CS auto-fix: ordered_imports (FrontendTelemetryIngestService before Session)."
},
{
"path": "tests/Service/Audit/ApiAuditServiceTest.php",
"summary": "CS auto-fix: ordered_imports (RequestContext before RequestRuntime)."
},
{
"path": "tests/Service/Audit/SystemAuditServiceTest.php",
"summary": "CS auto-fix: ordered_imports (RequestContext before SessionStore)."
}
],
"guard_evidence": [
{
"guard_id": "GR-CORE-005",
"status": "pass",
"evidence": "All login flow branches in AuthService mapped: email-not-verified, invalid credentials (Auth::login static), inactive account, password-login-disabled-all-tenants, no-active-tenant, success. Testable branches (canLoginToTenant, refreshSessionAuthState, loginUserById, email-not-verified) are covered by AuthServiceTest (17 tests). Static Auth::login() branches documented as untestable at unit level (open item). RememberMeService: all public methods and all autoLoginFromCookie branches covered (16 tests)."
},
{
"guard_id": "GR-SEC-001",
"status": "pass",
"evidence": "Security-relevant branches tested: (1) inactive user rejected — AuthServiceTest::testLoginUserByIdReturnsErrorWhenUserInactive, (2) no-active-tenant enforced — AuthServiceTest::testLoginUserByIdFailsWhenNoActiveTenant, (3) email-not-verified rejected — AuthServiceTest::testLoginReturnsNeedsVerificationWhenEmailNotVerified, (4) remember-me hash mismatch deletes token — RememberMeServiceTest::testAutoLoginDeletesTokenWhenHashMismatch, (5) expired token cleaned — RememberMeServiceTest::testAutoLoginDeletesTokenAndClearsCookieWhenExpired, (6) no-active-tenant auto-login logout — RememberMeServiceTest::testAutoLoginLogsOutWhenNoActiveTenant, (7) inactive user auto-login rejected — RememberMeServiceTest::testAutoLoginDeletesTokenWhenUserInactive. CSRF check is action-layer (Session::checkCsrfToken in pages/auth/login().php), needs functional test (open item)."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "33 new unit tests: AuthServiceTest (17) and RememberMeServiceTest (16). Behavior-oriented names. Coverage: AuthService — canLoginToTenant (4), refreshSessionAuthState (6), loginUserById (6), login email-not-verified (1). RememberMeService — rememberUser (1), autoLoginFromCookie (11 branches), forgetCurrentUser (2), forgetAllForUser (1), expireAllTokensByAdmin (1). No redundancy with existing SSO/OIDC/bearer-token tests."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "All tests use constructor-injected mocks via newService() factory. No new global-state dependencies. RequestRuntimeInterface uses anonymous-class stub (PHPUnit 12 cannot mock interfaces with method named 'method()'). No duplicate assertions between test classes. Existing auth tests unchanged."
}
],
"commands": [
{
"cmd": "docker compose exec -T php vendor/bin/phpunit tests/Service/Auth/AuthServiceTest.php tests/Service/Auth/RememberMeServiceTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec -T php vendor/bin/phpunit --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec -T php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec -T php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec -T php composer cs:check",
"result": "pass"
}
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "Exit code 0. 429 tests, 10703 assertions, 0 failures. 1 warning + 1 deprecation (pre-existing). Previously blocked by CoreStarterkitContractTest superglobal failure — resolved by SUPERGLOBAL-MIGRATION-001."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "Exit code 0. 0 errors. Previously had 3 errors in FrontendTelemetryIngestService.php — fixed in earlier run."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Exit code 0. 11 tests, 6116 assertions. CoreStarterkitContractTest::testPagesUseRequestInputInsteadOfSuperglobals now passes — superglobal migration completed in SUPERGLOBAL-MIGRATION-001."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "Exit code 0. 0 of 511 files need fixing."
}
],
"test_results": [
{
"name": "AuthServiceTest::testCanLoginToTenantReturnsFalseForInvalidUserId",
"result": "pass"
},
{
"name": "AuthServiceTest::testCanLoginToTenantReturnsFalseForInvalidTenantId",
"result": "pass"
},
{
"name": "AuthServiceTest::testCanLoginToTenantReturnsTrueWhenTenantIsAssigned",
"result": "pass"
},
{
"name": "AuthServiceTest::testCanLoginToTenantReturnsFalseWhenTenantNotAssigned",
"result": "pass"
},
{
"name": "AuthServiceTest::testRefreshSessionRequiresLogoutWhenUserIdIsZero",
"result": "pass"
},
{
"name": "AuthServiceTest::testRefreshSessionRequiresLogoutWhenUserNotFound",
"result": "pass"
},
{
"name": "AuthServiceTest::testRefreshSessionRequiresLogoutWhenUserIsInactive",
"result": "pass"
},
{
"name": "AuthServiceTest::testRefreshSessionReturnsOkWhenVersionMatchesAndTenantActive",
"result": "pass"
},
{
"name": "AuthServiceTest::testRefreshSessionRefreshesWhenVersionMismatch",
"result": "pass"
},
{
"name": "AuthServiceTest::testRefreshSessionRequiresLogoutWhenNoActiveTenantAfterRefresh",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginUserByIdReturnsErrorForZeroId",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginUserByIdReturnsErrorWhenUserNotFound",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginUserByIdReturnsErrorWhenUserInactive",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginUserByIdSucceedsWithActiveTenant",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginUserByIdFailsWhenNoActiveTenant",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginUserByIdSetsPreferredTenantWhenProvided",
"result": "pass"
},
{
"name": "AuthServiceTest::testLoginReturnsNeedsVerificationWhenEmailNotVerified",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testRememberUserCreatesTokenAndSetsCookie",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginReturnsFalseWhenAlreadyLoggedIn",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginReturnsFalseWhenNoCookie",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginReturnsFalseForMalformedCookieWithoutColon",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginClearsCookieWhenSelectorIsEmpty",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginClearsCookieWhenSelectorNotFoundInDb",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginDeletesTokenAndClearsCookieWhenExpired",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginDeletesTokenWhenHashMismatch",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginDeletesTokenWhenUserNotFound",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginDeletesTokenWhenUserInactive",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginSucceedsAndRotatesToken",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testAutoLoginLogsOutWhenNoActiveTenant",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testForgetCurrentUserDeletesTokenAndClearsCookie",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testForgetCurrentUserDoesNothingWhenNoCookie",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testForgetAllForUserDelegatesToRepository",
"result": "pass"
},
{
"name": "RememberMeServiceTest::testExpireAllTokensByAdminDelegatesToRepository",
"result": "pass"
}
],
"open_items": [
"AuthService::login() calls static Auth::login() (MintyPHP framework) — direct DB query, not unit-testable without integration DB or refactoring. Remaining branches testable only via loginUserById or at integration level.",
"AuthService::loginAndRedirect() and logoutAndRedirect() call Router::redirect() (static, exits). Testable only with integration/functional tests.",
"CSRF rejection in pages/auth/login().php is action-layer (Session::checkCsrfToken()). Needs functional test.",
"RememberMeService::autoLoginFromCookie() calls static Session::regenerate() / Auth::logout() — 1 PHP warning in tests (no active session). Functionally correct."
]
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "AUTH-LOGIN-REVIEW-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "test(auth): add 33 unit tests for AuthService and RememberMeService\n\nAuthServiceTest (17 tests): canLoginToTenant, refreshSessionAuthState,\nloginUserById, login email-not-verified branch.\nRememberMeServiceTest (16 tests): rememberUser, autoLoginFromCookie\n(all 11 branches), forgetCurrentUser, forgetAllForUser,\nexpireAllTokensByAdmin.\n\nAlso fixes: 3 PHPStan errors in FrontendTelemetryIngestService,\n8 CS-Fixer violations in out-of-scope files (ordered_imports,\nbraces_position).\n\nAll quality gates green: QG-001 (429 tests/0 failures),\nQG-002 (0 errors), QG-003 (11 arch tests pass), QG-006 (0 violations).\n\nTask: AUTH-LOGIN-REVIEW-001",
"notes": "Guard review pass (0 findings), acceptance review pass (SC-001..SC-005), CI pass (QG-001/002/003/006 all exit 0). Previous blocker (CoreStarterkitContractTest superglobal failure) resolved by SUPERGLOBAL-MIGRATION-001."
}

View File

@@ -1,151 +0,0 @@
{
"task_id": "AUTH-LOGIN-REVIEW-001",
"summary": "Review and harden the Auth/Login test strategy by mapping current automation coverage, identifying high-value gaps, and defining a consolidation plan for meaningful unit tests around local login and remember-me behavior.",
"assumptions": [
"Primary focus is local email/password login and remember-me flow; Microsoft OIDC/SSO tests stay unchanged unless a dependency gap is found.",
"No functional product behavior changes are required in this task; only test architecture, coverage, and maintainability improvements.",
"Auth/Login security checks in scope include server-side authorization invariants and CSRF handling on POST endpoints."
],
"scope": {
"in": [
"lib/Service/Auth/AuthService.php",
"lib/Service/Auth/RememberMeService.php",
"pages/auth/login().php",
"pages/api/v1/auth/login().php",
"tests/Service/Auth/*",
"tests/Http/ApiAuthTest.php",
"Coverage matrix and gap analysis for auth/login-related tests"
],
"out": [
"New user-facing auth features",
"UI redesign or template visual changes",
"Refactor outside auth/login testability needs",
"Broad RBAC policy redesign"
]
},
"guardrails": {
"required_guard_ids": [
"GR-CORE-005",
"GR-SEC-001",
"GR-TEST-001",
"GR-TEST-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "A complete auth/login test inventory exists, mapping current tests to concrete login behaviors (happy path, failure modes, and edge cases) for the scoped files."
},
{
"id": "SC-002",
"criterion": "A prioritized gap list exists with severity and rationale, including missing tests for security-relevant branches (authz invariants and CSRF behavior on POST login endpoints)."
},
{
"id": "SC-003",
"criterion": "A consolidation plan is defined that removes redundant/overlapping auth tests and groups remaining tests by behavior-oriented units with clear ownership."
},
{
"id": "SC-004",
"criterion": "A concrete unit-test implementation plan is defined for AuthService and RememberMeService with constructor-injected mocks and no new untestable patterns."
},
{
"id": "SC-005",
"criterion": "Planned test additions/updates are explicitly traceable to required guard IDs and required quality gates."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Build Auth/Login behavior map",
"description": "Trace login flow branches in AuthService, RememberMeService, pages/auth/login().php, and pages/api/v1/auth/login().php; map each branch to existing tests in tests/Service/Auth/* and tests/Http/ApiAuthTest.php.",
"guard_refs": [
"GR-TEST-001",
"GR-CORE-005"
]
},
{
"id": "S2",
"title": "Create coverage matrix and identify gaps",
"description": "Document covered vs uncovered scenarios: valid login, invalid credentials, inactive user, unverified email, tenant restrictions, remember-me token lifecycle, and CSRF rejection paths for POST endpoints.",
"guard_refs": [
"GR-SEC-001",
"GR-CORE-005",
"GR-TEST-001"
]
},
{
"id": "S3",
"title": "Prioritize and sequence test improvements",
"description": "Rank gaps by risk and impact, prioritizing security and auth correctness; define minimum first wave to deliver high-signal unit tests with low maintenance cost.",
"guard_refs": [
"GR-CORE-005",
"GR-TEST-001"
]
},
{
"id": "S4",
"title": "Design consolidation targets",
"description": "Define which tests to merge, split, or rewrite so each test class has a single responsibility and behavior-focused naming; avoid duplicate assertions across Service/Auth and Http layers.",
"guard_refs": [
"GR-TEST-001",
"GR-TEST-002"
]
},
{
"id": "S5",
"title": "Implement first-wave unit test refactor/additions",
"description": "Add or update tests (not product behavior) to cover highest-priority gaps; ensure constructor-injected doubles and deterministic assertions for auth outcomes and remember-me side effects.",
"guard_refs": [
"GR-TEST-001",
"GR-TEST-002",
"GR-SEC-001"
]
},
{
"id": "S6",
"title": "Run mandatory quality gates and publish evidence",
"description": "Execute QG-001, QG-002, QG-003, QG-006 and attach concise evidence linking new/updated tests to the target scenarios and guardrails.",
"guard_refs": [
"GR-TEST-001",
"GR-TEST-002"
]
}
],
"tests": [
"tests/Service/Auth/AuthServiceTest.php (new): login success, invalid credentials, inactive account, email-not-verified, no-active-tenant, remember-me delegation",
"tests/Service/Auth/RememberMeServiceTest.php (new or expanded): token create/validate/rotate/expire/forget flows and invalid-token edge cases",
"tests/Http/ApiAuthTest.php (extend only if needed): bearer token parsing edge cases remain deterministic",
"Targeted regression run for existing auth service tests under tests/Service/Auth/*",
"QG-001 full PHPUnit run",
"QG-002 PHPStan analyse",
"QG-003 CoreStarterkitContractTest",
"QG-006 composer cs:check"
],
"acceptance_checks": [
"SC-001: Coverage matrix exists and references all scoped auth/login source files and current related tests.",
"SC-002: Gap list includes at least one CSRF POST-path check and at least one server-side authz invariant check with priority.",
"SC-003: Consolidation decisions identify redundant tests and define target destination tests/classes.",
"SC-004: Planned AuthService/RememberMeService tests use constructor-injected mocks and avoid untestable static side-effect patterns.",
"SC-005: Each new/updated planned test is mapped to at least one required guard ID and all required QG IDs are scheduled and reported."
],
"risks": [
{
"risk": "Auth flow mixes service logic with session/global side effects, which can make unit tests brittle or integration-heavy.",
"mitigation": "Prefer service-level tests with mocked repositories/gateways/session store; isolate unavoidable global/session behavior to minimal focused tests."
},
{
"risk": "Over-consolidation can remove useful scenario specificity and reduce diagnostic value when tests fail.",
"mitigation": "Consolidate only duplicate setup/assertion patterns; keep distinct business/security scenarios as separate tests with behavior-centric names."
},
{
"risk": "Security-critical branches (CSRF/authz) may be under-tested if only happy-path consolidation is executed.",
"mitigation": "Treat GR-SEC-001 and GR-CORE-005 scenarios as mandatory in first-wave test additions before lower-risk cleanup."
}
]
}

View File

@@ -1,43 +0,0 @@
{
"task_id": "AUTH-LOGIN-REVIEW-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004",
"SC-005"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "A complete auth/login test inventory exists, mapping current tests to concrete login behaviors (happy path, failure modes, and edge cases) for the scoped files.",
"result": "pass",
"evidence": "execution-report lists detailed behavior coverage for AuthService and RememberMeService in guard_evidence plus full test_results (33 auth tests) and changed_files for scoped test classes."
},
{
"criterion_id": "SC-002",
"criterion": "A prioritized gap list exists with severity and rationale, including missing tests for security-relevant branches (authz invariants and CSRF behavior on POST login endpoints).",
"result": "pass",
"evidence": "execution-report open_items documents remaining gaps with rationale (static Auth::login branch limits, redirect static exits, CSRF functional-test gap), and GR-SEC-001 evidence explicitly covers authz/security branches and CSRF action-layer boundary."
},
{
"criterion_id": "SC-003",
"criterion": "A consolidation plan is defined that removes redundant/overlapping auth tests and groups remaining tests by behavior-oriented units with clear ownership.",
"result": "pass",
"evidence": "execution-report shows consolidated behavior-oriented structure into AuthServiceTest and RememberMeServiceTest with explicit branch grouping and statement that overlap with existing SSO/OIDC/bearer tests was avoided."
},
{
"criterion_id": "SC-004",
"criterion": "A concrete unit-test implementation plan is defined for AuthService and RememberMeService with constructor-injected mocks and no new untestable patterns.",
"result": "pass",
"evidence": "execution-report confirms constructor-injected mocks via newService() and 33 implemented unit tests across AuthServiceTest/RememberMeServiceTest; no new untestable pattern introduced, with existing framework static constraints explicitly documented."
},
{
"criterion_id": "SC-005",
"criterion": "Planned test additions/updates are explicitly traceable to required guard IDs and required quality gates.",
"result": "pass",
"evidence": "execution-report maps outcomes to required guards (GR-CORE-005, GR-SEC-001, GR-TEST-001, GR-TEST-002) and required gates (QG-001/002/003/006), all reported as pass with executed commands."
}
]
}

View File

@@ -1,17 +0,0 @@
{
"task_id": "AUTH-LOGIN-REVIEW-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-CORE-005",
"GR-SEC-001",
"GR-TEST-001",
"GR-TEST-002"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
],
"findings": []
}

View File

@@ -1,178 +0,0 @@
{
"task_id": "AUTH-MICROSOFT-REMEMBER-001",
"status": "completed",
"executed_at": "2026-03-10T00:00:00Z",
"steps": [
{
"id": "S1",
"title": "Datenmodell und Settings-Keys festlegen",
"status": "completed",
"changes": [
"lib/Service/Settings/SettingKeys.php — added MICROSOFT_AUTO_REMEMBER_DEFAULT_KEY and REMEMBER_TOKEN_LIFETIME_DAYS_KEY",
"db/init/init.sql — added auto_remember_mode column to tenant_auth_microsoft, added 2 settings seed rows",
"db/updates/2026-03-10-microsoft-auto-remember.sql — NEW idempotent update script"
]
},
{
"id": "S2",
"title": "Settings-Gateway fuer Login-Persistenz implementieren",
"status": "completed",
"changes": [
"lib/Service/Settings/SettingsLoginPersistenceGateway.php — NEW gateway (bool+int with validation, fallbacks)",
"lib/Service/Settings/SettingServicesFactory.php — added createSettingsLoginPersistenceGateway()",
"lib/App/Container/Registrars/SettingsRegistrar.php — registered gateway + injected into AdminSettingsService"
]
},
{
"id": "S3",
"title": "Tenant-Sso-Service auf Override-Policy erweitern",
"status": "completed",
"changes": [
"lib/Service/Auth/AuthSettingsGateway.php — added SettingsLoginPersistenceGateway dep + 2 delegate methods",
"lib/Service/Auth/AuthGatewayFactory.php — added createSettingsLoginPersistenceGateway() + wired to AuthSettingsGateway",
"lib/Repository/Tenant/TenantMicrosoftAuthRepository.php — added auto_remember_mode to SELECT/UPSERT",
"lib/Service/Auth/TenantSsoService.php — added auto_remember_mode to get/save, normalizeAutoRememberMode(), shouldAutoRememberOnMicrosoftLogin()"
]
},
{
"id": "S4",
"title": "Admin-Settings-Flow erweitern",
"status": "completed",
"changes": [
"lib/Service/Settings/AdminSettingsService.php — added SettingsLoginPersistenceGateway to constructor, buildPageData, updateFromAdmin, audit arrays"
]
},
{
"id": "S5",
"title": "Tenant-SSO-UI und AuthZ-Feldschutz erweitern",
"status": "completed",
"changes": [
"pages/admin/tenants/_form.phtml — added tri-state select for microsoft_auto_remember_mode in SSO tab",
"lib/Service/Access/TenantAuthorizationPolicy.php — added microsoft_auto_remember_mode to containsSsoFields()"
]
},
{
"id": "S6",
"title": "RememberMeService auf dynamische TTL umstellen",
"status": "completed",
"changes": [
"lib/Service/Auth/RememberMeService.php — added nullable AuthSettingsGateway param, modified lifetimeSeconds()",
"lib/Service/Auth/AuthServicesFactory.php — passes AuthSettingsGateway to RememberMeService"
]
},
{
"id": "S7",
"title": "Microsoft-Callback um Policy-gesteuertes Remember erweitern",
"status": "completed",
"changes": [
"pages/auth/microsoft/callback().php — added shouldAutoRememberOnMicrosoftLogin + rememberUser call after successful login"
]
},
{
"id": "S8",
"title": "Tests erweitern und Gates absichern",
"status": "completed",
"changes": [
"tests/Service/Settings/SettingsLoginPersistenceGatewayTest.php — NEW (16 tests: bool/int getter/setter, validation, fallback, seconds computation)",
"tests/Service/Settings/AdminSettingsServiceSessionPolicyTest.php — extended with 3 login persistence tests",
"tests/Service/Auth/TenantSsoServiceTest.php — extended with 6 shouldAutoRemember + saveTenantMicrosoftAuth tests",
"tests/Service/Access/TenantAuthorizationPolicyTest.php — extended with 1 auto_remember_mode SSO field test",
"tests/Service/Auth/RememberMeServiceTest.php — extended with 2 dynamic TTL tests + updated helper",
"i18n/default_de.json — added 12 translation keys",
"i18n/default_en.json — added 12 translation keys",
"pages/admin/settings/index(default).phtml — added Login persistence card in Security tab"
]
},
{
"id": "S9",
"title": "Review-Guard-Findings beheben (Re-Run)",
"status": "completed",
"changes": [
"lib/App/Container/Registrars/SettingsRegistrar.php — fixed import order (ordered_imports)",
"lib/Service/Auth/AuthGatewayFactory.php — fixed import order (ordered_imports)",
"tests/Service/Settings/AdminSettingsServiceSessionPolicyTest.php — fixed import order (ordered_imports)",
"pages/admin/tenants/create().php — added auto_remember_mode to POST repopulation for SSO fields",
"pages/admin/tenants/edit($id).php — added auto_remember_mode to POST repopulation for SSO fields"
]
}
],
"quality_gates": [
{
"id": "QG-001",
"description": "PHPUnit",
"status": "PASS",
"evidence": "555 tests, 13476 assertions, 0 failures"
},
{
"id": "QG-002",
"description": "PHPStan level 5",
"status": "PASS",
"evidence": "No errors"
},
{
"id": "QG-003",
"description": "CoreStarterkitContractTest",
"status": "PASS",
"evidence": "11 tests, 6253 assertions, 0 failures"
},
{
"id": "QG-005",
"description": "Browser smoke",
"status": "PASS",
"evidence": "Manually tested: Admin Settings Security tab, Tenant SSO form, and Microsoft login callback verified in browser"
},
{
"id": "QG-006",
"description": "PHP CS Fixer",
"status": "PASS",
"evidence": "530 files checked, 0 fixable issues, exit code 0"
}
],
"success_criteria": [
{
"id": "SC-001",
"status": "PASS",
"evidence": "SettingsLoginPersistenceGateway implements isMicrosoftAutoRememberDefault() (fallback false) and getRememberTokenLifetimeDays() (fallback 30, range 1-365). Both included in buildPageData(). 16 unit tests cover all paths."
},
{
"id": "SC-002",
"status": "PASS",
"evidence": "tenant_auth_microsoft.auto_remember_mode column (NULL/0/1) added. TenantSsoService get/save include the field. Tenant form has tri-state select. 6 tests cover policy resolution."
},
{
"id": "SC-003",
"status": "PASS",
"evidence": "Microsoft callback calls shouldAutoRememberOnMicrosoftLogin() after loginUserById success only. force_off and global-off+inherit correctly prevent token creation."
},
{
"id": "SC-004",
"status": "PASS",
"evidence": "RememberMeService.lifetimeSeconds() reads from AuthSettingsGateway when available, falls back to 30-day constant. Token creation and rotation both use lifetimeSeconds(). 2 tests verify dynamic TTL."
},
{
"id": "SC-005",
"status": "PASS",
"evidence": "All visible text uses t(). 12 keys added to de/en. CSRF/PRG/AuthZ unchanged. containsSsoFields() includes microsoft_auto_remember_mode."
},
{
"id": "SC-006",
"status": "PASS",
"evidence": "QG-001 (555 tests PASS), QG-002 (0 errors), QG-003 (11 contract tests PASS), QG-006 (CS Fixer PASS, 0 fixable issues, exit 0). New test file + 4 extended test files."
}
],
"resolved_findings": [
{
"id": "RG-001",
"resolution": "Ran composer cs:check (QG-006). Fixed 3 ordered_imports violations in SettingsRegistrar.php, AuthGatewayFactory.php, AdminSettingsServiceSessionPolicyTest.php. Fixed pre-existing blank_line_after_opening_tag in config/settings.php. CS check now exits 0 with 0 fixable issues."
},
{
"id": "RG-002",
"resolution": "Added 'auto_remember_mode' => $post['microsoft_auto_remember_mode'] ?? null to the SSO field repopulation array in pages/admin/tenants/create().php. Selection now survives validation errors."
},
{
"id": "RG-003",
"resolution": "Added 'auto_remember_mode' => $post['microsoft_auto_remember_mode'] ?? null to the SSO field repopulation array in pages/admin/tenants/edit($id).php. Selection now survives validation errors."
}
],
"open_items": []
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "AUTH-MICROSOFT-REMEMBER-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "feat(auth): add microsoft auto-remember policy with tenant override and configurable remember TTL",
"notes": "Guard review pass, acceptance review pass, and all reported quality gates are pass."
}

View File

@@ -1,225 +0,0 @@
{
"task_id": "AUTH-MICROSOFT-REMEMBER-001",
"summary": "Implementiere steuerbares Auto-Remember fuer erfolgreiche Microsoft-Logins mit globalem Default + Tenant-Override (inherit/force_on/force_off) und fuehre eine globale, konfigurierbare Remember-Token-TTL (in Tagen) fuer alle Remember-Tokens ein. Ziel-Runtime-Kontext: agent-system/runs/AUTH-MICROSOFT-REMEMBER-001/plan.json.",
"assumptions": [
"Bestehendes Default-Verhalten bleibt unveraendert, bis Konfiguration aktiv gesetzt wird (globaler Auto-Remember-Default = aus).",
"Die neue Remember-TTL gilt fuer alle Remember-Tokens (lokaler Login und Microsoft-basierter Remember-Token).",
"Es werden keine API-Endpunkte oder OpenAPI-Vertraege geaendert; Scope ist Web-Auth + Admin-Settings/Tenant-UI + Service/Repository.",
"Microsoft-OIDC-Handshake bleibt unveraendert; erweitert wird nur das Verhalten nach erfolgreichem Login."
],
"scope": {
"in": [
"Globales Settings-Modell fuer Microsoft Auto-Remember Default (bool) und Remember-Token-TTL in Tagen (int) inkl. Validation/Fallbacks.",
"Tenant-spezifischer Microsoft-Override mit drei Zustaenden: inherit, force_on, force_off.",
"Microsoft-Callback-Flow: Remember-Token wird bei erfolgreicher Microsoft-Anmeldung gemaess effektiver Policy gesetzt.",
"RememberMeService-Lifetime auf konfigurierbare TTL umstellen (statt fixer 30 Tage).",
"Admin-Settings-UI und Tenant-SSO-UI um neue Steuerungsfelder erweitern inkl. i18n de/en.",
"Idempotente DB-Updates in db/updates fuer bestehende Installationen sowie Anpassung von db/init/init.sql fuer Fresh-Install.",
"Automatisierte Tests und Quality-Gates fuer neue Logik, Settings-Validierung, Policy-Prioritaet und UI/Contract-Integritaet."
],
"out": [
"Aenderungen an Session Idle/Absolute Timeout-Logik.",
"Aenderungen an OIDC Discovery/Token Exchange/JWT Validierung.",
"Neues API-Interface fuer diese Konfiguration.",
"Aenderungen am bestehenden lokalen Remember-Checkbox-UX im Login-Formular.",
"Breite Refactors ausserhalb Auth/Settings/Tenant-SSO."
]
},
"guardrails": {
"required_guard_ids": [
"GR-CORE-001",
"GR-CORE-003",
"GR-CORE-005",
"GR-CORE-010",
"GR-CORE-011",
"GR-CORE-012",
"GR-SEC-001",
"GR-SEC-002",
"GR-SEC-003",
"GR-UI-009",
"GR-UI-010",
"GR-UI-013",
"GR-UI-015",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-005",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "Globale Settings fuer Microsoft Auto-Remember Default und Remember-Token-TTL sind im Settings-Layer implementiert, validiert und mit stabilen Fallbacks verfuegbar (Default: Auto-Remember aus, TTL 30 Tage)."
},
{
"id": "SC-002",
"criterion": "Tenant-Override fuer Microsoft Auto-Remember ist als 3-stufiges Modell (inherit/force_on/force_off) durchgaengig gespeichert, editierbar und im Runtime-Verhalten wirksam."
},
{
"id": "SC-003",
"criterion": "Bei erfolgreicher Microsoft-Anmeldung wird genau dann ein Remember-Token gesetzt, wenn die effektive Policy dies erlaubt; bei force_off oder global aus + inherit wird kein Token gesetzt."
},
{
"id": "SC-004",
"criterion": "RememberMeService nutzt die konfigurierbare TTL fuer Token-Erstellung und Token-Rotation; die feste 30-Tage-Konstante steuert das Verhalten nicht mehr."
},
{
"id": "SC-005",
"criterion": "Admin-Settings- und Tenant-SSO-Formulare enthalten die neuen Felder barrierearm und vollständig lokalisiert (de/en), ohne AuthZ-/CSRF-/PRG-Regression."
},
{
"id": "SC-006",
"criterion": "Neue und angepasste PHPUnit-Tests decken Settings-Validierung, Tenant-Prioritaet und Remember-Policy-Verhalten ab; alle Pflicht-Gates QG-001/002/003/005/006 sind mit Evidenz PASS."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Datenmodell und Settings-Keys festlegen",
"description": "Fuehre zwei globale Settings-Keys ein: microsoft_auto_remember_default (bool, default 0) und remember_token_lifetime_days (int, default 30). Erweitere tenant_auth_microsoft um ein tri-state-faehiges Feld fuer Tenant-Override (NULL=inherit, 1=force_on, 0=force_off). Erstelle idempotentes SQL-Update in db/updates mit Guards fuer bestehende Installationen und aktualisiere db/init/init.sql fuer Neuinstallationen.",
"guard_refs": [
"GR-CORE-010",
"GR-CORE-011",
"GR-SEC-003"
]
},
{
"id": "S2",
"title": "Settings-Gateway fuer Login-Persistenz implementieren",
"description": "Implementiere einen dedizierten Settings-Gateway fuer Login-Persistenz (bool + TTL inkl. Grenzen, z. B. 1..365 Tage) auf Basis von SettingsMetadataGateway. Binde ihn in SettingServicesFactory ein und expose die Read-Methoden ueber AuthSettingsGateway fuer Auth-Services.",
"guard_refs": [
"GR-CORE-001",
"GR-TEST-002",
"GR-CORE-011"
]
},
{
"id": "S3",
"title": "Tenant-Sso-Service auf Override-Policy erweitern",
"description": "Erweitere TenantMicrosoftAuthRepository Select/Upsert um das neue Override-Feld und normalisiere im TenantSsoService die Eingabe auf inherit/force_on/force_off. Implementiere eine eindeutige Policy-Resolution-Methode shouldAutoRememberOnMicrosoftLogin(tenantId) mit Prioritaet: force_on > force_off > global default bei inherit.",
"guard_refs": [
"GR-CORE-001",
"GR-SEC-003",
"GR-TEST-001",
"GR-TEST-002"
]
},
{
"id": "S4",
"title": "Admin-Settings-Flow erweitern",
"description": "Ergaenze AdminSettingsService buildPageData und updateFromAdmin um die neuen globalen Felder inkl. Validierung, Fehlerkeys und Persistenz. Erweitere pages/admin/settings/index(default).phtml um UI-Felder fuer globalen Microsoft-Auto-Remember-Default und Remember-TTL (nummerisch). Halte CSRF-Pruefung und PRG-Fluss unveraendert.",
"guard_refs": [
"GR-SEC-001",
"GR-CORE-012",
"GR-UI-009",
"GR-UI-010",
"GR-UI-015"
]
},
{
"id": "S5",
"title": "Tenant-SSO-UI und AuthZ-Feldschutz erweitern",
"description": "Ergaenze pages/admin/tenants/_form.phtml um ein 3-stufiges Auswahlfeld fuer Microsoft Auto-Remember Override (inherit/force_on/force_off) innerhalb des SSO-Bereichs. Erweitere create/edit-Flows fuer Formular-Repopulation und erweitere TenantAuthorizationPolicy::containsSsoFields um das neue Feld, damit unberechtigte SSO-Aenderungen weiterhin serverseitig blockiert werden.",
"guard_refs": [
"GR-CORE-005",
"GR-SEC-001",
"GR-UI-009",
"GR-UI-013",
"GR-UI-015"
]
},
{
"id": "S6",
"title": "RememberMeService auf dynamische TTL umstellen",
"description": "Entferne die harte Lifetime-Konstante als verhaltensbestimmende Quelle und nutze statt dessen den neuen Settings-Wert fuer rememberUser() und Token-Rotation in autoLoginFromCookie(). Stelle sicher, dass ungueltige Werte nicht zu unendlichen oder negativen Laufzeiten fuehren (Gateway-validierte Grenzen + Fallback).",
"guard_refs": [
"GR-CORE-001",
"GR-TEST-001",
"GR-TEST-002"
]
},
{
"id": "S7",
"title": "Microsoft-Callback um Policy-gesteuertes Remember erweitern",
"description": "Erweitere pages/auth/microsoft/callback().php nach erfolgreichem loginUserById um policy-gesteuerten Aufruf von rememberUser(userId), basierend auf der effektiven TenantSsoService-Policy. Fehlerpfade, Redirects und bestehendes Session-Timestamp-Verhalten bleiben unveraendert.",
"guard_refs": [
"GR-CORE-003",
"GR-SEC-001",
"GR-CORE-005"
]
},
{
"id": "S8",
"title": "Tests erweitern und Gates absichern",
"description": "Erweitere/erstelle PHPUnit-Tests fuer Settings-Gateway, AdminSettingsService, TenantSsoService, TenantAuthorizationPolicy und RememberMeService mit Fokus auf Defaults, Grenzen, Prioritaetsauflosung und side effects. Fuehre Pflicht-Gates aus und dokumentiere Evidenz pro SC-ID.",
"guard_refs": [
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-002",
"GR-UI-009"
]
}
],
"tests": [
"Neuer Test: tests/Service/Settings/SettingsLoginPersistenceGatewayTest.php fuer bool+TTL Fallback, Validierungsgrenzen und Persistenz.",
"Erweiterung: tests/Service/Settings/AdminSettingsServiceSessionPolicyTest.php um neue Felder microsoft_auto_remember_default und remember_token_lifetime_days (gueltig/ungueltig).",
"Erweiterung: tests/Service/Auth/TenantSsoServiceTest.php fuer Override-Normalisierung und effektive Policy-Prioritaet (force_on/force_off/inherit+global).",
"Erweiterung: tests/Service/Access/TenantAuthorizationPolicyTest.php um neuen SSO-Feldschutz fuer microsoft_auto_remember_mode.",
"Erweiterung: tests/Service/Auth/RememberMeServiceTest.php fuer dynamische TTL in rememberUser() und Token-Rotation.",
"QG-001: docker compose exec php vendor/bin/phpunit",
"QG-002: docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"QG-003: docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
"QG-005: Manueller Browser-Smoke fuer Admin Settings + Tenant SSO + Microsoft Login Callback (keine neuen JS-Fehler, kein A11y-Regressionsverhalten).",
"QG-006: docker compose exec php composer cs:check"
],
"acceptance_checks": [
"SC-001: In Settings-Layer sind microsoft_auto_remember_default und remember_token_lifetime_days mit dokumentierten Defaults/Fallbacks verfuegbar und in buildPageData sichtbar.",
"SC-002: Tenant-Form speichert und laedt den 3-stufigen Override stabil; effective policy entspricht exakt der Prioritaetsregel.",
"SC-003: Microsoft-Callback setzt Remember-Token nur bei effektiver Policy=true und nie in Fehler-/Denied-Pfaden.",
"SC-004: Remember-Tokens erhalten expires_at entsprechend konfigurierter TTL (nicht mehr fest 30 Tage) in Erzeugung und Rotation.",
"SC-005: Neue sichtbare Texte laufen ueber t() und sind in i18n/default_de.json und i18n/default_en.json vorhanden; CSRF/PRG/AuthZ-Verhalten bleibt intakt.",
"SC-006: Alle geplanten Testfaelle laufen stabil und QG-001/002/003/005/006 sind mit PASS dokumentiert."
],
"ux_notes": {
"affected_patterns": [
"Admin settings cards in pages/admin/settings/index(default).phtml",
"Tenant SSO section in pages/admin/tenants/_form.phtml",
"Existing tenant SSO toggle behavior in web/js/components/app-tenant-sso-toggle.js"
],
"ui_states_required": [
"loading",
"empty",
"error",
"success"
],
"a11y_touchpoints": [
"Neue Checkbox/Number-Input in globalen Einstellungen (Labels, Hilfe, Fehleranzeige).",
"Neues tri-state Feld im Tenant-SSO-Formular (semantische Feldbezeichnung und Keyboard-Bedienbarkeit).",
"Fehlerdarstellung via bestehende notice/form-error Muster ohne Fokus-Regression."
]
},
"risks": [
{
"risk": "Falsch konfigurierte TTL kann zu ungewollt langen Persistenzzeiten fuehren.",
"mitigation": "Strikte Validierungsgrenzen im Settings-Gateway (z. B. 1..365), klarer Default 30 und Admin-Validierungsfehler bei ungueltigen Werten."
},
{
"risk": "DB-Migration fuer bestehende Installationen kann fehlschlagen oder nicht idempotent sein.",
"mitigation": "Idempotentes db/updates Script mit information_schema-Guard und Contract-Pruefung ueber DatabaseUpdatesContractTest + QG-003."
},
{
"risk": "Tri-state Semantik kann in UI und Persistenz inkonsistent werden.",
"mitigation": "Einheitliche Normalisierung im Service, explizite Mapping-Regeln (NULL/1/0) und gezielte Unit-Tests fuer jeden Zustand."
},
{
"risk": "Callback-Erweiterung koennte unbeabsichtigt Erfolgs-/Fehlerfluesse beeinflussen.",
"mitigation": "Token-Set nur nach erfolgreichem loginUserById und unveraenderte Redirect-/Flash-Fehlerpfade; manueller SSO-Smoke in QG-005."
}
]
}

View File

@@ -1,50 +0,0 @@
{
"task_id": "AUTH-MICROSOFT-REMEMBER-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004",
"SC-005",
"SC-006"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "Globale Settings fuer Microsoft Auto-Remember Default und Remember-Token-TTL sind im Settings-Layer implementiert, validiert und mit stabilen Fallbacks verfuegbar (Default: Auto-Remember aus, TTL 30 Tage).",
"result": "pass",
"evidence": "SettingsLoginPersistenceGateway implementiert isMicrosoftAutoRememberDefault() mit Fallback false sowie getRememberTokenLifetimeDays() mit Fallback 30 und Range 1..365; Seeds sind in db/init/init.sql und db/updates/2026-03-10-microsoft-auto-remember.sql vorhanden; AdminSettingsService::buildPageData() liefert beide Werte; SettingsLoginPersistenceGatewayTest deckt Fallback/Validierung ab."
},
{
"criterion_id": "SC-002",
"criterion": "Tenant-Override fuer Microsoft Auto-Remember ist als 3-stufiges Modell (inherit/force_on/force_off) durchgaengig gespeichert, editierbar und im Runtime-Verhalten wirksam.",
"result": "pass",
"evidence": "tenant_auth_microsoft.auto_remember_mode (NULL/0/1) ist im Schema vorhanden; TenantMicrosoftAuthRepository liest/schreibt auto_remember_mode; TenantSsoService normalisiert Eingaben (inherit/0/1) und nutzt sie in shouldAutoRememberOnMicrosoftLogin(); Tenant-Form enthaelt das 3-stufige Select-Feld; Create/Edit repopulieren auto_remember_mode; TenantSsoServiceTest prueft Persistenz und Prioritaet."
},
{
"criterion_id": "SC-003",
"criterion": "Bei erfolgreicher Microsoft-Anmeldung wird genau dann ein Remember-Token gesetzt, wenn die effektive Policy dies erlaubt; bei force_off oder global aus + inherit wird kein Token gesetzt.",
"result": "pass",
"evidence": "In pages/auth/microsoft/callback().php erfolgt rememberUser() nur nach erfolgreichem loginUserById() und nur wenn TenantSsoService::shouldAutoRememberOnMicrosoftLogin() true liefert; die Policy-Methode liefert force_on=true, force_off=false, inherit=globaler Default; TenantSsoServiceTest deckt diese Policy-Faelle ab."
},
{
"criterion_id": "SC-004",
"criterion": "RememberMeService nutzt die konfigurierbare TTL fuer Token-Erstellung und Token-Rotation; die feste 30-Tage-Konstante steuert das Verhalten nicht mehr.",
"result": "pass",
"evidence": "RememberMeService verwendet lifetimeSeconds() sowohl bei create() als auch bei updateToken() (Rotation); lifetimeSeconds() liest getRememberTokenLifetimeDays() aus AuthSettingsGateway; AuthServicesFactory injiziert dieses Gateway in die Runtime-Instanz; RememberMeServiceTest verifiziert konfigurierte TTL und Fallback-Verhalten."
},
{
"criterion_id": "SC-005",
"criterion": "Admin-Settings- und Tenant-SSO-Formulare enthalten die neuen Felder barrierearm und vollstaendig lokalisiert (de/en), ohne AuthZ-/CSRF-/PRG-Regression.",
"result": "pass",
"evidence": "Admin-Settings-Form enthaelt remember_token_lifetime_days und microsoft_auto_remember_default; Tenant-SSO-Form enthaelt microsoft_auto_remember_mode; neue sichtbare Labels/Hinweise laufen ueber t() und sind in i18n/default_de.json sowie i18n/default_en.json vorhanden; CSRF+PRG bleiben in pages/admin/settings/index().php sowie Tenant create/edit POST-Flow erhalten; TenantAuthorizationPolicy::containsSsoFields() enthaelt microsoft_auto_remember_mode und ist durch TenantAuthorizationPolicyTest abgesichert."
},
{
"criterion_id": "SC-006",
"criterion": "Neue und angepasste PHPUnit-Tests decken Settings-Validierung, Tenant-Prioritaet und Remember-Policy-Verhalten ab; alle Pflicht-Gates QG-001/002/003/005/006 sind mit Evidenz PASS.",
"result": "pass",
"evidence": "Codeseitige Testabdeckung ist vorhanden (neue/erweiterte Tests fuer SettingsLoginPersistenceGateway, AdminSettingsServiceSessionPolicy, TenantSsoService, RememberMeService, TenantAuthorizationPolicy). Aktuelle Gate-Runs: QG-001 PASS (docker compose exec php vendor/bin/phpunit -> 555 Tests, 13476 Assertions, Exit 0), QG-002 PASS (phpstan: No errors), QG-003 PASS (CoreStarterkitContractTest: 11 Tests, 6253 Assertions), QG-006 PASS (composer cs:check: 0 fixable issues). QG-005 ist im execution-report als PASS dokumentiert (manueller Browser-Smoke fuer Admin Settings Security Tab, Tenant SSO und Microsoft Callback)."
}
]
}

View File

@@ -1,30 +0,0 @@
{
"task_id": "AUTH-MICROSOFT-REMEMBER-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-CORE-001",
"GR-CORE-003",
"GR-CORE-005",
"GR-CORE-010",
"GR-CORE-011",
"GR-CORE-012",
"GR-SEC-001",
"GR-SEC-002",
"GR-SEC-003",
"GR-UI-009",
"GR-UI-010",
"GR-UI-013",
"GR-UI-015",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-002"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-005",
"QG-006"
],
"findings": []
}

View File

@@ -1,125 +0,0 @@
{
"task_id": "CODE-DEDUP-ACTIONS-001",
"plan_ref": "agent-system/runs/CODE-DEDUP-ACTIONS-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "lib/Service/Audit/AuditMetadataEnricher.php",
"summary": "CREATED — S1: New service resolving audit user IDs (created_by, modified_by, etc.) into display labels and UUIDs. Accepts configurable field list. Depends on UserReadRepositoryInterface."
},
{
"path": "lib/Service/Data/GridUserCountEnricher.php",
"summary": "CREATED — S2: New stateless service that collects entity IDs from grid rows, batch-queries total and active user counts via callables, and returns a lookup map."
},
{
"path": "tests/Service/Audit/AuditMetadataEnricherTest.php",
"summary": "CREATED — S3: 6 tests covering label resolution, email fallback, zero/missing IDs, user not found, and custom fields."
},
{
"path": "tests/Service/Data/GridUserCountEnricherTest.php",
"summary": "CREATED — S3: 6 tests covering typical rows, empty rows, ID deduplication, zero IDs, zero counts, and negative inactive clamping."
},
{
"path": "lib/App/Container/Registrars/AppServicesRegistrar.php",
"summary": "S4: Registered AuditMetadataEnricher (with UserReadRepository dep) and GridUserCountEnricher (stateless) in DI container."
},
{
"path": "pages/admin/tenants/edit($id).php",
"summary": "S5: Replaced 27 lines of inline audit metadata resolution (created_by, modified_by, status_changed_by) with single AuditMetadataEnricher call. Removed unused $userAccountService variable."
},
{
"path": "pages/admin/roles/edit($id).php",
"summary": "S5: Replaced 18 lines of inline audit metadata resolution (created_by, modified_by) with single AuditMetadataEnricher call. Removed unused $userAccountService variable."
},
{
"path": "pages/admin/departments/edit($id).php",
"summary": "S5: Replaced 18 lines of inline audit metadata resolution (created_by, modified_by) with single AuditMetadataEnricher call. Removed unused $userAccountService variable."
},
{
"path": "pages/admin/users/edit($id).php",
"summary": "S5: Replaced 28 lines of inline audit metadata resolution (created_by, modified_by, active_changed_by) with single AuditMetadataEnricher call inside permission guard."
},
{
"path": "pages/admin/roles/data().php",
"summary": "S6: Replaced 12 lines of ID collection + user count aggregation with GridUserCountEnricher.computeCounts() call. Row loop uses count map lookup."
},
{
"path": "pages/admin/departments/data().php",
"summary": "S6: Replaced 11 lines of ID collection + user count aggregation with GridUserCountEnricher.computeCounts() call. Row loop uses count map lookup."
},
{
"path": "pages/admin/tenants/data().php",
"summary": "S6: Replaced 8 lines of ID collection + user count aggregation inside $fetchRows closure with GridUserCountEnricher.computeCounts() call. Also uses enricher IDs for SSO map query."
}
],
"guard_evidence": [
{
"guard_id": "GR-CORE-001",
"status": "pass",
"evidence": "AuditMetadataEnricher is a Service class with single repository dependency (UserReadRepositoryInterface). GridUserCountEnricher is a stateless Service with no dependencies. Both follow the Service contract — business logic, no HTML, no orchestration flow."
},
{
"guard_id": "GR-CORE-002",
"status": "pass",
"evidence": "Strict layering preserved. New services registered in AppServicesRegistrar. Action files call services via app() helper. No cross-layer violations. No circular dependencies."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "12 new tests added (6 per service). 603 total tests run, 0 errors introduced by this task. 1 pre-existing failure (TranslationKeysTest) unrelated."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "New tests cover edge cases: zero IDs, missing fields, user not found, email fallback, ID deduplication, negative inactive clamping. No existing tests modified."
},
{
"guard_id": "GR-LANG-001",
"status": "pass",
"evidence": "All new PHP files follow PSR-4 namespace conventions. Import ordering auto-fixed by php-cs-fixer."
},
{
"guard_id": "GR-LANG-002",
"status": "pass",
"evidence": "PHPStan level 5: 0 new errors from this task. 4 pre-existing errors in AuthzUiContractTest.php are unrelated."
}
],
"commands": [
{ "cmd": "docker compose exec php vendor/bin/phpunit --no-coverage", "result": "pass" },
{ "cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress", "result": "pass" },
{ "cmd": "docker compose exec php vendor/bin/php-cs-fixer fix --dry-run --config=.php-cs-fixer.dist.php", "result": "pass" },
{ "cmd": "docker compose exec php vendor/bin/phpunit --filter CoreStarterkitContractTest|ContainerFactoryContractTest", "result": "pass" },
{ "cmd": "rg 'AuditMetadataEnricher' pages/admin/ (SC-001: usage in 4 edit action files)", "result": "pass" },
{ "cmd": "rg 'GridUserCountEnricher' pages/admin/ (SC-002: usage in 3 data endpoint files)", "result": "pass" },
{ "cmd": "rg 'created_by_label.*=.*trim' pages/ (SC-003: zero inline audit resolution left)", "result": "pass" }
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "603 tests, 0 errors introduced by this task. 12 new tests added. 1 pre-existing failure (TranslationKeysTest: missing i18n key) — verified pre-existing: same failure reproduces on clean branch (git stash → phpunit → same failure). This task introduced zero test failures."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "0 new PHPStan errors introduced by this task. 4 pre-existing errors in tests/Architecture/AuthzUiContractTest.php (nullable offset warnings) — verified pre-existing: present on clean branch before any task changes. This task introduced zero PHPStan errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Architecture contract tests (CoreStarterkitContractTest + ContainerFactoryContractTest): 24 tests, 6454 assertions, all green."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "php-cs-fixer fix --dry-run: 0 of 526 files need fixing."
}
],
"test_results": [
{ "name": "tests/Service/Audit/AuditMetadataEnricherTest.php", "result": "pass" },
{ "name": "tests/Service/Data/GridUserCountEnricherTest.php", "result": "pass" },
{ "name": "tests/Architecture/CoreStarterkitContractTest.php", "result": "pass" },
{ "name": "tests/Architecture/ContainerFactoryContractTest.php", "result": "pass" }
],
"open_items": []
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "CODE-DEDUP-ACTIONS-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "refactor(actions): deduplicate audit metadata and grid user count enrichment",
"notes": "review-guards and review-acceptance are pass; execution-report documents QG-001/QG-002/QG-003/QG-006 as pass with no task-introduced failures."
}

View File

@@ -1,128 +0,0 @@
{
"task_id": "CODE-DEDUP-ACTIONS-001",
"summary": "Extract repeated action-layer patterns (audit metadata enrichment, grid user count calculation) into shared service classes to eliminate ~260 lines of duplication across 7+ action/data files.",
"assumptions": [
"Audit metadata enrichment (created_by, modified_by labels) repeats identically across 4+ edit actions.",
"Grid user count calculation repeats identically across 3 data endpoints (roles, departments, tenants).",
"Both patterns are pure data transformation — suitable for service extraction without changing action flow."
],
"scope": {
"in": [
"New: lib/Service/Audit/AuditMetadataEnricher.php — extract audit label resolution",
"New: lib/Service/Data/GridUserCountEnricher.php — extract user count aggregation for grid data endpoints",
"pages/admin/tenants/edit($id).php — replace inline audit metadata with service call",
"pages/admin/roles/edit($id).php — replace inline audit metadata with service call",
"pages/admin/users/edit($tenantslug,$id).php — replace inline audit metadata with service call",
"pages/admin/departments/edit($id).php — replace inline audit metadata with service call",
"pages/admin/roles/data().php — replace inline user count logic with service call",
"pages/admin/departments/data().php — replace inline user count logic with service call",
"pages/admin/tenants/data().php — replace inline user count logic with service call",
"Factory registrars for new services"
],
"out": [
"View files (.phtml) — not touched",
"Repository SQL queries — not touched",
"Frontend JS/CSS — not touched",
"Permission/authorization logic — not touched"
]
},
"guardrails": {
"required_guard_ids": [
"GR-CORE-001",
"GR-CORE-002",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-001",
"GR-LANG-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "AuditMetadataEnricher exists in lib/Service/Audit/ and is used by all 4 edit actions instead of inline created_by/modified_by resolution."
},
{
"id": "SC-002",
"criterion": "GridUserCountEnricher exists in lib/Service/Data/ and is used by all 3 data endpoints instead of inline count aggregation."
},
{
"id": "SC-003",
"criterion": "No inline audit-label or user-count-aggregation logic remains in pages/ action files."
},
{
"id": "SC-004",
"criterion": "PHPUnit, PHPStan, Architecture Contract, and CS checks pass green; zero new failures introduced by this task (pre-existing failures are out of scope)."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Create AuditMetadataEnricher service",
"description": "Extract the repeated pattern of resolving created_by, modified_by, status_changed_by, active_changed_by into display labels (name or email fallback) and UUIDs. Accept entity array by reference, list of audit fields to resolve, and UserAccountService dependency.",
"guard_refs": ["GR-CORE-001", "GR-TEST-002"]
},
{
"id": "S2",
"title": "Create GridUserCountEnricher service",
"description": "Extract repeated pattern of collecting entity IDs from grid rows, querying user counts (total + active), and enriching rows with count fields. Generic enough to work with roles, departments, and tenants.",
"guard_refs": ["GR-CORE-001", "GR-TEST-002"]
},
{
"id": "S3",
"title": "Write unit tests for both new services",
"description": "AuditMetadataEnricher: test with present/missing/zero audit IDs, user not found fallback. GridUserCountEnricher: test with empty rows, mixed IDs, zero counts.",
"guard_refs": ["GR-TEST-001"]
},
{
"id": "S4",
"title": "Register new services in DI container",
"description": "Add factory registrations in appropriate Container/Registrars/ files.",
"guard_refs": ["GR-CORE-002"]
},
{
"id": "S5",
"title": "Refactor edit actions to use AuditMetadataEnricher",
"description": "Replace inline audit metadata resolution in all 4 edit actions with enricher service call.",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S6",
"title": "Refactor data endpoints to use GridUserCountEnricher",
"description": "Replace inline user count aggregation in all 3 data endpoints with enricher service call.",
"guard_refs": ["GR-CORE-001", "GR-CORE-002"]
},
{
"id": "S7",
"title": "Run all quality gates",
"description": "QG-001, QG-002, QG-003, QG-006.",
"guard_refs": ["GR-LANG-002"]
}
],
"tests": [
"tests/Service/Audit/AuditMetadataEnricherTest.php — new, covers label resolution edge cases",
"tests/Service/Data/GridUserCountEnricherTest.php — new, covers count aggregation edge cases",
"tests/Architecture/CoreStarterkitContractTest.php — must stay green"
],
"acceptance_checks": [
"SC-001: rg 'AuditMetadataEnricher' pages/admin/ shows usage in 4 edit action files",
"SC-002: rg 'GridUserCountEnricher' pages/admin/ shows usage in 3 data endpoint files",
"SC-003: rg 'created_by_label.*=.*trim' pages/ returns 0 matches (no inline resolution left)",
"SC-004: QG-001 + QG-002 + QG-003 + QG-006 introduce zero new failures (pre-existing issues are out of scope)"
],
"risks": [
{
"risk": "Edit actions have slightly different audit fields (some have status_changed_by, others don't)",
"mitigation": "Design AuditMetadataEnricher to accept a configurable list of field names to resolve. Each caller specifies which fields apply."
},
{
"risk": "Data endpoints have slightly different count repository interfaces",
"mitigation": "Design GridUserCountEnricher to accept a callable or repository interface for count queries, keeping it generic."
}
]
}

View File

@@ -1,31 +0,0 @@
{
"task_id": "CODE-DEDUP-ACTIONS-001",
"verdict": "pass",
"checked_criterion_ids": ["SC-001", "SC-002", "SC-003", "SC-004"],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "AuditMetadataEnricher exists in lib/Service/Audit/ and is used by all 4 edit actions instead of inline created_by/modified_by resolution.",
"result": "pass",
"evidence": "execution-report.changed_files lists lib/Service/Audit/AuditMetadataEnricher.php plus all 4 edit actions as refactored; execution-report.commands includes the SC-001 rg check with result pass."
},
{
"criterion_id": "SC-002",
"criterion": "GridUserCountEnricher exists in lib/Service/Data/ and is used by all 3 data endpoints instead of inline count aggregation.",
"result": "pass",
"evidence": "execution-report.changed_files lists lib/Service/Data/GridUserCountEnricher.php plus all 3 data endpoints as refactored; execution-report.commands includes the SC-002 rg check with result pass."
},
{
"criterion_id": "SC-003",
"criterion": "No inline audit-label or user-count-aggregation logic remains in pages/ action files.",
"result": "pass",
"evidence": "execution-report.commands includes the SC-003 inline-pattern grep check with result pass ('rg created_by_label.*=.*trim pages/')."
},
{
"criterion_id": "SC-004",
"criterion": "PHPUnit, PHPStan, Architecture Contract, and CS checks pass green; zero new failures introduced by this task (pre-existing failures are out of scope).",
"result": "pass",
"evidence": "execution-report.quality_gate_results marks QG-001, QG-002, QG-003, and QG-006 as pass, and execution-report.commands marks all corresponding commands as pass; notes explicitly classify remaining issues as pre-existing and not introduced by this task."
}
]
}

View File

@@ -1,19 +0,0 @@
{
"task_id": "CODE-DEDUP-ACTIONS-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-CORE-001",
"GR-CORE-002",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-001",
"GR-LANG-002"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
],
"findings": []
}

View File

@@ -1,102 +0,0 @@
{
"task_id": "CODE-DEDUP-REPOSITORY-001",
"plan_ref": "agent-system/runs/CODE-DEDUP-REPOSITORY-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "lib/Repository/Support/RepositoryArrayHelper.php",
"summary": "CREATED — S1: New final class with 4 public static methods: unwrap(), unwrapList(), extractIds(), sanitizePositiveIds(). Provides shared array helpers for MintyPHP DB layer row unwrapping and ID handling."
},
{
"path": "tests/Repository/Support/RepositoryArrayHelperTest.php",
"summary": "CREATED — S2: 21 tests covering all 4 methods with edge cases: null input, empty arrays, missing keys, deduplication, custom ID fields, fallback to row, zero/negative IDs, type casting, reindexing."
},
{
"path": "lib/Repository/Tenant/TenantRepository.php",
"summary": "S3: Removed private unwrap() and unwrapList() methods. Replaced all inline calls with RepositoryArrayHelper::unwrap/unwrapList/extractIds/sanitizePositiveIds. Eliminated ~40 lines of duplication."
},
{
"path": "lib/Repository/Access/RoleRepository.php",
"summary": "S4: Removed private unwrap() and unwrapList() methods. Replaced all inline calls with RepositoryArrayHelper equivalents. Eliminated ~35 lines of duplication."
},
{
"path": "lib/Repository/Access/PermissionRepository.php",
"summary": "S4: Replaced inline unwrap patterns ($row['permissions'] ?? $row) in list(), listActive(), listPaged(), find(), findByKey() with RepositoryArrayHelper::unwrapList/unwrap. Eliminated ~25 lines of duplication."
},
{
"path": "lib/Repository/Org/DepartmentRepository.php",
"summary": "S5: Removed private unwrap() and unwrapList() methods. Replaced all inline calls with RepositoryArrayHelper equivalents. Eliminated ~40 lines of duplication."
},
{
"path": "lib/Repository/Org/UserDepartmentRepository.php",
"summary": "S5: Replaced inline extractIds pattern in listDepartmentIdsByUserId() and sanitizePositiveIds in countByDepartmentIds() with RepositoryArrayHelper calls. Eliminated ~12 lines of duplication."
},
{
"path": "lib/Repository/Access/UserRoleRepository.php",
"summary": "S6: Replaced inline extractIds patterns in listRoleIdsByUserId() and listUserIdsByRoleIds(), and sanitizePositiveIds in countByRoleIds() and listUserIdsByRoleIds() with RepositoryArrayHelper calls. Eliminated ~20 lines of duplication."
}
],
"guard_evidence": [
{
"guard_id": "GR-CORE-001",
"status": "pass",
"evidence": "RepositoryArrayHelper is a final class in lib/Repository/Support/ — the repository support layer. Contains only static array manipulation methods. No business logic, no HTTP, no HTML. All 6 refactored repositories remain in their correct namespaces with unchanged public API."
},
{
"guard_id": "GR-LANG-001",
"status": "pass",
"evidence": "All new and modified PHP files follow PSR-4 namespace conventions. MintyPHP\\Repository\\Support\\RepositoryArrayHelper maps to lib/Repository/Support/RepositoryArrayHelper.php. CS Fixer reports 0 of 528 files need fixing."
},
{
"guard_id": "GR-LANG-002",
"status": "pass",
"evidence": "PHPStan level 5: 0 new errors from this task. 4 pre-existing errors in tests/Architecture/AuthzUiContractTest.php (nullable offset warnings) — verified pre-existing in prior tasks."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "21 new tests added in RepositoryArrayHelperTest. 624 total tests run, 0 errors introduced by this task. 1 pre-existing failure (TranslationKeysTest: missing i18n key) — verified pre-existing in prior tasks."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "New tests cover edge cases: null/empty input, missing table keys, ID deduplication, custom ID fields, fallback to row, zero/negative IDs, type casting from strings, array reindexing. No existing tests modified."
}
],
"commands": [
{ "cmd": "docker compose exec php vendor/bin/phpunit --no-coverage", "result": "pass" },
{ "cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress", "result": "pass" },
{ "cmd": "docker compose exec php vendor/bin/php-cs-fixer fix --dry-run --config=.php-cs-fixer.dist.php", "result": "pass" },
{ "cmd": "docker compose exec php vendor/bin/phpunit --filter CoreStarterkitContractTest|ContainerFactoryContractTest", "result": "pass" },
{ "cmd": "rg 'RepositoryArrayHelper' lib/Repository/ --files-with-matches (SC-001+SC-002: usage in 6 repo files + helper itself)", "result": "pass" },
{ "cmd": "rg 'private function unwrap' lib/Repository/ (SC-002: 0 inline unwrap in scoped repos; remaining are out-of-scope repos)", "result": "pass" }
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "624 tests, 0 errors introduced by this task. 21 new tests added. 1 pre-existing failure (TranslationKeysTest: missing i18n key) — verified pre-existing in prior tasks (git stash → phpunit → same failure). This task introduced zero test failures."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "0 new PHPStan errors introduced by this task. 4 pre-existing errors in tests/Architecture/AuthzUiContractTest.php (nullable offset warnings) — verified pre-existing in prior tasks. This task introduced zero PHPStan errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Architecture contract tests (CoreStarterkitContractTest + ContainerFactoryContractTest): 24 tests, 6466 assertions, all green."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "php-cs-fixer fix --dry-run: 0 of 528 files need fixing."
}
],
"test_results": [
{ "name": "tests/Repository/Support/RepositoryArrayHelperTest.php", "result": "pass" },
{ "name": "tests/Architecture/CoreStarterkitContractTest.php", "result": "pass" },
{ "name": "tests/Architecture/ContainerFactoryContractTest.php", "result": "pass" }
],
"open_items": []
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "CODE-DEDUP-REPOSITORY-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "refactor(repository): deduplicate unwrap and id array helpers",
"notes": "Guard and acceptance reviews are pass; execution-report quality gates QG-001, QG-002, QG-003, and QG-006 are pass with no task-introduced failures."
}

View File

@@ -1,124 +0,0 @@
{
"task_id": "CODE-DEDUP-REPOSITORY-001",
"summary": "Extract repeated repository boilerplate (ID extraction, array sanitization, unwrap/normalization) into shared support utilities to eliminate ~290 lines of duplication across 6+ repository files.",
"assumptions": [
"MintyPHP DB layer returns nested arrays with table-name keys (e.g. $row['tenants']).",
"All repositories use the same unwrap, unwrapList, extractIds, and sanitizePositiveIds patterns.",
"A trait or static helper class is the right abstraction — no base repository class needed (repositories are thin)."
],
"scope": {
"in": [
"lib/Repository/Tenant/TenantRepository.php — unwrap, unwrapList, listIds extraction",
"lib/Repository/Access/RoleRepository.php — unwrap, unwrapList, listIds extraction",
"lib/Repository/Org/DepartmentRepository.php — unwrap, unwrapList, listIds extraction",
"lib/Repository/Access/PermissionRepository.php — ID extraction patterns",
"lib/Repository/Access/UserRoleRepository.php — ID sanitization",
"lib/Repository/Org/UserDepartmentRepository.php — ID sanitization",
"New file: lib/Repository/Support/RepositoryArrayHelper.php"
],
"out": [
"Repository method signatures (public API unchanged)",
"Repository SQL queries (not touched)",
"Service layer changes",
"Frontend changes"
]
},
"guardrails": {
"required_guard_ids": [
"GR-CORE-001",
"GR-LANG-001",
"GR-LANG-002",
"GR-TEST-001",
"GR-TEST-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "A single RepositoryArrayHelper class exists in lib/Repository/Support/ providing extractIds(), sanitizePositiveIds(), unwrap(), and unwrapList() as static methods."
},
{
"id": "SC-002",
"criterion": "All 6+ repository files use RepositoryArrayHelper instead of inline implementations. No duplicate unwrap/extractIds/sanitize logic remains."
},
{
"id": "SC-003",
"criterion": "Repository public method signatures and return values are identical before and after refactoring (pure internal refactor)."
},
{
"id": "SC-004",
"criterion": "PHPUnit, PHPStan, Architecture Contract, and CS checks pass green; zero new failures introduced by this task (pre-existing failures are out of scope)."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Create RepositoryArrayHelper",
"description": "Create lib/Repository/Support/RepositoryArrayHelper.php with static methods: extractIds(array $rows, string $tableKey): array, sanitizePositiveIds(array $ids): array, unwrap(?array $row, string $tableKey): ?array, unwrapList(mixed $rows, string $tableKey): array.",
"guard_refs": ["GR-CORE-001", "GR-LANG-001"]
},
{
"id": "S2",
"title": "Write unit tests for RepositoryArrayHelper",
"description": "Test all 4 methods with edge cases: empty arrays, null input, missing table keys, mixed types, duplicate IDs, negative IDs.",
"guard_refs": ["GR-TEST-001", "GR-TEST-002"]
},
{
"id": "S3",
"title": "Refactor TenantRepository",
"description": "Replace inline unwrap/unwrapList/extractIds with RepositoryArrayHelper calls. Verify no signature changes.",
"guard_refs": ["GR-CORE-001"]
},
{
"id": "S4",
"title": "Refactor RoleRepository and PermissionRepository",
"description": "Replace inline implementations with RepositoryArrayHelper calls.",
"guard_refs": ["GR-CORE-001"]
},
{
"id": "S5",
"title": "Refactor DepartmentRepository and UserDepartmentRepository",
"description": "Replace inline implementations with RepositoryArrayHelper calls.",
"guard_refs": ["GR-CORE-001"]
},
{
"id": "S6",
"title": "Refactor UserRoleRepository",
"description": "Replace inline ID sanitization with RepositoryArrayHelper::sanitizePositiveIds().",
"guard_refs": ["GR-CORE-001"]
},
{
"id": "S7",
"title": "Run all quality gates",
"description": "QG-001 PHPUnit, QG-002 PHPStan, QG-003 Architecture, QG-006 CS check.",
"guard_refs": ["GR-LANG-002"]
}
],
"tests": [
"tests/Repository/Support/RepositoryArrayHelperTest.php — new, covers all 4 static methods with edge cases",
"tests/Architecture/CoreStarterkitContractTest.php — must stay green",
"Existing service tests that call repository methods indirectly — must stay green"
],
"acceptance_checks": [
"SC-001: File lib/Repository/Support/RepositoryArrayHelper.php exists with 4 public static methods",
"SC-002: rg 'foreach.*\\$row.*\\[.*id.*\\]' lib/Repository/ returns only RepositoryArrayHelper, not inline implementations",
"SC-003: git diff shows no changes to public method signatures in repository files",
"SC-004: QG-001 + QG-002 + QG-003 + QG-006: zero new failures introduced by this task (pre-existing failures are out of scope)"
],
"risks": [
{
"risk": "Subtle behavioral difference in edge cases between inline and helper implementations",
"mitigation": "Write comprehensive helper tests first (TDD). Run full QG-001 after each repository file change."
},
{
"risk": "Table key naming inconsistency across repositories",
"mitigation": "Audit all unwrap calls first to confirm table key naming. The helper accepts the key as parameter, so naming differences are handled."
}
]
}

View File

@@ -1,36 +0,0 @@
{
"task_id": "CODE-DEDUP-REPOSITORY-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "A single RepositoryArrayHelper class exists in lib/Repository/Support/ providing extractIds(), sanitizePositiveIds(), unwrap(), and unwrapList() as static methods.",
"result": "pass",
"evidence": "execution-report.changed_files lists lib/Repository/Support/RepositoryArrayHelper.php as created with all four static methods; execution-report.commands includes the SC-001+SC-002 RepositoryArrayHelper usage check with result pass."
},
{
"criterion_id": "SC-002",
"criterion": "All 6+ repository files use RepositoryArrayHelper instead of inline implementations. No duplicate unwrap/extractIds/sanitize logic remains.",
"result": "pass",
"evidence": "execution-report.changed_files lists 6 repository refactors (Tenant, Role, Permission, Department, UserDepartment, UserRole) and removal/replacement of inline helper logic; execution-report.commands includes pass results for RepositoryArrayHelper usage and private unwrap duplication checks."
},
{
"criterion_id": "SC-003",
"criterion": "Repository public method signatures and return values are identical before and after refactoring (pure internal refactor).",
"result": "pass",
"evidence": "execution-report.guard_evidence for GR-CORE-001 states unchanged public API across refactored repositories, and changed_files summaries describe internal helper replacement/removal of private methods only."
},
{
"criterion_id": "SC-004",
"criterion": "PHPUnit, PHPStan, Architecture Contract, and CS checks pass green; zero new failures introduced by this task (pre-existing failures are out of scope).",
"result": "pass",
"evidence": "execution-report.quality_gate_results marks QG-001, QG-002, QG-003, and QG-006 as pass; execution-report.commands records all corresponding commands as pass; notes classify remaining issues as pre-existing and not introduced by this task."
}
]
}

View File

@@ -1,18 +0,0 @@
{
"task_id": "CODE-DEDUP-REPOSITORY-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-CORE-001",
"GR-LANG-001",
"GR-LANG-002",
"GR-TEST-001",
"GR-TEST-002"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-006"
],
"findings": []
}

View File

@@ -1,121 +0,0 @@
{
"task_id": "DOCS-RESTRUCTURE-001",
"plan_ref": "agent-system/runs/DOCS-RESTRUCTURE-001/plan.json",
"status": "done",
"changed_files": [
{ "path": "docs/index.md", "summary": "Rebuilt with exactly 4 Diataxis groups (Tutorials, How-to Guides, Explanation, Reference); all 42 files referenced once each." },
{ "path": "docs/tutorial-01-erste-schritte.md", "summary": "Renamed from erste-schritte.md; date updated to 2026-03-06; internal links migrated." },
{ "path": "docs/tutorial-02-systemueberblick.md", "summary": "Renamed from 00-systemueberblick.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-03-setup-und-erster-login.md", "summary": "Renamed from 01-setup-und-erster-login.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-04-architektur-request-flow.md", "summary": "Renamed from 03-architektur-request-flow.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-05-erste-aenderung-ui.md", "summary": "Renamed from 04-erste-aenderung-ui.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-06-erste-aenderung-backend.md", "summary": "Renamed from 05-erste-aenderung-backend.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-07-security-tenant-scope.md", "summary": "Renamed from 06-security-tenant-scope.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-08-api-erweitern.md", "summary": "Renamed from 07-api-erweitern.md; date updated; internal links migrated." },
{ "path": "docs/tutorial-09-advanced-scheduler-sso.md", "summary": "Renamed from 08-advanced-scheduler-sso.md; date updated; internal links migrated." },
{ "path": "docs/howto-erste-aenderung.md", "summary": "Renamed from erste-aenderung.md; date updated; internal links migrated." },
{ "path": "docs/howto-globale-suche.md", "summary": "Renamed from globale-suche.md; date updated." },
{ "path": "docs/howto-importe.md", "summary": "Renamed from importe.md; date updated." },
{ "path": "docs/howto-benutzerdefinierte-felder.md", "summary": "Renamed from benutzerdefinierte-felder.md; date updated." },
{ "path": "docs/howto-geplante-aufgaben.md", "summary": "Renamed from geplante-aufgaben.md; date updated." },
{ "path": "docs/howto-auth-sso-smoke-test.md", "summary": "Renamed from auth-sso-smoke-test.md; date updated." },
{ "path": "docs/howto-anfragelimits.md", "summary": "Renamed from anfragelimits.md; date updated." },
{ "path": "docs/howto-rbac-permissions-playbook.md", "summary": "Renamed from rbac-permissions-playbook.md; date updated." },
{ "path": "docs/howto-request-input-validation.md", "summary": "Renamed from request-input-validation.md; date updated." },
{ "path": "docs/howto-docker-lokal.md", "summary": "Renamed from docker-lokal.md; date updated." },
{ "path": "docs/howto-docker-produktiv.md", "summary": "Renamed from docker-produktiv.md; date updated." },
{ "path": "docs/howto-betriebscheck-doctor.md", "summary": "Renamed from betriebscheck-doctor.md; date updated." },
{ "path": "docs/howto-fehlerbehebung.md", "summary": "Renamed from fehlerbehebung.md; date updated." },
{ "path": "docs/howto-lokale-entwicklung.md", "summary": "Renamed from lokale-entwicklung.md; date updated; internal links migrated." },
{ "path": "docs/howto-dokumentation-erweitern.md", "summary": "Renamed from dokumentation-erweitern.md; IA section updated to Diataxis 4-quadrant model; naming convention documented; date updated; internal links migrated." },
{ "path": "docs/explanation-architektur.md", "summary": "Renamed from architektur.md; date updated; internal links migrated." },
{ "path": "docs/explanation-di-container.md", "summary": "Renamed from di-container.md; added Letzte Aktualisierung line." },
{ "path": "docs/explanation-sicherheitsmodell.md", "summary": "Renamed from sicherheitsmodell.md; date updated; internal links migrated." },
{ "path": "docs/explanation-einstellungen-speicherung.md", "summary": "Renamed from einstellungen-speicherung.md; date updated." },
{ "path": "docs/explanation-docker-betrieb.md", "summary": "Renamed from docker-betrieb.md; date updated; internal links migrated." },
{ "path": "docs/reference-domain-glossar.md", "summary": "Renamed from 02-domain-glossar.md; date updated; internal links migrated." },
{ "path": "docs/reference-api.md", "summary": "Renamed from api.md; date updated; internal links migrated." },
{ "path": "docs/reference-konfiguration.md", "summary": "Renamed from konfiguration.md; date updated." },
{ "path": "docs/reference-konventionen.md", "summary": "Renamed from konventionen.md; date updated." },
{ "path": "docs/reference-lib-standards.md", "summary": "Renamed from lib-standards.md; date updated; internal links migrated." },
{ "path": "docs/reference-benutzer-lifecycle-policy.md", "summary": "Renamed from benutzer-lifecycle-policy.md; date updated." },
{ "path": "docs/reference-frontend-css.md", "summary": "Renamed from frontend-css.md; date updated." },
{ "path": "docs/reference-frontend-javascript.md", "summary": "Renamed from frontend-javascript.md; date updated." },
{ "path": "docs/reference-entwickler-checkliste.md", "summary": "Renamed from entwickler-checkliste.md; date updated; internal links migrated." },
{ "path": "docs/reference-codex-prompts.md", "summary": "Renamed from codex-prompts.md; date updated." },
{ "path": "docs/reference-agents-overview.md", "summary": "Migrated from docs/agents/overview.md to top-level; added Letzte Aktualisierung line." },
{ "path": "docs/reference-agents-roles.md", "summary": "Migrated from docs/agents/roles.md to top-level; added Letzte Aktualisierung line." },
{ "path": "docs/reference-agents-flow.md", "summary": "Migrated from docs/agents/flow.md to top-level; added Letzte Aktualisierung line." },
{ "path": "tests/Architecture/CodexSkillsContractTest.php", "summary": "Updated 2 assertions to reference new slug docs/reference-codex-prompts.md instead of old docs/codex-prompts.md." }
],
"guard_evidence": [
{
"guard_id": "GR-UI-009",
"status": "pass",
"evidence": "No a11y regression: changes are purely Markdown restructuring (file renames, link updates, date normalization). No HTML, JS, CSS, or interactive UI components were touched."
},
{
"guard_id": "GR-SEC-004",
"status": "pass",
"evidence": "rg for external asset patterns (![](https://...), <script src=https>, <link href=https>, @import url(https)) across docs/*.md returned 0 hits. All <script> matches are inline code examples in markdown (explanation-architektur.md, reference-frontend-javascript.md, reference-entwickler-checkliste.md)."
}
],
"commands": [
{ "cmd": "git mv (42 renames: tutorials, howto, explanation, reference prefixes + agents/ migration)", "result": "pass" },
{ "cmd": "sed -i '' (bulk link replacement across all docs/*.md)", "result": "pass" },
{ "cmd": "sed -i '' (date update to 2026-03-06 across all docs/*.md)", "result": "pass" },
{ "cmd": "Index structure check: grep '^## ' docs/index.md returns exactly Tutorials, How-to Guides, Explanation, Reference", "result": "pass" },
{ "cmd": "Coverage check: comm -23 files vs refs and comm -13 refs vs files both empty; no duplicates in index", "result": "pass" },
{ "cmd": "Subfolder check: find docs -mindepth 2 -name '*.md' returns 0", "result": "pass" },
{ "cmd": "Link existence check: all /docs/<slug>.md references resolve to existing files", "result": "pass" },
{ "cmd": "External asset check (GR-SEC-004): rg for remote URLs in markdown returns 0 real hits", "result": "pass" },
{ "cmd": "Format check: all 42 content files have exactly 1 H1 and 1 Letzte Aktualisierung line", "result": "pass" },
{ "cmd": "openapi.yaml unchanged: git diff docs/openapi.yaml shows no changes", "result": "pass" },
{ "cmd": "docker compose exec php composer cs:check — 0 of 511 files need fixing", "result": "pass" }
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "429 tests, 10703 assertions, 0 failures. (Warnings: 1 deprecation, unrelated to docs.)"
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "PHPStan level 5: [OK] No errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "CoreStarterkitContractTest: 11 tests, 6116 assertions, OK."
},
{
"gate_id": "QG-004",
"result": "pass",
"notes": "All structural rg checks return 0 violations."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "PHP CS Fixer: 0 of 511 files need fixing."
}
],
"test_results": [
{ "name": "T1: Index has exactly 4 Diataxis H2 groups in correct order", "result": "pass" },
{ "name": "T2: Coverage check (no orphans, no duplicates)", "result": "pass" },
{ "name": "T3: Subfolder check (0 nested .md files)", "result": "pass" },
{ "name": "T4: Alt-link check (no old slugs or /docs/agents/ refs remain)", "result": "pass" },
{ "name": "T5: Link existence check (all refs resolve)", "result": "pass" },
{ "name": "T6: GR-SEC-004 external asset check", "result": "pass" },
{ "name": "T7: Document format check (1x H1 + Letzte Aktualisierung per file)", "result": "pass" },
{ "name": "T8: openapi.yaml unchanged and linked in reference section", "result": "pass" },
{ "name": "T9: QG-001 PHPUnit full", "result": "pass" },
{ "name": "T10: QG-002 PHPStan", "result": "pass" },
{ "name": "T11: QG-003 Architecture Core Contract", "result": "pass" },
{ "name": "T12: QG-004 Structural rg checks", "result": "pass" },
{ "name": "T13: Manual docs smoke (/admin/docs)", "result": "skipped", "notes": "Requires manual browser verification: navigation groups, TOC anchors, search on renamed slugs." }
],
"open_items": [
"SC-007: Manual smoke test in /admin/docs pending — verify navigation shows all 4 groups, TOC anchors work, and search returns hits for renamed pages."
]
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "DOCS-RESTRUCTURE-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "docs: restructure documentation to Diataxis layout and update doc slug references",
"notes": "Guard and acceptance reviews are pass; reported quality gates are pass."
}

View File

@@ -1,167 +0,0 @@
{
"task_id": "DOCS-RESTRUCTURE-001",
"summary": "Kompletter Umbau der Dokumentation im Verzeichnis docs nach einem strikt angewendeten Diátaxis-Modell mit genau vier Quadranten (Tutorials, How-to, Explanation, Reference), konsequenter Umbenennung der Markdown-Dateien, Migration der Agent-Dokumente aus dem Unterordner in Top-Level-Slugs und vollständiger Aktualisierung aller internen Doku-Links ohne fachliche Inhaltsänderung.",
"assumptions": [
"Alte /admin/docs/<slug>-Links dürfen brechen; es werden keine Kompatibilitäts-Stubs für alte Slugs vorgesehen.",
"Die technische Scope-Grenze bleibt bei doc/*; es werden keine Änderungen in lib/, pages/, templates/ oder web/ vorgenommen.",
"Weil der aktuelle Docs-Parser nur /docs/<slug>.md indexiert, müssen alle Markdown-Zieldateien im Top-Level docs/ liegen (kein docs/agents/*.md im Zielzustand).",
"docs/openapi.yaml bleibt unter exakt diesem Pfad bestehen, da der API-Docs-Endpunkt diese Datei fest referenziert.",
"Die Aufgabe ist rein strukturell/redaktionell (Verständlichkeit, IA, Navigierbarkeit) und prüft keine fachliche Korrektheit der Inhalte."
],
"scope": {
"in": [
"docs/index.md",
"alle Markdown-Dateien in docs/*.md",
"Migration von docs/agents/*.md zu neuen Top-Level-Dateien in docs/",
"Aktualisierung interner Links und Verweise innerhalb docs/*.md",
"Beibehaltung und Verlinkung von docs/openapi.yaml innerhalb der neuen Reference-Struktur"
],
"out": [
"neue fachliche Themen",
"UI-Redesign oder Änderungen am Docs-Renderer",
"Änderungen an Routing/Parser/Service-Code außerhalb docs/",
"inhaltliche Fachvalidierung bestehender technischer Aussagen"
]
},
"guardrails": {
"required_guard_ids": [
"GR-UI-009",
"GR-SEC-004"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-004"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "docs/index.md enthält exakt vier Diátaxis-Hauptgruppen in dieser Reihenfolge: Tutorials, How-to Guides, Explanation, Reference."
},
{
"id": "SC-002",
"criterion": "Alle bisherigen 42 Markdown-Dokumente (39 in docs/*.md plus 3 aus docs/agents/*.md) sind auf neue Diátaxis-konforme Slugs migriert und liegen danach vollständig im Top-Level docs/."
},
{
"id": "SC-003",
"criterion": "Jede Markdown-Datei (außer docs/index.md) ist genau einmal in docs/index.md referenziert; es gibt keine Orphans und keine Duplikat-Referenzen."
},
{
"id": "SC-004",
"criterion": "Alle internen /docs/*.md-Verweise in der Doku zeigen auf existierende neue Slugs; es bleiben keine Verweise auf alte Slugs oder /docs/agents/*.md übrig."
},
{
"id": "SC-005",
"criterion": "Alle migrierten Seiten behalten genau ein H1 und eine Zeile 'Letzte Aktualisierung: YYYY-MM-DD' und sind sprachlich auf Klarheit/Lesbarkeit in ihrem Diátaxis-Typ harmonisiert."
},
{
"id": "SC-006",
"criterion": "docs/openapi.yaml bleibt unverändert am Pfad docs/openapi.yaml und wird in der neuen Reference-Struktur konsistent verlinkt."
},
{
"id": "SC-007",
"criterion": "Admin-Dokumentation unter /admin/docs funktioniert mit der neuen Struktur: Navigation zeigt alle Gruppen/Seiten, Seiteninhalte und TOC-Anker laden korrekt, Suche liefert Treffer auf umbenannte Seiten."
},
{
"id": "SC-008",
"criterion": "Alle festgelegten Quality Gates (QG-001, QG-002, QG-003, QG-004) sind erfolgreich ausgeführt und Guard-Evidence für GR-UI-009 und GR-SEC-004 ist dokumentiert."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Verbindliche Ziel-IA und Rename-Konvention festlegen",
"description": "Definiere als harte Regel: Dateinamenformat <quadrant-prefix>-<slug>.md, wobei Prefix nur tutorial-, howto-, explanation-, reference- ist. Lege die vollständige Zuordnung fest: Tutorials = erste-schritte, 00-systemueberblick, 01-setup-und-erster-login, 03-architektur-request-flow, 04-erste-aenderung-ui, 05-erste-aenderung-backend, 06-security-tenant-scope, 07-api-erweitern, 08-advanced-scheduler-sso; How-to = erste-aenderung, globale-suche, importe, benutzerdefinierte-felder, geplante-aufgaben, auth-sso-smoke-test, anfragelimits, rbac-permissions-playbook, request-input-validation, docker-lokal, docker-produktiv, betriebscheck-doctor, fehlerbehebung, lokale-entwicklung, dokumentation-erweitern; Explanation = architektur, di-container, sicherheitsmodell, einstellungen-speicherung, docker-betrieb; Reference = 02-domain-glossar, api, konfiguration, konventionen, lib-standards, benutzer-lifecycle-policy, frontend-css, frontend-javascript, entwickler-checkliste, codex-prompts sowie agents/overview, agents/roles, agents/flow.",
"guard_refs": [
"GR-UI-009",
"GR-SEC-004"
]
},
{
"id": "S2",
"title": "Dateien konsequent umbenennen und Agents ins Top-Level migrieren",
"description": "Benenne alle Markdown-Dateien gemäß Mapping um. Tutorials nummeriert als tutorial-01-erste-schritte bis tutorial-09-advanced-scheduler-sso; How-to als howto-<alter-slug>; Explanation als explanation-<alter-slug>; Reference als reference-<alter-slug>. Migriere docs/agents/overview.md, docs/agents/roles.md, docs/agents/flow.md zu docs/reference-agents-overview.md, docs/reference-agents-roles.md, docs/reference-agents-flow.md und entferne anschließend den Unterordner docs/agents aus der aktiven Struktur.",
"guard_refs": [
"GR-UI-009"
]
},
{
"id": "S3",
"title": "docs/index.md strikt auf vier Diátaxis-Gruppen umbauen",
"description": "Ersetze die bestehende gemischte Struktur in docs/index.md vollständig durch genau vier Gruppen: Tutorials, How-to Guides, Explanation, Reference. Trage jede Ziel-Datei genau einmal ein, behalte das bestehende Linkformat /docs/<slug>.md bei (wegen Parser), und aktualisiere 'Letzte Aktualisierung' auf den Migrationstag.",
"guard_refs": [
"GR-UI-009"
]
},
{
"id": "S4",
"title": "Alle internen Links und Querverweise auf neue Slugs migrieren",
"description": "Führe eine vollständige Link-Migration in allen docs/*.md durch: alte Slugs, alte nummerierte Lernpfad-Dateinamen und /docs/agents/*-Verweise durch neue Diátaxis-Slugs ersetzen; Referenzen auf docs/openapi.yaml bestehen lassen. Stelle sicher, dass keine tote Verlinkung und kein Alt-Slug-Backlink verbleibt.",
"guard_refs": [
"GR-UI-009",
"GR-SEC-004"
]
},
{
"id": "S5",
"title": "Lesbarkeits- und Diátaxis-Format normalisieren",
"description": "Harmonisiere die Seitenstruktur ohne fachliche Neuinhalte: pro Seite genau ein H1, 'Letzte Aktualisierung', klarer Zielabschnitt und Diátaxis-typische Abschnittsreihenfolge (Tutorial: Lernschritte/Ergebnis; How-to: Voraussetzungen/Schritte/Validierung; Explanation: Kontext/Warum/Abgrenzung; Reference: Fakten, Kontrakte, Tabellen). Passe auch die Contributor-Regeln in der umbenannten How-to-Dokumentationsseite an die neue IA an.",
"guard_refs": [
"GR-UI-009"
]
},
{
"id": "S6",
"title": "Guard-Checks, Quality-Gates und Docs-Smoke ausführen",
"description": "Führe die strukturellen Doku-Checks und alle geforderten Gates aus, dokumentiere Ergebnisse nach SC-ID: Link-Konsistenz, Index-Abdeckung, keine Remote-Asset-Einbindung in Markdown, anschließend QG-001, QG-002, QG-003, QG-004. Abschließend manueller Smoke in /admin/docs für Navigation, TOC-Anker und Suche.",
"guard_refs": [
"GR-UI-009",
"GR-SEC-004"
]
}
],
"tests": [
"Index-Strukturcheck: docs/index.md enthält exakt 4 H2-Gruppen (Tutorials, How-to Guides, Explanation, Reference) in dieser Reihenfolge.",
"Abdeckungscheck ohne Orphans/Duplikate: Vergleich docs/*.md (ohne index.md) gegen alle in docs/index.md referenzierten /docs/<slug>.md muss leer sein.",
"Subfolder-Check: find docs -mindepth 2 -name '*.md' liefert 0 Treffer (keine verbleibenden docs/agents/*.md).",
"Alt-Link-Check: rg auf alte Slugs und /docs/agents/ in docs/*.md liefert 0 Treffer.",
"Link-Existenzcheck: jeder /docs/<slug>.md-Verweis in docs/*.md zeigt auf eine vorhandene Datei docs/<slug>.md.",
"GR-SEC-004 Check: rg nach externen Asset-Einbindungen in Markdown (z. B. ![](...https://...), <script>, <link>, @import url(https://)) liefert 0 Treffer.",
"Dokumentenformat-Check: jede Datei in docs/*.md hat genau ein H1 und eine Zeile 'Letzte Aktualisierung: YYYY-MM-DD'.",
"Manueller Docs-Smoke: /admin/docs lädt, Gruppennavigation vollständig, TOC-Anker springen korrekt, Suche findet Treffer in umbenannten Seiten.",
"QG-001: docker compose exec php vendor/bin/phpunit",
"QG-002: docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"QG-003: docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
"QG-004: Structural rg checks gemäß agent-system/checks/quality-gates.json"
],
"acceptance_checks": [
"SC-001: Sichtprüfung docs/index.md bestätigt exakt vier Diátaxis-Gruppen in korrekter Reihenfolge.",
"SC-002: Dateiinventur zeigt 42 migrierte Markdown-Seiten im Top-Level docs/ und keine verbleibenden docs/agents/*.md.",
"SC-003: Abdeckungscheck (Dateiliste vs. Index-Referenzen) ist leer und ohne Duplikate.",
"SC-004: Alt-Link-Check und Link-Existenzcheck laufen ohne Treffer/Fehler.",
"SC-005: Format-Check bestätigt pro Seite 1x H1 + Letzte-Aktualisierung + lesbare Diátaxis-Struktur.",
"SC-006: docs/openapi.yaml existiert unverändert und ist in der Reference-Sektion korrekt verlinkt.",
"SC-007: Manueller Smoke in /admin/docs bestätigt Navigation, Inhaltsanzeige, TOC und Suche für neue Slugs.",
"SC-008: QG-001, QG-002, QG-003 und QG-004 sind mit PASS dokumentiert; Guard-Evidence für GR-UI-009 und GR-SEC-004 liegt vor."
],
"risks": [
{
"risk": "Große Rename-Migration kann interne Links oder Suchpfade unbemerkt brechen.",
"mitigation": "Vollständige Alt-Link- und Link-Existenz-Checks automatisiert ausführen und erst nach fehlerfreiem Ergebnis finalisieren."
},
{
"risk": "Durch Wegfall alter Slugs verlieren bestehende Bookmarks und gespeicherte Admin-Docs-Links ihre Gültigkeit.",
"mitigation": "Breaking explizit dokumentieren, neue Diátaxis-Slug-Liste im Changelog/Release-Hinweis veröffentlichen."
},
{
"risk": "Beim Vereinheitlichen der Struktur könnten unbeabsichtigt fachliche Aussagen verändert werden.",
"mitigation": "Redaktionelle Änderungen auf Struktur/Klarheit begrenzen, fachliche Sätze nicht neu interpretieren, Diff-Review mit Fokus auf semantische Unverändertheit."
},
{
"risk": "Nicht beachtete Parser-Regeln für docs/index.md könnten dazu führen, dass Seiten trotz vorhandener Datei nicht im Admin auftauchen.",
"mitigation": "Index strikt im erwarteten /docs/<slug>.md-Format pflegen und nach jeder größeren Index-Änderung /admin/docs-Smoketest durchführen."
}
]
}

View File

@@ -1,65 +0,0 @@
{
"task_id": "DOCS-RESTRUCTURE-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004",
"SC-005",
"SC-006",
"SC-007",
"SC-008"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "docs/index.md enthält exakt vier Diátaxis-Hauptgruppen in dieser Reihenfolge: Tutorials, How-to Guides, Explanation, Reference.",
"result": "pass",
"evidence": "execution-report.commands enthält den Index-Strukturcheck mit pass; zusätzlich test_results T1 = pass."
},
{
"criterion_id": "SC-002",
"criterion": "Alle bisherigen 42 Markdown-Dokumente (39 in docs/*.md plus 3 aus docs/agents/*.md) sind auf neue Diátaxis-konforme Slugs migriert und liegen danach vollständig im Top-Level docs/.",
"result": "pass",
"evidence": "execution-report.changed_files listet die 42 migrierten Doku-Dateien; execution-report.commands enthält 'Subfolder check ... returns 0' mit pass."
},
{
"criterion_id": "SC-003",
"criterion": "Jede Markdown-Datei (außer docs/index.md) ist genau einmal in docs/index.md referenziert; es gibt keine Orphans und keine Duplikat-Referenzen.",
"result": "pass",
"evidence": "execution-report.commands enthält 'Coverage check ... both empty; no duplicates in index' mit pass; test_results T2 = pass."
},
{
"criterion_id": "SC-004",
"criterion": "Alle internen /docs/*.md-Verweise in der Doku zeigen auf existierende neue Slugs; es bleiben keine Verweise auf alte Slugs oder /docs/agents/*.md übrig.",
"result": "pass",
"evidence": "execution-report.commands enthält Alt-Link-/Existenz-Prüfungen mit pass; test_results T4 und T5 = pass."
},
{
"criterion_id": "SC-005",
"criterion": "Alle migrierten Seiten behalten genau ein H1 und eine Zeile 'Letzte Aktualisierung: YYYY-MM-DD' und sind sprachlich auf Klarheit/Lesbarkeit in ihrem Diátaxis-Typ harmonisiert.",
"result": "pass",
"evidence": "execution-report.commands enthält den Format-Check 'all 42 content files have exactly 1 H1 and 1 Letzte Aktualisierung line' mit pass; test_results T7 = pass."
},
{
"criterion_id": "SC-006",
"criterion": "docs/openapi.yaml bleibt unverändert am Pfad docs/openapi.yaml und wird in der neuen Reference-Struktur konsistent verlinkt.",
"result": "pass",
"evidence": "execution-report.commands enthält 'openapi.yaml unchanged ... no changes' mit pass; test_results T8 = pass."
},
{
"criterion_id": "SC-007",
"criterion": "Admin-Dokumentation unter /admin/docs funktioniert mit der neuen Struktur: Navigation zeigt alle Gruppen/Seiten, Seiteninhalte und TOC-Anker laden korrekt, Suche liefert Treffer auf umbenannte Seiten.",
"result": "pass",
"evidence": "Manueller Smoke-Test wurde laut User-Bestaetigung erfolgreich durchgefuehrt (Navigation, TOC-Anker, Suche auf umbenannten Slugs)."
},
{
"criterion_id": "SC-008",
"criterion": "Alle festgelegten Quality Gates (QG-001, QG-002, QG-003, QG-004) sind erfolgreich ausgeführt und Guard-Evidence für GR-UI-009 und GR-SEC-004 ist dokumentiert.",
"result": "pass",
"evidence": "execution-report.quality_gate_results zeigt QG-001 bis QG-004 jeweils pass; execution-report.guard_evidence enthält GR-UI-009 und GR-SEC-004 jeweils pass."
}
],
"missing_or_wrong": []
}

View File

@@ -1,16 +0,0 @@
{
"task_id": "DOCS-RESTRUCTURE-001",
"verdict": "pass",
"checked_guard_ids": [
"GR-UI-009",
"GR-SEC-004"
],
"checked_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-004",
"QG-006"
],
"findings": []
}

View File

@@ -1,297 +0,0 @@
{
"task_id": "LOGIN-AUTH-QUALITY-CHECK-001",
"plan_ref": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json",
"status": "done",
"changed_files": [
{
"path": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json",
"summary": "Explizite Guard-Matrix fuer alle In-Scope-Flows angelegt (Flow->Datei-Zuordnung, Guard-Refs, priorisierte Findings, priority_summary mit critical/high/medium open=0)."
},
{
"path": "pages/auth/register().php",
"summary": "Register-POST auf klares POST-Gate umgestellt und CSRF-Pruefung vor Request-Verarbeitung erzwungen; CSRF-Fehlerpfad konsistent umgesetzt."
},
{
"path": "templates/login.phtml",
"summary": "Lokalisierte Passwort-Toggle-Labels als Datenattribute fuer JS-Consumer bereitgestellt."
},
{
"path": "web/js/components/app-password-toggle.js",
"summary": "Login-Passwort-Toggle konsolidiert: duplicate-init-Guard eingefuehrt (passwordToggleBound), erneute Wrapper/Listener-Bindungen verhindert."
},
{
"path": "web/js/components/app-password-hints.js",
"summary": "Login-Passworthinweise konsolidiert: idempotente Container-Bindung eingefuehrt (passwordHintsBound), Mehrfach-Listener bei Re-Init verhindert."
},
{
"path": "web/css/pages/app-login.css",
"summary": "Ungenutzten Legacy-Selektor .back_to_login entfernt (konkrete CSS-Cleanup-Massnahme im Login-Scope)."
},
{
"path": "i18n/default_de.json",
"summary": "Neue i18n-Keys fuer Passwort-Toggle (Show/Hide password) in Deutsch ergaenzt."
},
{
"path": "i18n/default_en.json",
"summary": "Neue i18n-Keys fuer Passwort-Toggle (Show/Hide password) in Englisch ergaenzt."
},
{
"path": "tests/Service/Auth/PasswordResetServiceTest.php",
"summary": "Neue Unit-Tests fuer PasswordResetService (requestReset/verifyCode/resetPassword inkl. expiry/attempts/used/success)."
},
{
"path": "tests/Service/Auth/EmailVerificationServiceTest.php",
"summary": "Neue Unit-Tests fuer EmailVerificationService (sendVerification/verifyCode/resendVerification inkl. already_verified/attempt-limit)."
},
{
"path": "tests/Service/Auth/MicrosoftOidcServiceTest.php",
"summary": "Automatisierten SSO-Regressionstest fuer Callback-Domain-Policy ergänzt (email_domain_not_allowed)."
},
{
"path": "tests/Architecture/AuthLoginFrontendCleanupContractTest.php",
"summary": "Neuer Architektur-Contract fuer Frontend-Cleanup-Invarianten (kein .back_to_login, duplicate-binding guards in Login-JS)."
},
{
"path": "tests/Architecture/AuthLoginAccessibilityContractTest.php",
"summary": "A11y-Contract erweitert: Toggle muss lokalisierte Labels nutzen und darf kein tabIndex=-1 verwenden."
},
{
"path": "tests/Architecture/AuthRegisterSecurityContractTest.php",
"summary": "Neuer Security-Contract: Register-POST muss CSRF vor Payload-Verarbeitung pruefen."
}
],
"guard_evidence": [
{
"guard_id": "GR-CORE-001",
"status": "pass",
"evidence": "Guard-Matrix dokumentiert pro Auth-Flow die Layer-Zuordnung; Aenderungen bleiben in bestehender Schichtung (Pages orchestration, Services business logic)."
},
{
"guard_id": "GR-CORE-002",
"status": "pass",
"evidence": "QG-004 structural checks weiterhin 0; keine direkte Service/Gateway/Repository-Instanziierung in pages eingefuehrt."
},
{
"guard_id": "GR-CORE-003",
"status": "pass",
"evidence": "Auth pages nutzen weiterhin requestInput()-Vertrag; register nutzt now isMethod('POST') + requestInput body access ohne superglobal drift."
},
{
"guard_id": "GR-CORE-004",
"status": "pass",
"evidence": "QG-004 ApiResponse::readJsonBody check in pages/api/v1 bleibt 0."
},
{
"guard_id": "GR-CORE-005",
"status": "pass",
"evidence": "Keine Abschwaechung serverseitiger AuthZ-Pruefungen; Guard-Matrix zeigt keine offenen critical/high findings in AuthZ-relevanten Flows."
},
{
"guard_id": "GR-CORE-006",
"status": "pass",
"evidence": "Tenant-Scope-Invarianten unveraendert; SSO-Callback-Regression nun automatisiert mit Domain-Policy-Fehlerpfad abgesichert."
},
{
"guard_id": "GR-CORE-008",
"status": "pass",
"evidence": "Keine neuen direkten Superglobal-Zugriffe in pages/auth oder api pages eingefuehrt."
},
{
"guard_id": "GR-CORE-012",
"status": "pass",
"evidence": "Mutierende Erfolgsfaelle bleiben PRG-konform (register success -> redirect verify-email etc.)."
},
{
"guard_id": "GR-SEC-001",
"status": "pass",
"evidence": "Register-POST enforce't CSRF vor Payload-Verarbeitung; AuthRegisterSecurityContractTest bestaetigt die Reihenfolge."
},
{
"guard_id": "GR-SEC-002",
"status": "pass",
"evidence": "Keine neuen unredacted PII/secret logging paths eingefuehrt; neue Tests arbeiten mock-basiert ohne Log-Ausleitung."
},
{
"guard_id": "GR-SEC-003",
"status": "pass",
"evidence": "Keine SQL-String-Building-Aenderungen; QG-004 structural checks weiterhin 0 und neue Tests nutzen Repository-Interfaces."
},
{
"guard_id": "GR-SEC-004",
"status": "pass",
"evidence": "Keine externen CDN/remote assets in touched login templates/js/css eingefuehrt."
},
{
"guard_id": "GR-SEC-005",
"status": "pass",
"evidence": "Keine Kryptografie-Aenderungen in diesem Run; bestehende Crypto-Pfade unveraendert."
},
{
"guard_id": "GR-UI-009",
"status": "pass",
"evidence": "A11y bleibt intakt: AuthLoginAccessibilityContractTest gruen, Toggle keyboard-fokusfaehig und semantisch button-basiert."
},
{
"guard_id": "GR-UI-010",
"status": "pass",
"evidence": "Neue Toggle-Texte werden ueber t()-Keys bereitgestellt und in de/en gepflegt."
},
{
"guard_id": "GR-UI-011",
"status": "pass",
"evidence": "Login-JS konsolidiert: app-password-toggle und app-password-hints besitzen duplicate-binding guards (idempotente Initialisierung)."
},
{
"guard_id": "GR-UI-012",
"status": "pass",
"evidence": "Konkrete Login-CSS-Cleanup-Massnahme umgesetzt: ungenutzter Selektor .back_to_login entfernt; Contract testet Abwesenheit."
},
{
"guard_id": "GR-UI-013",
"status": "pass",
"evidence": "Interaktive Controls bleiben semantisch/keyboard-operabel; Toggle aktualisiert aria-labels korrekt (show/hide)."
},
{
"guard_id": "GR-UI-015",
"status": "pass",
"evidence": "i18n key completeness erfuellt: Show/Hide password in default_de.json und default_en.json vorhanden."
},
{
"guard_id": "GR-TEST-001",
"status": "pass",
"evidence": "Testabdeckung erweitert fuer Reset/Verify plus explizite SSO-Regression (MicrosoftOidcServiceTest callback domain policy)."
},
{
"guard_id": "GR-TEST-002",
"status": "pass",
"evidence": "Neue Tests bleiben DI/mock-freundlich ohne untestbare neue global side-effects."
},
{
"guard_id": "GR-LANG-002",
"status": "pass",
"evidence": "composer cs:check pass (0/518 files fixable)."
}
],
"commands": [
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Service/Auth/MicrosoftOidcServiceTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginFrontendCleanupContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Service/Auth/PasswordResetServiceTest.php tests/Service/Auth/EmailVerificationServiceTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/AuthLoginAccessibilityContractTest.php tests/Architecture/AuthRegisterSecurityContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"result": "pass"
},
{
"cmd": "docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php --no-progress",
"result": "pass"
},
{
"cmd": "(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",
"result": "pass"
},
{
"cmd": "docker compose exec php composer cs:check",
"result": "pass"
},
{
"cmd": "Manual browser smoke (QG-005) for /login, /password/forgot->verify->reset, /verify-email, /auth/microsoft/start+callback, devtools console",
"result": "pass"
}
],
"quality_gate_results": [
{
"gate_id": "QG-001",
"result": "pass",
"notes": "Exit code 0. PHPUnit full: 487 tests, 11030 assertions, Warnings 1, Deprecations 1 (non-blocking)."
},
{
"gate_id": "QG-002",
"result": "pass",
"notes": "Exit code 0. PHPStan: [OK] No errors."
},
{
"gate_id": "QG-003",
"result": "pass",
"notes": "Exit code 0. CoreStarterkitContractTest: 11 tests, 6167 assertions."
},
{
"gate_id": "QG-004",
"result": "pass",
"notes": "Alle strukturellen rg-check Zaehler sind 0 (8/8 checks gruen)."
},
{
"gate_id": "QG-005",
"result": "pass",
"notes": "Manueller Browser-Smoke zuvor erfolgreich bestaetigt (lokal+SSO+Recovery+Verify+Console)."
},
{
"gate_id": "QG-006",
"result": "pass",
"notes": "Exit code 0. composer cs:check: 0 von 518 Dateien fixbar."
}
],
"test_results": [
{
"name": "MicrosoftOidcServiceTest",
"result": "pass",
"notes": "15 tests, 61 assertions; inkl. neuer callback regression email_domain_not_allowed."
},
{
"name": "AuthLoginFrontendCleanupContractTest",
"result": "pass",
"notes": "3 tests, 15 assertions; prueft CSS cleanup + JS duplicate-binding guards."
},
{
"name": "PasswordResetServiceTest + EmailVerificationServiceTest",
"result": "pass",
"notes": "27 tests, 81 assertions."
},
{
"name": "AuthLoginAccessibilityContractTest + AuthRegisterSecurityContractTest",
"result": "pass",
"notes": "6 tests, 105 assertions."
},
{
"name": "CoreStarterkitContractTest",
"result": "pass",
"notes": "11 tests, 6167 assertions."
},
{
"name": "PHPUnit Full",
"result": "pass",
"notes": "487 tests, 11030 assertions, warnings/deprecations vorhanden aber kein failure."
},
{
"name": "PHPStan",
"result": "pass",
"notes": "No errors."
},
{
"name": "PHP CS Check",
"result": "pass",
"notes": "0/518 files fixable."
},
{
"name": "Manual Browser Smoke (QG-005)",
"result": "pass",
"notes": "Manueller Smoke erfolgreich bestaetigt."
}
],
"open_items": []
}

View File

@@ -1,10 +0,0 @@
{
"task_id": "LOGIN-AUTH-QUALITY-CHECK-001",
"ready_to_finalize": true,
"guard_review": "pass",
"acceptance_review": "pass",
"ci_status": "pass",
"final_action": "commit",
"commit_message": "feat(auth): finalize login auth quality check hardening and coverage",
"notes": "review-guards, review-acceptance und execution-report sind jeweils PASS; QG-001 bis QG-006 sind PASS."
}

View File

@@ -1,231 +0,0 @@
{
"task_id": "LOGIN-AUTH-QUALITY-CHECK-001",
"scope_ref": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/plan.json",
"flows": [
{
"flow": "local login",
"files": [
"pages/auth/login().php",
"pages/auth/login(login).phtml",
"web/js/app-login-init.js",
"web/js/components/app-password-toggle.js",
"web/js/components/app-password-hints.js",
"web/css/pages/app-login.css"
],
"guard_ids": [
"GR-CORE-003",
"GR-CORE-005",
"GR-CORE-006",
"GR-SEC-001",
"GR-UI-009",
"GR-UI-010",
"GR-UI-011",
"GR-UI-012",
"GR-UI-013",
"GR-UI-015"
],
"findings": [
{
"id": "F-LOGIN-001",
"severity": "medium",
"status": "resolved",
"summary": "Password toggle lacked duplicate-init guard and keyboard focus resilience evidence.",
"evidence": "web/js/components/app-password-toggle.js now has passwordToggleBound guard; tests/Architecture/AuthLoginFrontendCleanupContractTest.php validates guard presence."
},
{
"id": "F-LOGIN-002",
"severity": "low",
"status": "resolved",
"summary": "Unused legacy login CSS selector remained in app-login.css.",
"evidence": "Selector .back_to_login removed; tests/Architecture/AuthLoginFrontendCleanupContractTest.php asserts absence."
}
]
},
{
"flow": "register",
"files": [
"pages/auth/register().php",
"pages/auth/register(login).phtml"
],
"guard_ids": [
"GR-CORE-003",
"GR-CORE-012",
"GR-SEC-001",
"GR-UI-009",
"GR-UI-010"
],
"findings": [
{
"id": "F-REGISTER-001",
"severity": "high",
"status": "resolved",
"summary": "Register POST accepted payload without upfront CSRF verification.",
"evidence": "pages/auth/register().php now enforces Session::checkCsrfToken() before payload handling; tests/Architecture/AuthRegisterSecurityContractTest.php green."
}
]
},
{
"flow": "forgot/verify/reset",
"files": [
"pages/auth/forgot().php",
"pages/auth/verify().php",
"pages/auth/reset().php",
"lib/Service/Auth/PasswordResetService.php",
"pages/auth/forgot(login).phtml",
"pages/auth/verify(login).phtml",
"pages/auth/reset(login).phtml"
],
"guard_ids": [
"GR-SEC-001",
"GR-SEC-002",
"GR-TEST-001",
"GR-TEST-002",
"GR-UI-009"
],
"findings": [
{
"id": "F-RESET-001",
"severity": "medium",
"status": "resolved",
"summary": "Edge-case unit regression coverage for reset lifecycle was incomplete.",
"evidence": "tests/Service/Auth/PasswordResetServiceTest.php adds requestReset/verifyCode/resetPassword edge and failure paths."
}
]
},
{
"flow": "verify-email",
"files": [
"pages/auth/verify-email().php",
"pages/auth/verify-email(login).phtml",
"lib/Service/Auth/EmailVerificationService.php"
],
"guard_ids": [
"GR-SEC-001",
"GR-SEC-002",
"GR-TEST-001",
"GR-TEST-002",
"GR-UI-009"
],
"findings": [
{
"id": "F-VERIFY-001",
"severity": "medium",
"status": "resolved",
"summary": "Automated coverage for verify-email resend/attempt-limit branches was incomplete.",
"evidence": "tests/Service/Auth/EmailVerificationServiceTest.php covers send/verify/resend including already_verified and attempt limit."
}
]
},
{
"flow": "microsoft start",
"files": [
"pages/auth/microsoft/start().php",
"lib/Service/Auth/TenantSsoService.php",
"lib/Service/Auth/MicrosoftOidcService.php"
],
"guard_ids": [
"GR-CORE-005",
"GR-CORE-006",
"GR-SEC-002",
"GR-TEST-001",
"GR-TEST-002"
],
"findings": [
{
"id": "F-MSSO-START-001",
"severity": "none",
"status": "none",
"summary": "No open finding.",
"evidence": "tests/Service/Auth/MicrosoftOidcServiceTest.php already covered startAuthorization negative and PKCE happy paths."
}
]
},
{
"flow": "microsoft callback",
"files": [
"pages/auth/microsoft/callback().php",
"lib/Service/Auth/MicrosoftOidcService.php",
"lib/Service/Auth/SsoUserLinkService.php",
"lib/Service/Auth/TenantSsoService.php"
],
"guard_ids": [
"GR-CORE-005",
"GR-CORE-006",
"GR-SEC-002",
"GR-TEST-001",
"GR-TEST-002"
],
"findings": [
{
"id": "F-MSSO-CALLBACK-001",
"severity": "medium",
"status": "resolved",
"summary": "Missing explicit automated regression case for domain-policy rejection in callback.",
"evidence": "tests/Service/Auth/MicrosoftOidcServiceTest.php now includes testHandleCallbackReturnsEmailDomainNotAllowedWhenTenantDomainPolicyRejectsUser."
}
]
},
{
"flow": "api auth login",
"files": [
"pages/api/v1/auth/login().php"
],
"guard_ids": [
"GR-CORE-004",
"GR-CORE-005",
"GR-SEC-002",
"GR-SEC-003",
"GR-CORE-006"
],
"findings": [
{
"id": "F-API-LOGIN-001",
"severity": "none",
"status": "none",
"summary": "No open finding.",
"evidence": "QG-004 structural checks remain 0 violations; no direct readJsonBody/superglobal drift introduced."
}
]
},
{
"flow": "api me password",
"files": [
"pages/api/v1/me/password().php"
],
"guard_ids": [
"GR-CORE-005",
"GR-SEC-002",
"GR-SEC-003",
"GR-TEST-001"
],
"findings": [
{
"id": "F-API-ME-PASSWORD-001",
"severity": "none",
"status": "none",
"summary": "No open finding.",
"evidence": "No API behavior change in this run; quality gates and architecture contracts stay green."
}
]
}
],
"priority_summary": {
"critical": {
"open": 0,
"resolved": 0
},
"high": {
"open": 0,
"resolved": 1
},
"medium": {
"open": 0,
"resolved": 4
},
"low": {
"open": 0,
"resolved": 1
}
},
"notes": "Alle kritischen/hohen Findings sind geschlossen; keine offenen critical/high/medium Befunde im In-Scope-Auth-Bereich."
}

View File

@@ -1,249 +0,0 @@
{
"task_id": "LOGIN-AUTH-QUALITY-CHECK-001",
"summary": "Ganzheitlicher Quality-Check fuer Auth/Login/Password (lokal + Microsoft SSO) mit Guard- und Best-Practice-Review ueber PHP und Frontend, gezielten Korrekturen bei Verstoessen (inkl. unnoetigem CSS/JS), sowie Ergaenzung und Absicherung durch reproduzierbare Tests und Quality Gates.",
"assumptions": [
"Scope umfasst lokalen Login/Password-Flow und Microsoft-SSO-Start/Callback inklusive tenantabhaengiger Login-Methoden.",
"Umsetzungstiefe ist 'gezielte Fixes', kein breiter Architektur-Refactor.",
"Wenn API-Verhalten nicht geaendert wird, ist keine OpenAPI-Aenderung noetig.",
"Alle neu eingefuehrten sichtbaren Texte werden im selben Merge in de+en gepflegt."
],
"scope": {
"in": [
"pages/auth/login().php, register().php, forgot().php, verify().php, reset().php, verify-email().php",
"pages/auth/login(login).phtml, register(login).phtml, forgot(login).phtml, verify(login).phtml, reset(login).phtml, verify-email(login).phtml",
"pages/auth/microsoft/start().php und pages/auth/microsoft/callback().php",
"pages/api/v1/auth/login().php und pages/api/v1/me/password().php",
"lib/Service/Auth/AuthService.php, RememberMeService.php, PasswordResetService.php, EmailVerificationService.php, MicrosoftOidcService.php, TenantSsoService.php, SsoUserLinkService.php",
"templates/login.phtml und templates/partials/auth-help-links.phtml (plus ggf. auth-bezogene Partials)",
"web/js/app-login-init.js, web/js/components/app-password-toggle.js, web/js/components/app-password-hints.js",
"web/css/pages/app-login.css",
"tests/Service/Auth/*, tests/Http/ApiAuthTest.php, tests/Architecture/AuthLoginAccessibilityContractTest.php, tests/Architecture/AuthHelpLinksContractTest.php",
"i18n/default_de.json und i18n/default_en.json fuer neue Keys"
],
"out": [
"Neues Auth-Feature-Design oder Produktneuentwicklung",
"Breite Refactors ausserhalb Auth/Login/Password/SSO",
"DB-Schema-Migrationen",
"Nicht-authentifizierungsbezogene Admin-Module"
]
},
"guardrails": {
"required_guard_ids": [
"GR-CORE-001",
"GR-CORE-002",
"GR-CORE-003",
"GR-CORE-004",
"GR-CORE-005",
"GR-CORE-006",
"GR-CORE-008",
"GR-CORE-012",
"GR-SEC-001",
"GR-SEC-002",
"GR-SEC-003",
"GR-SEC-004",
"GR-SEC-005",
"GR-UI-009",
"GR-UI-010",
"GR-UI-011",
"GR-UI-012",
"GR-UI-013",
"GR-UI-015",
"GR-TEST-001",
"GR-TEST-002",
"GR-LANG-002"
],
"required_quality_gate_ids": [
"QG-001",
"QG-002",
"QG-003",
"QG-004",
"QG-005",
"QG-006"
]
},
"success_criteria": [
{
"id": "SC-001",
"criterion": "Eine vollstaendige Guard-Matrix fuer den Auth/Login/Password-Scope liegt vor, mit Datei-/Flow-Bezug und priorisierten Findings (kritisch/hoch/mittel)."
},
{
"id": "SC-002",
"criterion": "Alle kritischen und hohen Guard-Verstoesse im Scope sind durch gezielte Code-Anpassungen behoben oder mit begruendeter, nachvollziehbarer Ausnahme dokumentiert."
},
{
"id": "SC-003",
"criterion": "Frontend-Review entfernt oder konsolidiert unnoetige/duplizierte Login-CSS- und Login-JS-Logik, ohne A11y- oder UX-Regression."
},
{
"id": "SC-004",
"criterion": "Serverseitige Sicherheitsinvarianten bleiben fuer alle betroffenen Auth-Endpunkte intakt (AuthZ, Tenant-Scope, CSRF auf POST, keine sensiblen Leaks)."
},
{
"id": "SC-005",
"criterion": "Automatisierte Tests sind erweitert, sodass Login/Password-Edge-Cases und Regressionen (inkl. Reset/Verify sowie SSO-Pfade) reproduzierbar abgedeckt sind."
},
{
"id": "SC-006",
"criterion": "Alle Pflicht-Gates QG-001 bis QG-006 laufen mit PASS und nachvollziehbarer Evidenz."
},
{
"id": "SC-007",
"criterion": "Manuelle End-to-End-Smokes fuer lokale Login- und SSO-Kernfluesse sind erfolgreich, inklusive Browser-Console ohne neue JS-Fehler."
}
],
"implementation_steps": [
{
"id": "S1",
"title": "Guard-Matrix und Baseline erstellen",
"description": "Dokumentiere pro Auth/Login/Password-Flow die relevanten Guards aus Katalog und ordne sie konkreten Dateien/Codepfaden zu; erfasse bestehende Testabdeckung und initiale Gap-Liste.",
"guard_refs": [
"GR-CORE-001",
"GR-TEST-001"
]
},
{
"id": "S2",
"title": "PHP Page/API Boundary-Review durchfuehren",
"description": "Pruefe pages/auth/* und pages/api/v1/auth|me auf requestInput-Vertrag, Superglobal-Vermeidung, CSRF-Reihenfolge, API-Fehlerkonsistenz, PRG-Verhalten und serverseitige Sicherheitspruefungen.",
"guard_refs": [
"GR-CORE-003",
"GR-CORE-004",
"GR-CORE-008",
"GR-CORE-012",
"GR-SEC-001",
"GR-CORE-005",
"GR-CORE-006"
]
},
{
"id": "S3",
"title": "Service-Layer Security- und Testability-Review",
"description": "Pruefe AuthService/RememberMe/PasswordReset/EmailVerification/MicrosoftOIDC/TenantSSO auf Auth- und Tenant-Invarianten, PII-Redaktion, SQL-/Crypto-Nutzung sowie unit-testbare Konstruktion via Dependency Injection.",
"guard_refs": [
"GR-CORE-001",
"GR-SEC-002",
"GR-SEC-003",
"GR-SEC-005",
"GR-TEST-002"
]
},
{
"id": "S4",
"title": "Frontend/UI-UX Quality-Review mit CSS/JS-Cleanup",
"description": "Pruefe Login-Templates, Login-CSS und Login-JS auf A11y-Basics, i18n-Konformitaet, Semantik, UI-State-Vollstaendigkeit sowie JS/CSS-Reuse-first; entferne unnoetige/duplizierte Regeln oder Logik gezielt.",
"guard_refs": [
"GR-UI-009",
"GR-UI-010",
"GR-UI-011",
"GR-UI-012",
"GR-UI-013",
"GR-UI-015",
"GR-SEC-004"
]
},
{
"id": "S5",
"title": "Gezielte Korrekturen umsetzen",
"description": "Setze nur die priorisierten High-Impact-Fixes um (PHP + Frontend), ohne Scope-Ausweitung; bei UI-Textaenderungen i18n-Keys in de/en im selben Schritt nachziehen.",
"guard_refs": [
"GR-TEST-002",
"GR-UI-010",
"GR-UI-015",
"GR-LANG-002"
]
},
{
"id": "S6",
"title": "Testabdeckung gezielt erweitern",
"description": "Ergaenze/erweitere PHPUnit-Tests fuer unterabgedeckte Auth/Login/Password-Pfade (v. a. PasswordResetService, EmailVerificationService, Login/SSO-Edge-Cases) sowie passende Architektur-Contracts fuer Markup-/A11y-Invarianten.",
"guard_refs": [
"GR-TEST-001",
"GR-TEST-002",
"GR-UI-009",
"GR-SEC-001"
]
},
{
"id": "S7",
"title": "Quality Gates und Smokes ausfuehren",
"description": "Fuehre QG-001 bis QG-006 aus, inklusive struktureller rg-Checks und JS-Smoke; dokumentiere pro Gate PASS/FAIL/Blocker mit kurzer Evidenz.",
"guard_refs": [
"GR-LANG-002",
"GR-TEST-001",
"GR-UI-011",
"GR-UI-012"
]
},
{
"id": "S8",
"title": "Acceptance gegen SC-IDs abschliessen",
"description": "Mappe jede Success-Criterion-ID auf konkrete Evidenz (Dateien, Tests, Gate-Resultate, Smoke-Ergebnisse) und markiere verbleibende Restrisiken transparent.",
"guard_refs": [
"GR-TEST-001",
"GR-CORE-005"
]
}
],
"tests": [
"Bestehende Tests: tests/Service/Auth/AuthServiceTest.php und tests/Service/Auth/RememberMeServiceTest.php gezielt erweitern (fehlende Login-/Tenant-/SSO-Edge-Cases).",
"Neue Tests: tests/Service/Auth/PasswordResetServiceTest.php (requestReset, verifyCode, resetPassword inkl. expiry/attempts/used).",
"Neue Tests: tests/Service/Auth/EmailVerificationServiceTest.php (sendVerification, verifyCode, resendVerification inkl. already_verified/attempt limits).",
"Bestehende Architekturtests: tests/Architecture/AuthLoginAccessibilityContractTest.php und tests/Architecture/AuthHelpLinksContractTest.php bei Bedarf erweitern.",
"API-bezogene Regressionen: gezielte Ergaenzung in tests/Http/ApiAuthTest.php oder neuer fokussierter Auth-API-Test fuer Login-/Password-Randfaelle.",
"QG-001: docker compose exec php vendor/bin/phpunit",
"QG-002: docker compose exec php vendor/bin/phpstan analyse -c phpstan.neon --no-progress",
"QG-003: docker compose exec php vendor/bin/phpunit tests/Architecture/CoreStarterkitContractTest.php",
"QG-004: strukturelle rg-Checks gemaess agent-system/checks/quality-gates.json (Erwartung: 0 Verstosse)",
"QG-005: Browser-Smoke fuer Login, Forgot/Verify/Reset, Microsoft Start/Callback; DevTools Console ohne neue Errors",
"QG-006: docker compose exec php composer cs:check"
],
"acceptance_checks": [
"SC-001: Guard-Matrix enthaelt jeden In-Scope-Flow und referenziert konkrete Dateien plus Priorisierung der Findings.",
"SC-002: Kein offener kritischer/hoher Guard-Verstoss im Scope ohne dokumentierte und akzeptierte Ausnahme.",
"SC-003: Login-Frontend hat keine neu eingefuehrte A11y-Regressionsspur und keine unnoetige duplizierte CSS/JS-Logik in den geprueften Bereichen.",
"SC-004: AuthZ-, Tenant- und CSRF-Invarianten sind fuer alle betroffenen POST- und Login-Endpunkte nachweisbar intakt.",
"SC-005: Neue/erweiterte Tests schlagen bei kontrollierter Regelverletzung fehl und laufen im Zielzustand stabil gruen.",
"SC-006: QG-001 bis QG-006 sind mit PASS protokolliert.",
"SC-007: Manueller Smoke bestaetigt erfolgreiche Kernfluesse (lokal + SSO + Logout) ohne neue Browser-Console-Fehler."
],
"ux_notes": {
"affected_patterns": [
"Auth login card/layout (templates/login.phtml + pages/auth/*(login).phtml)",
"Tenant selection pattern (login-tenant-fieldset, login-tenant-choice-grid)",
"Auth help links partial (templates/partials/auth-help-links.phtml)",
"Password hint/toggle behavior (web/js/components/app-password-hints.js, app-password-toggle.js)",
"Login page styling (web/css/pages/app-login.css)"
],
"ui_states_required": [
"loading",
"empty",
"error",
"success"
],
"a11y_touchpoints": [
"Email/password/code inputs inklusive aria-invalid und aria-describedby Verknuepfungen",
"Fehler-Notices als assertive live regions mit fokussierbarer Rueckmeldung",
"Tenant-Radioauswahl und Tastatur-Fokuspfad",
"Microsoft Login CTA und lokale Login-Buttons mit semantischer Bedienbarkeit",
"Password toggle control (button semantics, focus-visible, aria-label updates)"
]
},
"risks": [
{
"risk": "Gezielte CSS-Bereinigung kann visuelle Login-Regressionen erzeugen, obwohl funktional korrekt.",
"mitigation": "Nur additive/minimale CSS-Deltas, visuelle Smokes pro Auth-Stage und QG-005 verpflichtend."
},
{
"risk": "Aenderungen an tenantabhaengiger Login-Methodik koennen lokale oder Microsoft-Login-Pfade unbeabsichtigt blockieren.",
"mitigation": "SSO- und Local-Branches jeweils mit positiven und negativen Tests absichern; End-to-End-Smoke fuer Start/Callback/Login ausfuehren."
},
{
"risk": "Security-Fixes koennen durch Nebenwirkungen API- oder Session-Verhalten brechen.",
"mitigation": "Invarianten zuerst testen (CSRF/AuthZ/Tenant), danach nur kleine isolierte Codeaenderungen mit Ruecktests."
},
{
"risk": "Neue Tests werden flakey wegen Session/Global-State-Kopplung.",
"mitigation": "Konsequent Mocking/DI nutzen, globale Abhaengigkeiten kapseln und deterministische Assertions ohne Zeitrennen definieren."
}
]
}

View File

@@ -1,57 +0,0 @@
{
"task_id": "LOGIN-AUTH-QUALITY-CHECK-001",
"verdict": "pass",
"checked_criterion_ids": [
"SC-001",
"SC-002",
"SC-003",
"SC-004",
"SC-005",
"SC-006",
"SC-007"
],
"checks": [
{
"criterion_id": "SC-001",
"criterion": "Eine vollstaendige Guard-Matrix fuer den Auth/Login/Password-Scope liegt vor, mit Datei-/Flow-Bezug und priorisierten Findings (kritisch/hoch/mittel).",
"result": "pass",
"evidence": "agent-system/runs/LOGIN-AUTH-QUALITY-CHECK-001/guard-matrix.json dokumentiert alle In-Scope-Flows mit Datei-/Guard-Zuordnung; priority_summary weist open=0 fuer critical/high/medium aus (high resolved=1, medium resolved=4)."
},
{
"criterion_id": "SC-002",
"criterion": "Alle kritischen und hohen Guard-Verstoesse im Scope sind durch gezielte Code-Anpassungen behoben oder mit begruendeter, nachvollziehbarer Ausnahme dokumentiert.",
"result": "pass",
"evidence": "guard-matrix.json zeigt keine offenen critical/high Findings; der High-Fund F-REGISTER-001 ist als resolved dokumentiert und in pages/auth/register().php umgesetzt (CSRF vor Payload), abgesichert durch tests/Architecture/AuthRegisterSecurityContractTest.php (lokal PASS)."
},
{
"criterion_id": "SC-003",
"criterion": "Frontend-Review entfernt oder konsolidiert unnoetige/duplizierte Login-CSS- und Login-JS-Logik, ohne A11y- oder UX-Regression.",
"result": "pass",
"evidence": "Konkrete Cleanup-Evidence vorhanden: web/css/pages/app-login.css ohne .back_to_login sowie idempotente Duplicate-Binding-Guards in web/js/components/app-password-toggle.js und web/js/components/app-password-hints.js; belegt durch tests/Architecture/AuthLoginFrontendCleanupContractTest.php (lokal PASS, 3 Tests/15 Assertions) und A11y-Contract tests/Architecture/AuthLoginAccessibilityContractTest.php (lokal PASS)."
},
{
"criterion_id": "SC-004",
"criterion": "Serverseitige Sicherheitsinvarianten bleiben fuer alle betroffenen Auth-Endpunkte intakt (AuthZ, Tenant-Scope, CSRF auf POST, keine sensiblen Leaks).",
"result": "pass",
"evidence": "CSRF-Invariante fuer Register ist codeseitig durchgesetzt und per Contract-Test bestaetigt; QG-004 Structural Checks lokal erneut 8x0 (u.a. keine verbotenen direkten Instanziierungen, keine ApiResponse::readJsonBody-Nutzung in pages/api/v1). execution-report.json guard_evidence markiert GR-CORE-005/006 und GR-SEC-002 als pass ohne offene Punkte."
},
{
"criterion_id": "SC-005",
"criterion": "Automatisierte Tests sind erweitert, sodass Login/Password-Edge-Cases und Regressionen (inkl. Reset/Verify sowie SSO-Pfade) reproduzierbar abgedeckt sind.",
"result": "pass",
"evidence": "Reset/Verify-Abdeckung: tests/Service/Auth/PasswordResetServiceTest.php und tests/Service/Auth/EmailVerificationServiceTest.php (lokal PASS, zusammen 27 Tests/81 Assertions). SSO-Abdeckung: tests/Service/Auth/MicrosoftOidcServiceTest.php enthaelt Callback-Domain-Policy-Regression (lokal PASS, 15 Tests/61 Assertions)."
},
{
"criterion_id": "SC-006",
"criterion": "Alle Pflicht-Gates QG-001 bis QG-006 laufen mit PASS und nachvollziehbarer Evidenz.",
"result": "pass",
"evidence": "execution-report.json weist QG-001..QG-006 als pass aus; lokal verifiziert wurden QG-004 (8 strukturelle Checks alle 0) und QG-006 (composer cs:check PASS, 0/518 fixable)."
},
{
"criterion_id": "SC-007",
"criterion": "Manuelle End-to-End-Smokes fuer lokale Login- und SSO-Kernfluesse sind erfolgreich, inklusive Browser-Console ohne neue JS-Fehler.",
"result": "pass",
"evidence": "execution-report.json dokumentiert QG-005 als pass fuer lokale Login- und SSO-Kernpfade inkl. Console-Check; keine widersprechenden Open Items vorhanden."
}
]
}

Some files were not shown because too many files have changed in this diff Show More