forked from fa/breadcrumb-the-shire
144 lines
6.6 KiB
PHP
144 lines
6.6 KiB
PHP
<?php
|
|
|
|
namespace MintyPHP\Tests\Architecture;
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
|
|
class AgentSystemContractTest extends TestCase
|
|
{
|
|
use ProjectFileAssertionSupport;
|
|
|
|
public function testAgentSystemJsonFilesAreValidJson(): void
|
|
{
|
|
$jsonFiles = [
|
|
'agent-system/checks/guard-catalog.json',
|
|
'agent-system/checks/quality-gates.json',
|
|
'agent-system/contracts/planner.schema.json',
|
|
'agent-system/contracts/executor.schema.json',
|
|
'agent-system/contracts/reviewer-guards.schema.json',
|
|
'agent-system/contracts/reviewer-acceptance.schema.json',
|
|
'agent-system/contracts/finalizer.schema.json',
|
|
'agent-system/templates/plan.template.json',
|
|
'agent-system/templates/execution-report.template.json',
|
|
'agent-system/templates/review-guards.template.json',
|
|
'agent-system/templates/review-acceptance.template.json',
|
|
'agent-system/templates/finalize.template.json',
|
|
];
|
|
|
|
foreach ($jsonFiles as $file) {
|
|
$decoded = json_decode($this->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<string,mixed>
|
|
*/
|
|
private function decodeJson(string $path): array
|
|
{
|
|
$decoded = json_decode($this->readProjectFile($path), true);
|
|
$this->assertIsArray($decoded, 'Invalid JSON in ' . $path);
|
|
return $decoded;
|
|
}
|
|
}
|