1
0

Erweiterung Contracts und Guards

This commit is contained in:
2026-03-09 19:56:08 +01:00
parent 01c05d997f
commit 7c75df51bb
11 changed files with 236 additions and 14 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

36
bin/docs-link-check.sh Executable file
View File

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

View File

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

View File

@@ -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);
}
/**

View File

@@ -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('/(?<![A-Za-z0-9_])\\/?docs\\/[a-z0-9_\\/-]+\\.md\\b/', $content, $matches);
foreach (array_unique($matches[0]) as $link) {
@@ -93,6 +88,62 @@ class CodexSkillsContractTest extends TestCase
}
}
public function testDocsIndexCoversAllMarkdownDocsWithoutOrphansOrDuplicates(): void
{
$index = $this->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<string>
*/
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;
}
}

View File

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