diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml new file mode 100644 index 0000000..6434166 --- /dev/null +++ b/.github/workflows/quality.yml @@ -0,0 +1,77 @@ +name: EvolvePHP 2 Quality + +on: + pull_request: + branches: + - 2.x + push: + branches: + - 2.x + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: evolvephp-2-quality-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + policy: + name: Policy (PHP 8.4) + runs-on: ubuntu-24.04 + + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: '8.4' + tools: composer:v2 + coverage: none + + - name: Validate workspace Composer manifest and lockfile + run: composer --working-dir=workspace validate --strict --check-lock + + - name: Install workspace dependencies + run: composer --working-dir=workspace install --no-interaction --no-progress --prefer-dist + + - name: Run root policy tests + run: php workspace/vendor/bin/phpunit --configuration phpunit.xml.dist tests/Architecture tests/Documentation + + workspace-quality: + name: Workspace quality (PHP ${{ matrix.php }}) + runs-on: ubuntu-24.04 + + strategy: + fail-fast: false + matrix: + php: + - '8.4' + - '8.5' + + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Set up PHP + uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 + with: + php-version: ${{ matrix.php }} + tools: composer:v2 + coverage: none + + - name: Validate workspace Composer manifest and lockfile + run: composer --working-dir=workspace validate --strict --check-lock + + - name: Install workspace dependencies + run: composer --working-dir=workspace install --no-interaction --no-progress --prefer-dist + + - name: Run workspace quality + run: composer --working-dir=workspace quality diff --git a/CHANGELOG.md b/CHANGELOG.md index 6263c07..d6bbc82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Documentation and governance +- Added Phase 2.6 GitHub Actions CI foundation with a PHP 8.4/8.5 workspace quality matrix, separate PHP 8.4 root-policy job, lockfile-based workspace installation, immutable action pinning, and successful initial CI execution for the current workspace, tooling and package foundation. - Added Phase 2.5.1 README and metadata consistency cleanup, aligning the EvolvePHP 2 README hierarchy, correcting root Composer metadata, deduplicating stale RFC index narration, removing duplicated phase history, and clarifying workspace installation and compatibility guidance. - Added Phase 2.5 workspace-owned Deptrac architecture and dependency-boundary enforcement for the initial EvolvePHP 2 package graph. - Added Phase 2.4 workspace-owned PHPStan level 6 static analysis, PHPUnit type-inference integration, PHP-CS-Fixer PER Coding Style 3.0 checks, and architecture-policy tests for cache, baseline and tooling ownership. diff --git a/README.md b/README.md index afb55d8..9cc3956 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ EvolvePHP 1 remains preserved on `master` for historical reference and legacy ma The current EvolvePHP 2 repository contains package boundaries, Composer workspace setup and quality-tooling foundations. Runtime framework implementation is not yet complete, and the packages are not yet published. -EvolvePHP 2 requires PHP 8.4. Local PHP 8.4 validation has been performed. PHP 8.5 remains pending Phase 2.6 CI evidence. +EvolvePHP 2 requires PHP 8.4. The current workspace quality pipeline is verified by GitHub Actions on PHP 8.4 and PHP 8.5 for the current workspace, quality tooling and package foundation. ## Project Overview @@ -21,7 +21,7 @@ The EvolvePHP 2 line redesigns the framework as a modular package architecture w - Runtime implementation: not yet complete - Package publication: packages are not yet published - PHP baseline: PHP 8.4 -- PHP 8.5: compatibility remains pending Phase 2.6 CI evidence +- CI verification: current workspace quality passes in GitHub Actions on PHP 8.4 and PHP 8.5 ## Requirements diff --git a/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php b/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php new file mode 100644 index 0000000..eeb4071 --- /dev/null +++ b/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php @@ -0,0 +1,206 @@ +root = dirname(__DIR__, 2); + $this->workflowPath = $this->projectPath('.github/workflows/quality.yml'); + + $this->assertFileExists($this->workflowPath, 'The canonical EvolvePHP 2 quality workflow should exist.'); + + $this->workflow = file_get_contents($this->workflowPath); + $this->assertNotFalse($this->workflow, 'The canonical EvolvePHP 2 quality workflow should be readable.'); + } + + public function testCanonicalWorkflowNameAndTriggersTargetOnlyEvolvePhp2(): void + { + $this->assertMatchesPattern('/^name:\s*EvolvePHP 2 Quality\s*$/m', $this->workflow); + $this->assertMatchesPattern('/^on:\s*$/m', $this->workflow); + $this->assertMatchesPattern('/^\s{2}pull_request:\s*\R\s{4}branches:\s*\R\s{6}- 2\.x\s*$/m', $this->workflow); + $this->assertMatchesPattern('/^\s{2}push:\s*\R\s{4}branches:\s*\R\s{6}- 2\.x\s*$/m', $this->workflow); + $this->assertMatchesPattern('/^\s{2}workflow_dispatch:\s*$/m', $this->workflow); + $this->assertDoesNotMatchPattern('/^\s*-\s*master\s*$/m', $this->workflow); + $this->assertDoesNotMatchPattern('/pull_request_target/', $this->workflow); + $this->assertDoesNotMatchPattern('/paths(?:-ignore)?:/', $this->workflow); + $this->assertDoesNotMatchPattern('/tags(?:-ignore)?:/', $this->workflow); + } + + public function testWorkflowUsesLeastPrivilegePermissionsAndCancelsSupersededRuns(): void + { + $this->assertMatchesPattern('/^permissions:\s*\R\s{2}contents:\s*read\s*$/m', $this->workflow); + $this->assertSame(1, preg_match_all('/^\s{2}[a-z-]+:\s*(?:read|write|none)\s*$/m', $this->extractTopLevelBlock('permissions'), $matches)); + $this->assertDoesNotMatchPattern('/\bwrite\b/', $this->extractTopLevelBlock('permissions')); + $this->assertMatchesPattern('/^concurrency:\s*$/m', $this->workflow); + $this->assertMatchesPattern('/^\s{2}group:\s*evolvephp-2-quality-\$\{\{ github\.workflow \}\}-\$\{\{ github\.event\.pull_request\.number \|\| github\.ref \}\}\s*$/m', $this->workflow); + $this->assertMatchesPattern('/^\s{2}cancel-in-progress:\s*true\s*$/m', $this->workflow); + } + + public function testWorkflowUsesOnlyApprovedUbuntuRunnerAndPhpMatrix(): void + { + $this->assertSame(2, preg_match_all('/^\s{4}runs-on:\s*ubuntu-24\.04\s*$/m', $this->workflow, $matches)); + $this->assertDoesNotMatchPattern('/ubuntu-latest|windows-|macos-/', $this->workflow); + $this->assertMatchesPattern('/fail-fast:\s*false/', $this->workflow); + $this->assertMatchesPattern('/php:\s*\R\s{10}- \'8\.4\'\s*\R\s{10}- \'8\.5\'/m', $this->workflow); + $this->assertSame(2, preg_match_all('/^\s{10}- \'8\.[45]\'\s*$/m', $this->workflow, $matches)); + $this->assertDoesNotMatchPattern('/nightly|experimental|lowest|highest|latest|8\.6/', $this->workflow); + } + + public function testWorkflowPinsOnlyReviewedActionReleaseCommits(): void + { + $checkoutSha = '3d3c42e5aac5ba805825da76410c181273ba90b1'; + $setupPhpSha = 'f3e473d116dcccaddc5834248c87452386958240'; + + $this->assertSame(2, preg_match_all('/uses:\s*actions\/checkout@' . $checkoutSha . '\s+# v7\.0\.1/', $this->workflow, $matches)); + $this->assertSame(2, preg_match_all('/uses:\s*shivammathur\/setup-php@' . $setupPhpSha . '\s+# 2\.37\.2/', $this->workflow, $matches)); + $this->assertSame(4, preg_match_all('/uses:\s*[^@\s]+@[0-9a-f]{40}\s+# (?:v7\.0\.1|2\.37\.2)/', $this->workflow, $matches)); + $this->assertMatchesPattern('/persist-credentials:\s*false/', $this->workflow); + $this->assertSame(2, preg_match_all('/tools:\s*composer:v2/', $this->workflow, $matches)); + $this->assertSame(2, preg_match_all('/coverage:\s*none/', $this->workflow, $matches)); + $this->assertDoesNotMatchPattern('/actions\/checkout@(?:v[0-9]+|main)|shivammathur\/setup-php@(?:v[0-9]+|main)/', $this->workflow); + $this->assertDoesNotMatchPattern('/uses:\s*(?!actions\/checkout@|shivammathur\/setup-php@)[^@\s]+@/', $this->workflow); + $this->assertDoesNotMatchPattern('/@[0-9a-f]{7,39}(?:\s|$)/', $this->workflow); + $this->assertDoesNotMatchPattern('/actions\/cache/', $this->workflow); + } + + public function testPolicyJobRunsRootPolicySuitesWithWorkspacePhpUnitOnPhp84(): void + { + $job = $this->extractJob('policy'); + + $this->assertMatchesPattern('/name:\s*Policy \(PHP 8\.4\)/', $job); + $this->assertMatchesPattern('/runs-on:\s*ubuntu-24\.04/', $job); + $this->assertMatchesPattern('/php-version:\s*\'8\.4\'/', $job); + $this->assertStringContainsString('composer --working-dir=workspace validate --strict --check-lock', $job); + $this->assertStringContainsString('composer --working-dir=workspace install --no-interaction --no-progress --prefer-dist', $job); + $this->assertStringContainsString('php workspace/vendor/bin/phpunit --configuration phpunit.xml.dist tests/Architecture tests/Documentation', $job); + $this->assertDoesNotMatchPattern('/composer install(?! --working-dir=workspace)|composer --working-dir=\.\s+install/', $job); + $this->assertDoesNotMatchPattern('/composer update|--ignore-platform-reqs?|config\.platform\.php/', $job); + $this->assertDoesNotMatchPattern('/phpunit.*(?:core|components|helpers|index\.php|route\.php)/i', $job); + } + + public function testWorkspaceQualityMatrixUsesLockfileInstallAndApprovedAggregateCommand(): void + { + $job = $this->extractJob('workspace-quality'); + + $this->assertMatchesPattern('/name:\s*Workspace quality \(PHP \$\{\{ matrix\.php \}\}\)/', $job); + $this->assertMatchesPattern('/strategy:\s*\R\s{6}fail-fast:\s*false/', $job); + $this->assertMatchesPattern('/php:\s*\R\s{10}- \'8\.4\'\s*\R\s{10}- \'8\.5\'/m', $job); + $this->assertStringContainsString('composer --working-dir=workspace validate --strict --check-lock', $job); + $this->assertStringContainsString('composer --working-dir=workspace install --no-interaction --no-progress --prefer-dist', $job); + $this->assertStringContainsString('composer --working-dir=workspace quality', $job); + $this->assertDoesNotMatchPattern('/composer update|style:fix|continue-on-error|--ignore-platform-reqs?/', $job); + $this->assertDoesNotMatchPattern('/composer --working-dir=workspace (architecture|analyse|style:check|test)(?:\s|$)/', $job); + } + + public function testWorkflowExcludesReleasePublishingSecretsCachesAndDeployments(): void + { + foreach (array( + '/secrets\./', + '/deploy(?:ment)?/i', + '/publish/i', + '/release/i', + '/upload-artifact/', + '/codecov|coveralls/i', + '/actions\/cache/', + '/environment:/', + '/sudo\b/', + '/composer install(?! --working-dir=workspace)/', + '/--ignore-platform-reqs?/', + '/config\.platform\.php/', + ) as $pattern) { + $this->assertDoesNotMatchPattern($pattern, $this->workflow); + } + } + + public function testDocumentationRecordsContinuousIntegrationCompatibilityEvidence(): void + { + $workspaceReadme = $this->readProjectFile('workspace/README.md'); + $changelog = $this->readProjectFile('CHANGELOG.md'); + + foreach (array( + '/## Continuous Integration/', + '/\.github\/workflows\/quality\.yml/', + '/EvolvePHP 2 Quality/', + '/pull requests.*2\.x|2\.x.*pull requests/i', + '/pushes.*2\.x|2\.x.*pushes/i', + '/manual.*dispatch|workflow_dispatch/i', + '/contents:\s*read/', + '/concurrency.*cancel|cancel.*concurrency/i', + '/Ubuntu 24\.04/', + '/policy job.*PHP 8\.4|PHP 8\.4.*policy job/i', + '/Architecture and Documentation.*workspace PHPUnit 13|workspace PHPUnit 13.*Architecture and Documentation/i', + '/EvolvePHP 1 runtime.*not part|not part.*EvolvePHP 1 runtime/i', + '/workspace quality matrix.*PHP 8\.4.*PHP 8\.5|PHP 8\.4.*PHP 8\.5.*workspace quality matrix/i', + '/Composer validation.*before.*install|validate.*before.*install/i', + '/lockfile.*composer install|composer install.*lockfile/i', + '/no `composer update`|not run `composer update`/i', + '/no platform-requirement bypass|platform-requirement bypass.*not/i', + '/no initial dependency cache|dependency cache.*not/i', + '/immutable.*full-SHA|full-SHA.*immutable/i', + '/release comments.*SHA|SHA.*release comments/i', + '/Phase 2\.6 CI matrix.*successfully executed|successfully executed.*Phase 2\.6 CI matrix/i', + '/workspace quality.*passes.*PHP 8\.4.*PHP 8\.5|PHP 8\.4.*PHP 8\.5.*workspace quality.*passes/i', + '/current.*(?:workspace|tooling|package foundation)|(?:workspace|tooling|package foundation).*current/i', + '/EvolvePHP 1 runtime.*(?:not part|excluded)|(?:not part|excluded).*EvolvePHP 1 runtime/i', + '/runtime implementation.*(?:incomplete|not complete)|(?:incomplete|not complete).*runtime implementation/i', + ) as $pattern) { + $this->assertMatchesPattern($pattern, $workspaceReadme); + } + + $this->assertMatchesPattern('/Phase 2\.6/i', $changelog); + $this->assertMatchesPattern('/PHP 8\.4\/8\.5 workspace quality matrix/i', $changelog); + $this->assertMatchesPattern('/successful initial CI execution|initial GitHub Actions execution completed successfully|CI execution.*successfully/i', $changelog); + $this->assertMatchesPattern('/separate PHP 8\.4 root-policy job/i', $changelog); + $this->assertMatchesPattern('/lockfile-based workspace installation/i', $changelog); + $this->assertMatchesPattern('/immutable action pinning/i', $changelog); + $this->assertDoesNotMatchPattern('/branch protection.*active|required checks|deployment|publishing|runtime implementation.*complete|legacy EvolvePHP 1 runtime.*PHP 8\.5/i', $changelog); + } + + private function extractTopLevelBlock($heading) + { + $pattern = '/^' . preg_quote($heading, '/') . ':\s*\R(?P.*?)(?=^[a-zA-Z_-]+:\s*|\z)/ms'; + + $this->assertSame(1, preg_match($pattern, $this->workflow, $matches), 'Missing top-level block: ' . $heading); + + return $matches['block']; + } + + private function extractJob($job) + { + $jobs = $this->extractTopLevelBlock('jobs'); + $pattern = '/^\s{2}' . preg_quote($job, '/') . ':\s*\R(?P.*?)(?=^\s{2}[a-zA-Z0-9_-]+:\s*|\z)/ms'; + + $this->assertSame(1, preg_match($pattern, $jobs, $matches), 'Missing job: ' . $job); + + return $matches['job']; + } + + private function projectPath($path) + { + return $this->root . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $path); + } + + private function readProjectFile($path) + { + $fullPath = $this->projectPath($path); + $this->assertFileExists($fullPath, $path . ' should exist before it is read.'); + + return file_get_contents($fullPath); + } + + private function assertMatchesPattern($pattern, $content) + { + $this->assertSame(1, preg_match($pattern, $content), 'Failed asserting that content matches ' . $pattern); + } + + private function assertDoesNotMatchPattern($pattern, $content) + { + $this->assertSame(0, preg_match($pattern, $content), 'Failed asserting that content does not match ' . $pattern); + } +} diff --git a/tests/Architecture/EvolvePhp2PhpUnitFoundationTest.php b/tests/Architecture/EvolvePhp2PhpUnitFoundationTest.php index 8060c20..7af7608 100644 --- a/tests/Architecture/EvolvePhp2PhpUnitFoundationTest.php +++ b/tests/Architecture/EvolvePhp2PhpUnitFoundationTest.php @@ -116,8 +116,10 @@ public function testWorkspaceReadmeDocumentsPhpUnitFoundationPolicy(): void $this->assertMatchesPattern('/test:testing/i', $content); $this->assertMatchesPattern('/workspace\/composer\.lock/i', $content); $this->assertMatchesPattern('/platform emulation/i', $content); - $this->assertMatchesPattern('/PHP 8\.5 compatibility.*pending.*Phase 2\.6 CI matrix|Phase 2\.6 CI matrix.*pending.*PHP 8\.5 compatibility/i', $content); - $this->assertDoesNotMatchPattern('/PHP 8\.5[^.\n]*(?:passes|supported|compatible)/i', $content); + $this->assertMatchesPattern('/PHP 8\.4.*baseline|baseline.*PHP 8\.4/i', $content); + $this->assertMatchesPattern('/GitHub Actions.*workspace quality.*PHP 8\.4.*PHP 8\.5|workspace quality.*GitHub Actions.*PHP 8\.4.*PHP 8\.5|PHP 8\.4.*PHP 8\.5.*workspace quality.*GitHub Actions/i', $content); + $this->assertMatchesPattern('/current.*(?:workspace quality|package foundation|tooling)|(?:workspace quality|package foundation|tooling).*current/i', $content); + $this->assertDoesNotMatchPattern('/PHP 8\.5.*pending|pending.*PHP 8\.5|Phase 2\.6.*pending|pending.*Phase 2\.6/i', $content); $this->assertMatchesPattern('/legacy root suite/i', $content); $this->assertMatchesPattern('/EvolvePHP 2 workspace suite/i', $content); } diff --git a/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php b/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php index ed58c36..75eb8d0 100644 --- a/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php +++ b/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php @@ -36,14 +36,18 @@ public function testRootReadmeIdentifiesEvolvePhp2BranchAndLegacyMasterLine(): v $this->assertMatchesPattern('/packages.*not yet published|not yet published.*packages/is', $content); } - public function testRootReadmeDocumentsPhp84WithoutClaimingPhp85Compatibility(): void + public function testRootReadmeDocumentsPhp84BaselineAndPhp85CiEvidence(): void { $content = $this->readProjectFile('README.md'); $this->assertMatchesPattern('/requires PHP 8\.4|PHP 8\.4.*required/i', $content); - $this->assertMatchesPattern('/PHP 8\.4.*validation|validation.*PHP 8\.4/is', $content); - $this->assertMatchesPattern('/PHP 8\.5.*Phase 2\.6|Phase 2\.6.*PHP 8\.5/i', $content); - $this->assertDoesNotMatchPattern('/PHP 8\.5[^.\n]*(?:compatible|compatibility is claimed|passes|supported)/i', $content); + $this->assertMatchesPattern('/PHP baseline:\s*PHP 8\.4|PHP 8\.4.*baseline/i', $content); + $this->assertMatchesPattern('/GitHub Actions.*(?:PHP 8\.4.*PHP 8\.5|PHP 8\.5.*PHP 8\.4)|(?:PHP 8\.4.*PHP 8\.5|PHP 8\.5.*PHP 8\.4).*GitHub Actions/i', $content); + $this->assertMatchesPattern('/workspace quality pipeline|quality pipeline.*workspace/i', $content); + $this->assertMatchesPattern('/current.*(?:workspace|tooling|package foundation)|(?:workspace|tooling|package foundation).*current/i', $content); + $this->assertMatchesPattern('/runtime framework implementation.*not yet complete|not yet complete.*runtime framework implementation/i', $content); + $this->assertDoesNotMatchPattern('/PHP 8\.5.*pending|pending.*PHP 8\.5|Do not claim PHP 8\.5/i', $content); + $this->assertDoesNotMatchPattern('/production[- ]ready|runtime framework supports PHP 8\.5|all runtime.*PHP 8\.5/i', $content); } public function testRootReadmeUsesWorkspaceSetupAndAvoidsLegacySetupAsEvolvePhp2Setup(): void diff --git a/workspace/README.md b/workspace/README.md index f56f5d5..89916d9 100644 --- a/workspace/README.md +++ b/workspace/README.md @@ -12,7 +12,7 @@ The preserved EvolvePHP 1 root Composer harness remains separate so legacy docum - Composer - Git -EvolvePHP 2 requires PHP 8.4. Workspace tooling and tests have been executed locally under PHP 8.4. PHP 8.5 compatibility remains pending the Phase 2.6 CI matrix. +EvolvePHP 2 requires PHP 8.4 as its baseline. GitHub Actions exercises the current workspace quality pipeline on PHP 8.4 and PHP 8.5 for the current tooling and package foundation. Platform emulation must not be used for runtime compatibility claims. Do not use `config.platform.php`, `--ignore-platform-req=php` or `--ignore-platform-reqs` to generate the workspace lockfile or claim PHP compatibility. @@ -239,18 +239,54 @@ Risky rules are disabled. The `declare_strict_types` fixer is not enabled; stric The lockfile must be generated and updated through Composer under real PHP 8.4 execution. It must never be handwritten or generated with platform emulation. +## Continuous Integration + +The canonical GitHub Actions workflow lives at: + +```text +.github/workflows/quality.yml +``` + +The workflow name is `EvolvePHP 2 Quality`. + +It runs for pull requests targeting `2.x`, pushes to `2.x` and manual dispatch. It uses least-privilege `contents: read` permissions and cancels superseded executions through workflow concurrency. + +All jobs run on the explicit Ubuntu 24.04 runner, using the `ubuntu-24.04` label. The workflow has no initial dependency cache. + +The policy job runs on PHP 8.4. The `Policy (PHP 8.4)` job validates the workspace Composer manifest and lockfile before installation, installs workspace dependencies from the committed lockfile with `composer install`, and runs the root Architecture and Documentation policy tests through workspace PHPUnit 13: + +```bash +php workspace/vendor/bin/phpunit --configuration phpunit.xml.dist tests/Architecture tests/Documentation +``` + +Those root policy tests validate EvolvePHP 2 repository governance and documentation. The preserved EvolvePHP 1 runtime is not part of the EvolvePHP 2 compatibility claim. + +The workspace quality matrix runs PHP 8.4 and PHP 8.5. Each matrix entry validates Composer metadata before installation, installs from `workspace/composer.lock`, and runs: + +```bash +composer --working-dir=workspace quality +``` + +The initial Phase 2.6 CI matrix has successfully executed. Workspace quality passes on PHP 8.4 and PHP 8.5, and the root policy job passes on PHP 8.4. This evidence applies to the current workspace, quality tooling and package foundation only. + +PHP 8.5 evidence for the current workspace quality pipeline is recorded by the Phase 2.6 CI matrix. + +The EvolvePHP 2 runtime implementation is incomplete, so this is not a broader runtime-production compatibility claim. + +The workflow must not run `composer update`, use a platform-requirement bypass, install root Composer dependencies or replace the approved aggregate quality command with duplicated individual quality commands. There is no platform-requirement bypass in CI. + +Action dependencies are pinned by immutable full-SHA references. The reviewed release comments must remain beside those SHAs so future audits can connect each commit pin to its intended release tag. + ## Compatibility Evidence -EvolvePHP 2 requires PHP 8.4. Local workspace validation has been performed under PHP 8.4. +EvolvePHP 2 requires PHP 8.4 as the baseline. The Phase 2.6 CI matrix has successfully executed in GitHub Actions: the current workspace quality pipeline passes on PHP 8.4 and PHP 8.5, and the root policy job passes on PHP 8.4. -PHP 8.5 compatibility remains pending the Phase 2.6 CI matrix. Do not claim PHP 8.5 support until that evidence exists. +This verifies the current workspace, quality tooling and package foundation only. The preserved EvolvePHP 1 runtime is excluded, and the EvolvePHP 2 runtime implementation remains incomplete. ## Deferred Work The following work remains deferred: -- GitHub Actions -- PHP 8.5 CI verification - Security and license scanning - Release automation - Runtime framework implementation