readProjectFile($file), true); $this->assertIsArray($decoded, 'Invalid JSON: ' . $file); } } public function testGuardCatalogIdsAreUniqueAndWellFormed(): void { $catalog = $this->decodeJson('agent-system/checks/guard-catalog.json'); $guards = is_array($catalog['guards'] ?? null) ? $catalog['guards'] : []; $this->assertNotSame([], $guards, 'Guard catalog must not be empty.'); $ids = []; foreach ($guards as $guard) { $id = (string) ($guard['id'] ?? ''); $source = (string) ($guard['source'] ?? ''); $this->assertMatchesRegularExpression('/^GR-[A-Z]+-[0-9]{3}$/', $id, 'Invalid guard id: ' . $id); $this->assertNotSame('', $source, 'Missing guard source for ' . $id); $sourcePath = explode('#', $source)[0]; $this->readProjectFile($sourcePath); $ids[] = $id; } $this->assertSame($ids, array_values(array_unique($ids)), 'Duplicate guard IDs found.'); } public function testQualityGateIdsAreUniqueAndWellFormed(): void { $gates = $this->decodeJson('agent-system/checks/quality-gates.json'); $items = is_array($gates['gates'] ?? null) ? $gates['gates'] : []; $this->assertNotSame([], $items, 'Quality gates must not be empty.'); $ids = []; foreach ($items as $gate) { $id = (string) ($gate['id'] ?? ''); $command = (string) ($gate['command'] ?? ''); $this->assertMatchesRegularExpression('/^QG-[0-9]{3}$/', $id, 'Invalid quality gate id: ' . $id); $this->assertNotSame('', trim($command), 'Missing command for ' . $id); $ids[] = $id; } $this->assertSame($ids, array_values(array_unique($ids)), 'Duplicate quality gate IDs found.'); } public function testTemplatesReferenceKnownGuardAndGateIds(): void { $catalog = $this->decodeJson('agent-system/checks/guard-catalog.json'); $gates = $this->decodeJson('agent-system/checks/quality-gates.json'); $guardIds = array_map( static fn (array $guard): string => (string) ($guard['id'] ?? ''), is_array($catalog['guards'] ?? null) ? $catalog['guards'] : [] ); $gateIds = array_map( static fn (array $gate): string => (string) ($gate['id'] ?? ''), is_array($gates['gates'] ?? null) ? $gates['gates'] : [] ); $planTemplate = $this->decodeJson('agent-system/templates/plan.template.json'); $planGuardIds = is_array($planTemplate['guardrails']['required_guard_ids'] ?? null) ? $planTemplate['guardrails']['required_guard_ids'] : []; $planGateIds = is_array($planTemplate['guardrails']['required_quality_gate_ids'] ?? null) ? $planTemplate['guardrails']['required_quality_gate_ids'] : []; foreach ($planGuardIds as $id) { $this->assertContains($id, $guardIds, 'Unknown guard id in plan template: ' . (string) $id); } foreach ($planGateIds as $id) { $this->assertContains($id, $gateIds, 'Unknown gate id in plan template: ' . (string) $id); } $reviewGuardsTemplate = $this->decodeJson('agent-system/templates/review-guards.template.json'); $reviewGuardIds = is_array($reviewGuardsTemplate['checked_guard_ids'] ?? null) ? $reviewGuardsTemplate['checked_guard_ids'] : []; $reviewGateIds = is_array($reviewGuardsTemplate['checked_quality_gate_ids'] ?? null) ? $reviewGuardsTemplate['checked_quality_gate_ids'] : []; foreach ($reviewGuardIds as $id) { $this->assertContains($id, $guardIds, 'Unknown guard id in review template: ' . (string) $id); } foreach ($reviewGateIds as $id) { $this->assertContains($id, $gateIds, 'Unknown gate id in review template: ' . (string) $id); } } public function testRolePromptsReferenceGuardCatalogAndQualityGates(): void { $plannerPrompt = $this->readProjectFile('agent-system/prompts/planner.md'); $executorPrompt = $this->readProjectFile('agent-system/prompts/executor.md'); $reviewerPrompt = $this->readProjectFile('agent-system/prompts/reviewer-guards.md'); $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); } /** * @return array */ private function decodeJson(string $path): array { $decoded = json_decode($this->readProjectFile($path), true); $this->assertIsArray($decoded, 'Invalid JSON in ' . $path); return $decoded; } }