Erweiterung Contracts und Guards
This commit is contained in:
@@ -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`)
|
||||
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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`
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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
36
bin/docs-link-check.sh
Executable 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)"
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Reference in New Issue
Block a user