Split meta architecture contracts
This commit is contained in:
@@ -74,8 +74,12 @@
|
|||||||
</testsuite>
|
</testsuite>
|
||||||
<testsuite name="ArchitectureMeta">
|
<testsuite name="ArchitectureMeta">
|
||||||
<file>tests/Architecture/ApiSchemaContractTest.php</file>
|
<file>tests/Architecture/ApiSchemaContractTest.php</file>
|
||||||
<file>tests/Architecture/AgentSystemContractTest.php</file>
|
<file>tests/Architecture/AgentSystemCatalogContractTest.php</file>
|
||||||
<file>tests/Architecture/CodexSkillsContractTest.php</file>
|
<file>tests/Architecture/AgentSystemFileContractTest.php</file>
|
||||||
|
<file>tests/Architecture/AgentSystemReferenceContractTest.php</file>
|
||||||
|
<file>tests/Architecture/CodexAutomationContractTest.php</file>
|
||||||
|
<file>tests/Architecture/CodexDocsIntegrityContractTest.php</file>
|
||||||
|
<file>tests/Architecture/CodexSkillReferenceContractTest.php</file>
|
||||||
</testsuite>
|
</testsuite>
|
||||||
</testsuites>
|
</testsuites>
|
||||||
</phpunit>
|
</phpunit>
|
||||||
|
|||||||
64
tests/Architecture/AgentSystemCatalogContractTest.php
Normal file
64
tests/Architecture/AgentSystemCatalogContractTest.php
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AgentSystemCatalogContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use AgentSystemContractSupport;
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testGuardCatalogIdsAreUniqueAndWellFormed(): void
|
||||||
|
{
|
||||||
|
$catalog = $this->decodeAgentJson('.agents/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'] ?? '');
|
||||||
|
$reviewer = (string) ($guard['reviewer'] ?? '');
|
||||||
|
$this->assertMatchesRegularExpression('/^GR-(CORE|TEST|UI|SEC)-[A-Z0-9]{3,}$/', $id, 'Invalid guard id: ' . $id);
|
||||||
|
$this->assertContains($reviewer, ['code', 'security'], 'Invalid reviewer for ' . $id);
|
||||||
|
$ids[] = $id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->assertSame($ids, array_values(array_unique($ids)), 'Duplicate guard IDs found.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testGuardCatalogReviewerAssignment(): void
|
||||||
|
{
|
||||||
|
$catalog = $this->decodeAgentJson('.agents/checks/guard-catalog.json');
|
||||||
|
$guards = is_array($catalog['guards'] ?? null) ? $catalog['guards'] : [];
|
||||||
|
|
||||||
|
foreach ($guards as $guard) {
|
||||||
|
$id = (string) ($guard['id'] ?? '');
|
||||||
|
$reviewer = (string) ($guard['reviewer'] ?? '');
|
||||||
|
|
||||||
|
if (str_starts_with($id, 'GR-SEC-')) {
|
||||||
|
$this->assertSame('security', $reviewer, $id . ' must be assigned to security reviewer');
|
||||||
|
} else {
|
||||||
|
$this->assertSame('code', $reviewer, $id . ' must be assigned to code reviewer');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQualityGateIdsAreUniqueAndWellFormed(): void
|
||||||
|
{
|
||||||
|
$gates = $this->decodeAgentJson('.agents/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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
16
tests/Architecture/AgentSystemContractSupport.php
Normal file
16
tests/Architecture/AgentSystemContractSupport.php
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
trait AgentSystemContractSupport
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string,mixed>
|
||||||
|
*/
|
||||||
|
private function decodeAgentJson(string $path): array
|
||||||
|
{
|
||||||
|
$decoded = json_decode($this->readProjectFile($path), true);
|
||||||
|
$this->assertIsArray($decoded, 'Invalid JSON in ' . $path);
|
||||||
|
return $decoded;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,198 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MintyPHP\Tests\Architecture;
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class AgentSystemContractTest extends TestCase
|
|
||||||
{
|
|
||||||
use ProjectFileAssertionSupport;
|
|
||||||
|
|
||||||
public function testAgentSystemJsonFilesAreValidJson(): void
|
|
||||||
{
|
|
||||||
$jsonFiles = [
|
|
||||||
'.agents/checks/guard-catalog.json',
|
|
||||||
'.agents/checks/quality-gates.json',
|
|
||||||
'.agents/contracts/analyst.schema.json',
|
|
||||||
'.agents/contracts/planner.schema.json',
|
|
||||||
'.agents/contracts/executor.schema.json',
|
|
||||||
'.agents/contracts/reviewer-code.schema.json',
|
|
||||||
'.agents/contracts/reviewer-security.schema.json',
|
|
||||||
'.agents/contracts/reviewer-acceptance.schema.json',
|
|
||||||
'.agents/contracts/finalizer.schema.json',
|
|
||||||
'.agents/templates/analysis.template.json',
|
|
||||||
'.agents/templates/plan.template.json',
|
|
||||||
'.agents/templates/execution-report.template.json',
|
|
||||||
'.agents/templates/review-code.template.json',
|
|
||||||
'.agents/templates/review-security.template.json',
|
|
||||||
'.agents/templates/review-acceptance.template.json',
|
|
||||||
'.agents/templates/finalize.template.json',
|
|
||||||
'.agents/contracts/module-manifest.schema.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('.agents/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'] ?? '');
|
|
||||||
$reviewer = (string) ($guard['reviewer'] ?? '');
|
|
||||||
$this->assertMatchesRegularExpression('/^GR-(CORE|TEST|UI|SEC)-[A-Z0-9]{3,}$/', $id, 'Invalid guard id: ' . $id);
|
|
||||||
$this->assertContains($reviewer, ['code', 'security'], 'Invalid reviewer for ' . $id);
|
|
||||||
$ids[] = $id;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertSame($ids, array_values(array_unique($ids)), 'Duplicate guard IDs found.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGuardCatalogReviewerAssignment(): void
|
|
||||||
{
|
|
||||||
$catalog = $this->decodeJson('.agents/checks/guard-catalog.json');
|
|
||||||
$guards = is_array($catalog['guards'] ?? null) ? $catalog['guards'] : [];
|
|
||||||
|
|
||||||
foreach ($guards as $guard) {
|
|
||||||
$id = (string) ($guard['id'] ?? '');
|
|
||||||
$reviewer = (string) ($guard['reviewer'] ?? '');
|
|
||||||
|
|
||||||
if (str_starts_with($id, 'GR-SEC-')) {
|
|
||||||
$this->assertSame('security', $reviewer, $id . ' must be assigned to security reviewer');
|
|
||||||
} else {
|
|
||||||
$this->assertSame('code', $reviewer, $id . ' must be assigned to code reviewer');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testQualityGateIdsAreUniqueAndWellFormed(): void
|
|
||||||
{
|
|
||||||
$gates = $this->decodeJson('.agents/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('.agents/checks/guard-catalog.json');
|
|
||||||
$gates = $this->decodeJson('.agents/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('.agents/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);
|
|
||||||
}
|
|
||||||
|
|
||||||
$reviewCodeTemplate = $this->decodeJson('.agents/templates/review-code.template.json');
|
|
||||||
$reviewGuardIds = is_array($reviewCodeTemplate['checked_guard_ids'] ?? null)
|
|
||||||
? $reviewCodeTemplate['checked_guard_ids']
|
|
||||||
: [];
|
|
||||||
$reviewGateIds = is_array($reviewCodeTemplate['checked_quality_gate_ids'] ?? null)
|
|
||||||
? $reviewCodeTemplate['checked_quality_gate_ids']
|
|
||||||
: [];
|
|
||||||
foreach ($reviewGuardIds as $id) {
|
|
||||||
$this->assertContains($id, $guardIds, 'Unknown guard id in review-code template: ' . (string) $id);
|
|
||||||
}
|
|
||||||
foreach ($reviewGateIds as $id) {
|
|
||||||
$this->assertContains($id, $gateIds, 'Unknown gate id in review-code template: ' . (string) $id);
|
|
||||||
}
|
|
||||||
|
|
||||||
$reviewSecurityTemplate = $this->decodeJson('.agents/templates/review-security.template.json');
|
|
||||||
$secGuardIds = is_array($reviewSecurityTemplate['checked_guard_ids'] ?? null)
|
|
||||||
? $reviewSecurityTemplate['checked_guard_ids']
|
|
||||||
: [];
|
|
||||||
foreach ($secGuardIds as $id) {
|
|
||||||
$this->assertContains($id, $guardIds, 'Unknown guard id in review-security template: ' . (string) $id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testRolePromptsReferenceGuardCatalog(): void
|
|
||||||
{
|
|
||||||
$plannerPrompt = $this->readProjectFile('.agents/prompts/planner.md');
|
|
||||||
$executorPrompt = $this->readProjectFile('.agents/prompts/executor.md');
|
|
||||||
$codeReviewerPrompt = $this->readProjectFile('.agents/prompts/reviewer-code.md');
|
|
||||||
$securityReviewerPrompt = $this->readProjectFile('.agents/prompts/reviewer-security.md');
|
|
||||||
|
|
||||||
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $plannerPrompt);
|
|
||||||
$this->assertStringContainsString('.agents/checks/quality-gates.json', $plannerPrompt);
|
|
||||||
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $executorPrompt);
|
|
||||||
$this->assertStringContainsString('.agents/checks/quality-gates.json', $executorPrompt);
|
|
||||||
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $codeReviewerPrompt);
|
|
||||||
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $securityReviewerPrompt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAllSevenRolePromptsExist(): void
|
|
||||||
{
|
|
||||||
$roles = ['analyst', 'planner', 'executor', 'reviewer-code', 'reviewer-security', 'reviewer-acceptance', 'finalizer'];
|
|
||||||
|
|
||||||
foreach ($roles as $role) {
|
|
||||||
$this->readProjectFile('.agents/prompts/' . $role . '.md');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAllSevenContractsExist(): void
|
|
||||||
{
|
|
||||||
$contracts = ['analyst', 'planner', 'executor', 'reviewer-code', 'reviewer-security', 'reviewer-acceptance', 'finalizer'];
|
|
||||||
|
|
||||||
foreach ($contracts as $contract) {
|
|
||||||
$decoded = json_decode($this->readProjectFile('.agents/contracts/' . $contract . '.schema.json'), true);
|
|
||||||
$this->assertIsArray($decoded, 'Invalid JSON schema: ' . $contract);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testAllSevenTemplatesExist(): void
|
|
||||||
{
|
|
||||||
$templates = [
|
|
||||||
'analysis', 'plan', 'execution-report',
|
|
||||||
'review-code', 'review-security', 'review-acceptance', 'finalize',
|
|
||||||
];
|
|
||||||
|
|
||||||
foreach ($templates as $template) {
|
|
||||||
$decoded = json_decode($this->readProjectFile('.agents/templates/' . $template . '.template.json'), true);
|
|
||||||
$this->assertIsArray($decoded, 'Invalid JSON template: ' . $template);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
70
tests/Architecture/AgentSystemFileContractTest.php
Normal file
70
tests/Architecture/AgentSystemFileContractTest.php
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AgentSystemFileContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testAgentSystemJsonFilesAreValidJson(): void
|
||||||
|
{
|
||||||
|
$jsonFiles = [
|
||||||
|
'.agents/checks/guard-catalog.json',
|
||||||
|
'.agents/checks/quality-gates.json',
|
||||||
|
'.agents/contracts/analyst.schema.json',
|
||||||
|
'.agents/contracts/planner.schema.json',
|
||||||
|
'.agents/contracts/executor.schema.json',
|
||||||
|
'.agents/contracts/reviewer-code.schema.json',
|
||||||
|
'.agents/contracts/reviewer-security.schema.json',
|
||||||
|
'.agents/contracts/reviewer-acceptance.schema.json',
|
||||||
|
'.agents/contracts/finalizer.schema.json',
|
||||||
|
'.agents/templates/analysis.template.json',
|
||||||
|
'.agents/templates/plan.template.json',
|
||||||
|
'.agents/templates/execution-report.template.json',
|
||||||
|
'.agents/templates/review-code.template.json',
|
||||||
|
'.agents/templates/review-security.template.json',
|
||||||
|
'.agents/templates/review-acceptance.template.json',
|
||||||
|
'.agents/templates/finalize.template.json',
|
||||||
|
'.agents/contracts/module-manifest.schema.json',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($jsonFiles as $file) {
|
||||||
|
$decoded = json_decode($this->readProjectFile($file), true);
|
||||||
|
$this->assertIsArray($decoded, 'Invalid JSON: ' . $file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllSevenRolePromptsExist(): void
|
||||||
|
{
|
||||||
|
$roles = ['analyst', 'planner', 'executor', 'reviewer-code', 'reviewer-security', 'reviewer-acceptance', 'finalizer'];
|
||||||
|
|
||||||
|
foreach ($roles as $role) {
|
||||||
|
$this->readProjectFile('.agents/prompts/' . $role . '.md');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllSevenContractsExist(): void
|
||||||
|
{
|
||||||
|
$contracts = ['analyst', 'planner', 'executor', 'reviewer-code', 'reviewer-security', 'reviewer-acceptance', 'finalizer'];
|
||||||
|
|
||||||
|
foreach ($contracts as $contract) {
|
||||||
|
$decoded = json_decode($this->readProjectFile('.agents/contracts/' . $contract . '.schema.json'), true);
|
||||||
|
$this->assertIsArray($decoded, 'Invalid JSON schema: ' . $contract);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAllSevenTemplatesExist(): void
|
||||||
|
{
|
||||||
|
$templates = [
|
||||||
|
'analysis', 'plan', 'execution-report',
|
||||||
|
'review-code', 'review-security', 'review-acceptance', 'finalize',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($templates as $template) {
|
||||||
|
$decoded = json_decode($this->readProjectFile('.agents/templates/' . $template . '.template.json'), true);
|
||||||
|
$this->assertIsArray($decoded, 'Invalid JSON template: ' . $template);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
76
tests/Architecture/AgentSystemReferenceContractTest.php
Normal file
76
tests/Architecture/AgentSystemReferenceContractTest.php
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class AgentSystemReferenceContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use AgentSystemContractSupport;
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testTemplatesReferenceKnownGuardAndGateIds(): void
|
||||||
|
{
|
||||||
|
$catalog = $this->decodeAgentJson('.agents/checks/guard-catalog.json');
|
||||||
|
$gates = $this->decodeAgentJson('.agents/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->decodeAgentJson('.agents/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);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reviewCodeTemplate = $this->decodeAgentJson('.agents/templates/review-code.template.json');
|
||||||
|
$reviewGuardIds = is_array($reviewCodeTemplate['checked_guard_ids'] ?? null)
|
||||||
|
? $reviewCodeTemplate['checked_guard_ids']
|
||||||
|
: [];
|
||||||
|
$reviewGateIds = is_array($reviewCodeTemplate['checked_quality_gate_ids'] ?? null)
|
||||||
|
? $reviewCodeTemplate['checked_quality_gate_ids']
|
||||||
|
: [];
|
||||||
|
foreach ($reviewGuardIds as $id) {
|
||||||
|
$this->assertContains($id, $guardIds, 'Unknown guard id in review-code template: ' . (string) $id);
|
||||||
|
}
|
||||||
|
foreach ($reviewGateIds as $id) {
|
||||||
|
$this->assertContains($id, $gateIds, 'Unknown gate id in review-code template: ' . (string) $id);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reviewSecurityTemplate = $this->decodeAgentJson('.agents/templates/review-security.template.json');
|
||||||
|
$secGuardIds = is_array($reviewSecurityTemplate['checked_guard_ids'] ?? null)
|
||||||
|
? $reviewSecurityTemplate['checked_guard_ids']
|
||||||
|
: [];
|
||||||
|
foreach ($secGuardIds as $id) {
|
||||||
|
$this->assertContains($id, $guardIds, 'Unknown guard id in review-security template: ' . (string) $id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRolePromptsReferenceGuardCatalog(): void
|
||||||
|
{
|
||||||
|
$plannerPrompt = $this->readProjectFile('.agents/prompts/planner.md');
|
||||||
|
$executorPrompt = $this->readProjectFile('.agents/prompts/executor.md');
|
||||||
|
$codeReviewerPrompt = $this->readProjectFile('.agents/prompts/reviewer-code.md');
|
||||||
|
$securityReviewerPrompt = $this->readProjectFile('.agents/prompts/reviewer-security.md');
|
||||||
|
|
||||||
|
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $plannerPrompt);
|
||||||
|
$this->assertStringContainsString('.agents/checks/quality-gates.json', $plannerPrompt);
|
||||||
|
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $executorPrompt);
|
||||||
|
$this->assertStringContainsString('.agents/checks/quality-gates.json', $executorPrompt);
|
||||||
|
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $codeReviewerPrompt);
|
||||||
|
$this->assertStringContainsString('.agents/checks/guard-catalog.json', $securityReviewerPrompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
46
tests/Architecture/CodexAutomationContractTest.php
Normal file
46
tests/Architecture/CodexAutomationContractTest.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class CodexAutomationContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testCodexSkillsSyncScriptSupportsCheckAndApply(): void
|
||||||
|
{
|
||||||
|
$script = $this->readProjectFile('bin/codex-skills-sync.sh');
|
||||||
|
$this->assertStringContainsString('--check', $script);
|
||||||
|
$this->assertStringContainsString('--apply', $script);
|
||||||
|
$this->assertStringContainsString('mode="check"', $script);
|
||||||
|
$this->assertStringContainsString('"core-guardrails"', $script);
|
||||||
|
$this->assertStringContainsString('"starterkit-planner"', $script);
|
||||||
|
$this->assertStringContainsString('"starterkit-grid-standards"', $script);
|
||||||
|
$this->assertStringContainsString('"starterkit-php-style-ci"', $script);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDocsLinkCheckScriptExcludesHistoricalRunArtifactsByDefault(): void
|
||||||
|
{
|
||||||
|
$script = $this->readProjectFile('bin/docs-link-check.sh');
|
||||||
|
$this->assertStringContainsString("!.agents/runs/**", $script);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testQualityGatesIncludeDocsLinksAndSkillsSyncChecks(): void
|
||||||
|
{
|
||||||
|
$qualityGates = json_decode($this->readProjectFile('.agents/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'] ?? ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
46
tests/Architecture/CodexDocsContractSupport.php
Normal file
46
tests/Architecture/CodexDocsContractSupport.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
trait CodexDocsContractSupport
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return list<string>
|
||||||
|
*/
|
||||||
|
private function filesCoveredByDocsLinkIntegrityCheck(): array
|
||||||
|
{
|
||||||
|
$root = dirname(__DIR__, 2);
|
||||||
|
$files = ['README.md'];
|
||||||
|
$scanDirs = ['docs', '.agents'];
|
||||||
|
|
||||||
|
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, '.agents/runs/')) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$files[] = $relative;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$files = array_values(array_unique($files));
|
||||||
|
sort($files);
|
||||||
|
return $files;
|
||||||
|
}
|
||||||
|
}
|
||||||
75
tests/Architecture/CodexDocsIntegrityContractTest.php
Normal file
75
tests/Architecture/CodexDocsIntegrityContractTest.php
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class CodexDocsIntegrityContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use CodexDocsContractSupport;
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testCodexPromptsDocumentationReferencesBothSkills(): void
|
||||||
|
{
|
||||||
|
$prompts = $this->readProjectFile('docs/reference-codex-prompts.md');
|
||||||
|
$this->assertStringContainsString('core-guardrails', $prompts);
|
||||||
|
$this->assertStringContainsString('starterkit-planner', $prompts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testDocsIndexReferencesCodexPrompts(): void
|
||||||
|
{
|
||||||
|
$index = $this->readProjectFile('docs/index.md');
|
||||||
|
$this->assertStringContainsString('/docs/reference-codex-prompts.md', $index);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
$normalized = ltrim($link, '/');
|
||||||
|
$absolute = dirname(__DIR__, 2) . '/' . $normalized;
|
||||||
|
$this->assertFileExists($absolute, sprintf('Broken docs link "%s" in %s', $link, $file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(dirname(__DIR__, 2) . '/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));
|
||||||
|
}
|
||||||
|
}
|
||||||
46
tests/Architecture/CodexSkillReferenceContractTest.php
Normal file
46
tests/Architecture/CodexSkillReferenceContractTest.php
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace MintyPHP\Tests\Architecture;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class CodexSkillReferenceContractTest extends TestCase
|
||||||
|
{
|
||||||
|
use ProjectFileAssertionSupport;
|
||||||
|
|
||||||
|
public function testCoreGuardrailsSkillAndReferencesExist(): void
|
||||||
|
{
|
||||||
|
$skill = $this->readProjectFile('.agents/skills/core-guardrails/SKILL.md');
|
||||||
|
$this->assertStringContainsString('name: core-guardrails', $skill);
|
||||||
|
|
||||||
|
$this->readProjectFile('.agents/skills/core-guardrails/references/boundaries-core.md');
|
||||||
|
$this->readProjectFile('.agents/skills/core-guardrails/references/boundaries-ui.md');
|
||||||
|
$this->readProjectFile('.agents/skills/core-guardrails/references/quality-gates.md');
|
||||||
|
$this->readProjectFile('.agents/skills/core-guardrails/references/source-map.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStarterkitPlannerSkillAndReferencesExist(): void
|
||||||
|
{
|
||||||
|
$skill = $this->readProjectFile('.agents/skills/starterkit-planner/SKILL.md');
|
||||||
|
$this->assertStringContainsString('name: starterkit-planner', $skill);
|
||||||
|
|
||||||
|
$this->readProjectFile('.agents/skills/starterkit-planner/references/plan-template.md');
|
||||||
|
$this->readProjectFile('.agents/skills/starterkit-planner/references/tradeoff-matrix.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStarterkitGridStandardsSkillAndReferencesExist(): void
|
||||||
|
{
|
||||||
|
$skill = $this->readProjectFile('.agents/skills/starterkit-grid-standards/SKILL.md');
|
||||||
|
$this->assertStringContainsString('name: starterkit-grid-standards', $skill);
|
||||||
|
|
||||||
|
$this->readProjectFile('.agents/skills/starterkit-grid-standards/references/list-checklist.md');
|
||||||
|
$this->readProjectFile('.agents/skills/starterkit-grid-standards/references/row-interaction-standard.md');
|
||||||
|
$this->readProjectFile('.agents/skills/starterkit-grid-standards/references/page-size-standard.md');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStarterkitPhpStyleCiSkillExists(): void
|
||||||
|
{
|
||||||
|
$skill = $this->readProjectFile('.agents/skills/starterkit-php-style-ci/SKILL.md');
|
||||||
|
$this->assertStringContainsString('name: starterkit-php-style-ci', $skill);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,209 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace MintyPHP\Tests\Architecture;
|
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
|
||||||
|
|
||||||
class CodexSkillsContractTest extends TestCase
|
|
||||||
{
|
|
||||||
use ProjectFileAssertionSupport;
|
|
||||||
|
|
||||||
public function testCoreGuardrailsSkillAndReferencesExist(): void
|
|
||||||
{
|
|
||||||
$skill = $this->readProjectFile('tools/codex-skills/core-guardrails/SKILL.md');
|
|
||||||
$this->assertStringContainsString('name: core-guardrails', $skill);
|
|
||||||
|
|
||||||
$this->readProjectFile('tools/codex-skills/core-guardrails/references/boundaries-core.md');
|
|
||||||
$this->readProjectFile('tools/codex-skills/core-guardrails/references/boundaries-ui.md');
|
|
||||||
$this->readProjectFile('tools/codex-skills/core-guardrails/references/quality-gates.md');
|
|
||||||
$this->readProjectFile('tools/codex-skills/core-guardrails/references/source-map.md');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testStarterkitPlannerSkillAndReferencesExist(): void
|
|
||||||
{
|
|
||||||
$skill = $this->readProjectFile('tools/codex-skills/starterkit-planner/SKILL.md');
|
|
||||||
$this->assertStringContainsString('name: starterkit-planner', $skill);
|
|
||||||
|
|
||||||
$this->readProjectFile('tools/codex-skills/starterkit-planner/references/plan-template.md');
|
|
||||||
$this->readProjectFile('tools/codex-skills/starterkit-planner/references/tradeoff-matrix.md');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testStarterkitGridStandardsSkillAndReferencesExist(): void
|
|
||||||
{
|
|
||||||
$skill = $this->readProjectFile('tools/codex-skills/starterkit-grid-standards/SKILL.md');
|
|
||||||
$this->assertStringContainsString('name: starterkit-grid-standards', $skill);
|
|
||||||
|
|
||||||
$this->readProjectFile('tools/codex-skills/starterkit-grid-standards/references/list-checklist.md');
|
|
||||||
$this->readProjectFile('tools/codex-skills/starterkit-grid-standards/references/row-interaction-standard.md');
|
|
||||||
$this->readProjectFile('tools/codex-skills/starterkit-grid-standards/references/page-size-standard.md');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testStarterkitPhpStyleCiSkillExists(): void
|
|
||||||
{
|
|
||||||
$skill = $this->readProjectFile('tools/codex-skills/starterkit-php-style-ci/SKILL.md');
|
|
||||||
$this->assertStringContainsString('name: starterkit-php-style-ci', $skill);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCodexPromptsDocumentationReferencesBothSkills(): void
|
|
||||||
{
|
|
||||||
$prompts = $this->readProjectFile('docs/reference-codex-prompts.md');
|
|
||||||
$this->assertStringContainsString('core-guardrails', $prompts);
|
|
||||||
$this->assertStringContainsString('starterkit-planner', $prompts);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDocsIndexReferencesCodexPrompts(): void
|
|
||||||
{
|
|
||||||
$index = $this->readProjectFile('docs/index.md');
|
|
||||||
$this->assertStringContainsString('/docs/reference-codex-prompts.md', $index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCodexSkillsSyncScriptSupportsCheckAndApply(): void
|
|
||||||
{
|
|
||||||
$script = $this->readProjectFile('bin/codex-skills-sync.sh');
|
|
||||||
$this->assertStringContainsString('--check', $script);
|
|
||||||
$this->assertStringContainsString('--apply', $script);
|
|
||||||
$this->assertStringContainsString('mode="check"', $script);
|
|
||||||
$this->assertStringContainsString('"core-guardrails"', $script);
|
|
||||||
$this->assertStringContainsString('"starterkit-planner"', $script);
|
|
||||||
$this->assertStringContainsString('"starterkit-grid-standards"', $script);
|
|
||||||
$this->assertStringContainsString('"starterkit-php-style-ci"', $script);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testDocsLinkCheckScriptExcludesHistoricalRunArtifactsByDefault(): void
|
|
||||||
{
|
|
||||||
$script = $this->readProjectFile('bin/docs-link-check.sh');
|
|
||||||
$this->assertStringContainsString("!agent-system/runs/**", $script);
|
|
||||||
}
|
|
||||||
|
|
||||||
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) {
|
|
||||||
$normalized = ltrim($link, '/');
|
|
||||||
$absolute = $this->projectRootPath() . '/' . $normalized;
|
|
||||||
$this->assertFileExists($absolute, sprintf('Broken docs link "%s" in %s', $link, $file));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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');
|
|
||||||
$this->assertStringContainsString('P0', $policy);
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -32,8 +32,8 @@ ohne dass jede Aenderung immer die komplette Suite im Kopf behalten muss.
|
|||||||
|
|
||||||
## Naechste Refactor-Kandidaten
|
## Naechste Refactor-Kandidaten
|
||||||
|
|
||||||
- `tests/Architecture/CodexSkillsContractTest.php`
|
|
||||||
- `tests/Architecture/ModuleRegistryContractTest.php`
|
- `tests/Architecture/ModuleRegistryContractTest.php`
|
||||||
- `tests/Architecture/AgentSystemContractTest.php`
|
|
||||||
- `tests/Architecture/DetailValidationSummaryContractTest.php`
|
- `tests/Architecture/DetailValidationSummaryContractTest.php`
|
||||||
- `tests/Architecture/DetailActionPolicyContractTest.php`
|
- `tests/Architecture/DetailActionPolicyContractTest.php`
|
||||||
|
- `tests/Architecture/NoAddressBookHardcodingTest.php`
|
||||||
|
- `tests/Architecture/NoBookmarksHardcodingTest.php`
|
||||||
|
|||||||
Reference in New Issue
Block a user