diff --git a/agent-system/checks/docs-skills-audit-policy.md b/agent-system/checks/docs-skills-audit-policy.md index 166738f..7e1de35 100644 --- a/agent-system/checks/docs-skills-audit-policy.md +++ b/agent-system/checks/docs-skills-audit-policy.md @@ -18,6 +18,7 @@ Einheitliche Einstufung und Ablage von Findings fuer Docs-/Skills-Audits. - `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`) diff --git a/agent-system/checks/quality-gates.json b/agent-system/checks/quality-gates.json index cc984e8..cfc8f79 100644 --- a/agent-system/checks/quality-gates.json +++ b/agent-system/checks/quality-gates.json @@ -44,6 +44,19 @@ "name": "Unused Composer packages", "command": "docker compose exec php vendor/bin/composer-unused", "note": "Run periodically (not per-merge). Flag any newly unused package to the reviewer." + }, + { + "id": "QG-008", + "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." + }, + { + "id": "QG-009", + "type": "fast", + "name": "Codex skills sync", + "command": "bin/codex-skills-sync.sh --check" } ] } diff --git a/agent-system/contracts/reviewer-guards.schema.json b/agent-system/contracts/reviewer-guards.schema.json index 2d8abfc..593bbf4 100644 --- a/agent-system/contracts/reviewer-guards.schema.json +++ b/agent-system/contracts/reviewer-guards.schema.json @@ -5,6 +5,7 @@ "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", @@ -30,17 +31,58 @@ "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" } + "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 } diff --git a/agent-system/prompts/executor.md b/agent-system/prompts/executor.md index f459306..2090a69 100644 --- a/agent-system/prompts/executor.md +++ b/agent-system/prompts/executor.md @@ -23,3 +23,4 @@ Rules: - 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` diff --git a/agent-system/prompts/planner.md b/agent-system/prompts/planner.md index 9750ea3..61c76fe 100644 --- a/agent-system/prompts/planner.md +++ b/agent-system/prompts/planner.md @@ -20,6 +20,7 @@ Rules: - 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") diff --git a/agent-system/prompts/reviewer-guards.md b/agent-system/prompts/reviewer-guards.md index 99c359b..de72e11 100644 --- a/agent-system/prompts/reviewer-guards.md +++ b/agent-system/prompts/reviewer-guards.md @@ -10,6 +10,7 @@ Required references: - `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` @@ -18,3 +19,7 @@ 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`) diff --git a/bin/docs-link-check.sh b/bin/docs-link-check.sh new file mode 100755 index 0000000..904ab68 --- /dev/null +++ b/bin/docs-link-check.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +repo_root="$(cd -- "${script_dir}/.." && pwd)" +cd "${repo_root}" + +tmp_file="$(mktemp)" +trap 'rm -f "${tmp_file}"' EXIT + +rg -n -o '(?:/docs|docs)/[a-z0-9_/-]+\.md' \ + README.md docs tools/codex-skills agent-system \ + -g '*.md' \ + -g '!agent-system/runs/**' \ + > "${tmp_file}" || true + +missing=0 +while IFS= read -r line; do + file="${line%%:*}" + rest="${line#*:}" + line_no="${rest%%:*}" + ref="${rest#*:}" + normalized="${ref#/}" + + if [[ ! -f "${normalized}" ]]; then + printf '%s:%s -> %s\n' "${file}" "${line_no}" "${ref}" + missing=1 + fi +done < "${tmp_file}" + +if [[ ${missing} -ne 0 ]]; then + echo "[FAIL] broken docs links found" + exit 1 +fi + +echo "[OK] docs links resolve (agent-system/runs/** excluded by default)" diff --git a/docs/reference-entwickler-checkliste.md b/docs/reference-entwickler-checkliste.md index b5a3044..c6be529 100644 --- a/docs/reference-entwickler-checkliste.md +++ b/docs/reference-entwickler-checkliste.md @@ -1,6 +1,6 @@ # Entwickler-Checkliste -Letzte Aktualisierung: 2026-03-06 +Letzte Aktualisierung: 2026-03-09 ## Ziel @@ -65,6 +65,9 @@ Kurze Definition of Done vor Merge. - [ ] `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` +- [ ] bei Docs-/Skills-Aenderungen: `docker compose exec php vendor/bin/phpunit tests/Architecture/CodexSkillsContractTest.php` +- [ ] bei Docs-/Skills-Aenderungen: `bin/docs-link-check.sh` (scannt standardmaessig ohne `agent-system/runs/**`) +- [ ] bei Docs-/Skills-Aenderungen: `bin/codex-skills-sync.sh --check` - [ ] bei JS-Änderungen: Browser-Smoke mit DevTools-Console (keine JS-Errors) - [ ] bei API-Änderungen: `docs/openapi.yaml` aktualisiert - [ ] Struktur-Gates geprüft (`rg` für Instanziierungsregeln) diff --git a/tests/Architecture/AgentSystemContractTest.php b/tests/Architecture/AgentSystemContractTest.php index a149558..66aa3e4 100644 --- a/tests/Architecture/AgentSystemContractTest.php +++ b/tests/Architecture/AgentSystemContractTest.php @@ -119,10 +119,16 @@ class AgentSystemContractTest extends TestCase $this->assertStringContainsString('agent-system/checks/guard-catalog.json', $plannerPrompt); $this->assertStringContainsString('agent-system/checks/quality-gates.json', $plannerPrompt); + $this->assertStringContainsString('DOCS-SKILLS-AUDIT-xxx', $plannerPrompt); + $this->assertStringContainsString('QG-008', $plannerPrompt); + $this->assertStringContainsString('QG-009', $plannerPrompt); $this->assertStringContainsString('agent-system/checks/guard-catalog.json', $executorPrompt); $this->assertStringContainsString('agent-system/checks/quality-gates.json', $executorPrompt); + $this->assertStringContainsString('QG-008', $executorPrompt); + $this->assertStringContainsString('QG-009', $executorPrompt); $this->assertStringContainsString('agent-system/checks/guard-catalog.json', $reviewerPrompt); $this->assertStringContainsString('agent-system/checks/quality-gates.json', $reviewerPrompt); + $this->assertStringContainsString('agent-system/checks/docs-skills-audit-policy.md', $reviewerPrompt); } /** diff --git a/tests/Architecture/CodexSkillsContractTest.php b/tests/Architecture/CodexSkillsContractTest.php index 49426f4..b577c66 100644 --- a/tests/Architecture/CodexSkillsContractTest.php +++ b/tests/Architecture/CodexSkillsContractTest.php @@ -69,20 +69,15 @@ class CodexSkillsContractTest extends TestCase $this->assertStringContainsString('"starterkit-php-style-ci"', $script); } - public function testDocumentationLinksResolveInReadmeDocsAndSourceMap(): void + public function testDocsLinkCheckScriptExcludesHistoricalRunArtifactsByDefault(): void { - $files = [ - 'README.md', - 'tools/codex-skills/core-guardrails/references/source-map.md', - ]; + $script = $this->readProjectFile('bin/docs-link-check.sh'); + $this->assertStringContainsString("!agent-system/runs/**", $script); + } - $docFiles = glob($this->projectRootPath() . '/docs/*.md'); - $this->assertNotFalse($docFiles, 'Failed to enumerate docs/*.md files.'); - foreach ($docFiles as $fullPath) { - $files[] = 'docs/' . basename($fullPath); - } - - foreach ($files as $file) { + public function testDocumentationLinksResolveAcrossDocsSkillsAndAgentGuides(): void + { + foreach ($this->filesCoveredByDocsLinkIntegrityCheck() as $file) { $content = $this->readProjectFile($file); preg_match_all('/(?readProjectFile('docs/index.md'); + preg_match_all('/\\/docs\\/([a-z0-9-]+\\.md)\\b/', $index, $matches); + $references = $matches[1]; + + $docFiles = glob($this->projectRootPath() . '/docs/*.md'); + $this->assertNotFalse($docFiles, 'Failed to enumerate docs/*.md files.'); + + $expectedDocs = []; + foreach ($docFiles as $fullPath) { + $basename = basename($fullPath); + if ($basename !== 'index.md') { + $expectedDocs[] = $basename; + } + } + + $refCounts = array_count_values($references); + $duplicates = []; + foreach ($refCounts as $ref => $count) { + if ($count > 1) { + $duplicates[] = $ref; + } + } + + $uniqueReferences = array_values(array_unique($references)); + sort($expectedDocs); + sort($uniqueReferences); + sort($duplicates); + + $orphans = array_values(array_diff($expectedDocs, $uniqueReferences)); + $missingTargets = array_values(array_diff($uniqueReferences, $expectedDocs)); + + $this->assertSame([], $duplicates, 'Duplicate docs/index.md references: ' . implode(', ', $duplicates)); + $this->assertSame([], $orphans, 'Docs markdown files missing in docs/index.md: ' . implode(', ', $orphans)); + $this->assertSame([], $missingTargets, 'docs/index.md references missing files: ' . implode(', ', $missingTargets)); + } + + public function testQualityGatesIncludeDocsLinksAndSkillsSyncChecks(): void + { + $qualityGates = json_decode($this->readProjectFile('agent-system/checks/quality-gates.json'), true); + $this->assertIsArray($qualityGates, 'Invalid quality-gates.json'); + $gates = is_array($qualityGates['gates'] ?? null) ? $qualityGates['gates'] : []; + + $byId = []; + foreach ($gates as $gate) { + $id = (string) ($gate['id'] ?? ''); + $byId[$id] = $gate; + } + + $this->assertArrayHasKey('QG-008', $byId); + $this->assertArrayHasKey('QG-009', $byId); + $this->assertSame('bin/docs-link-check.sh', (string) ($byId['QG-008']['command'] ?? '')); + $this->assertSame('bin/codex-skills-sync.sh --check', (string) ($byId['QG-009']['command'] ?? '')); + } + public function testDocsSkillsAuditPolicyExists(): void { $policy = $this->readProjectFile('agent-system/checks/docs-skills-audit-policy.md'); @@ -100,4 +151,59 @@ class CodexSkillsContractTest extends TestCase $this->assertStringContainsString('P1', $policy); $this->assertStringContainsString('DOCS-SKILLS-AUDIT-00x', $policy); } + + public function testReviewerGuardsFlowEnforcesDocsSkillsAuditPolicy(): void + { + $prompt = $this->readProjectFile('agent-system/prompts/reviewer-guards.md'); + $schema = $this->readProjectFile('agent-system/contracts/reviewer-guards.schema.json'); + + $this->assertStringContainsString('agent-system/checks/docs-skills-audit-policy.md', $prompt); + $this->assertStringContainsString('DOCS-SKILLS-AUDIT', $prompt); + + $this->assertStringContainsString('audit_policy_ref', $schema); + $this->assertStringContainsString('"P0"', $schema); + $this->assertStringContainsString('"P1"', $schema); + $this->assertStringContainsString('run_artifact_id', $schema); + $this->assertStringContainsString('DOCS-SKILLS-AUDIT-[0-9]{3}', $schema); + } + + /** + * @return list + */ + private function filesCoveredByDocsLinkIntegrityCheck(): array + { + $root = $this->projectRootPath(); + $files = ['README.md']; + $scanDirs = ['docs', 'tools/codex-skills', 'agent-system']; + + foreach ($scanDirs as $scanDir) { + $directory = new \RecursiveDirectoryIterator( + $root . '/' . $scanDir, + \FilesystemIterator::SKIP_DOTS + ); + $iterator = new \RecursiveIteratorIterator($directory); + + foreach ($iterator as $fileInfo) { + if (!$fileInfo->isFile()) { + continue; + } + + $fullPath = str_replace('\\', '/', $fileInfo->getPathname()); + if (!str_ends_with($fullPath, '.md')) { + continue; + } + + $relative = ltrim(str_replace(str_replace('\\', '/', $root), '', $fullPath), '/'); + if (str_starts_with($relative, 'agent-system/runs/')) { + continue; + } + + $files[] = $relative; + } + } + + $files = array_values(array_unique($files)); + sort($files); + return $files; + } } diff --git a/tools/codex-skills/core-guardrails/references/quality-gates.md b/tools/codex-skills/core-guardrails/references/quality-gates.md index b0a1675..f31120e 100644 --- a/tools/codex-skills/core-guardrails/references/quality-gates.md +++ b/tools/codex-skills/core-guardrails/references/quality-gates.md @@ -26,6 +26,14 @@ Erwartung: jeweils `0`. - 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/**`, `tools/codex-skills/**` und `agent-system/**` + - `agent-system/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`.