Erweiterung Contracts und Guards
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user