diff --git a/AGENTS.md b/AGENTS.md index 124c2ee..a3af996 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,116 +1,111 @@ -# Agent Workflow Rules +# Automation and Contribution Guide -Every coding agent must read and follow this file before auditing, planning or modifying this repository. +Every coding agent must read and follow this file as public automation guidance for this repository. -## 1. Scope discipline +This guide applies to repository work performed by contributors, automation tools and coding assistants. It keeps automated changes reviewable, evidence-based and aligned with the public EvolvePHP branch model. -- Work only on the requested phase and task. +## Scope Discipline + +- Work only on the requested task. - Do not add adjacent features because they appear useful. -- Keep commits and changes small and reviewable. +- Keep changes small and reviewable. - Identify assumptions before implementation. - Stop and report when a required dependency or decision is missing. -## 2. Pre-implementation audit +## Pre-Change Audit -Before code changes, every task must include: +Before modifying files, inspect the relevant files and summarize: -- Relevant-file inspection -- Current-behaviour summary -- Risks and dependencies -- Proposed files to modify -- Test plan -- Explicit non-goals +- current behavior; +- risks and dependencies; +- proposed files to modify; +- test plan; +- explicit non-goals. -## 3. Test-driven development +## Test-Driven Changes -All behavioural implementation must use: +Behavioral implementation must follow: ```text RED -> GREEN -> REFACTOR ``` -Rules: - -- Write or update tests before implementation. -- Demonstrate that the test initially fails for the expected reason. -- Implement the smallest change that makes it pass. -- Refactor only after the tests pass. -- Run targeted tests and the relevant full suite. -- Never delete, weaken or bypass a valid test just to obtain a passing result. -- Documentation, configuration and architecture changes should receive automated policy or validation tests where reasonably possible. +Write or update tests before implementation, demonstrate the expected failing result, make the smallest passing change and refactor only after the tests pass. Do not delete, weaken or bypass a valid test just to obtain a passing result. Documentation, configuration and architecture changes should receive automated policy or validation tests where reasonably possible. -## 4. Legacy preservation +## Legacy Preservation -- `master` represents the EvolvePHP 1 legacy line. -- `master` is reserved for preserved EvolvePHP 1 history and explicitly approved legacy maintenance. +- `master` preserves the EvolvePHP 1 legacy line. - EvolvePHP 1 maintenance must be clearly requested. -- An approved legacy-maintenance task must start from `master`, then continue on its own task-specific branch. -- Do not modernise EvolvePHP 1 unless a task explicitly targets legacy maintenance. -- Do not silently fix historical issues during documentation work. -- Preserve historical evidence and clearly document limitations. -- EvolvePHP 2 development must occur separately from the preserved legacy baseline. -- EvolvePHP 2 work is based on the current `2.x` branch. +- An approved legacy-maintenance task must start from `master` and continue on a task-specific branch. +- Do not modernize EvolvePHP 1 unless the task explicitly targets legacy maintenance. +- Preserve historical evidence and document limitations clearly. +- EvolvePHP 2 development is based on the current `2.x` branch. - EvolvePHP 2 changes must never be merged into `master`. -- EvolvePHP 1 changes must not be silently mixed into `2.x`. +- EvolvePHP 1 changes must not be mixed into `2.x`. -## 5. Branch safety +## Branch Safety -- Do not work directly on `master`; direct work on `master` remains prohibited unless the task explicitly targets approved legacy maintenance, and approved legacy maintenance still requires a task-specific branch. +- Do not work directly on `master`; direct work on `master` is prohibited unless the task explicitly targets approved legacy maintenance, and even then it must continue on a task-specific branch. - Confirm a clean working tree before editing. - Verify the requested base branch and exact base SHA before editing. -- Agents must not infer the correct base branch from GitHub's current default branch. +- Contributors and automation tools must not infer the correct base branch from GitHub's current default branch. - Use a task-specific branch. -- Do not push, merge, tag or open a pull request unless explicitly requested. +- Do not push, merge, tag or open a pull request unless explicitly requested; publication also requires explicit approval. - Never rewrite shared history. - Report unrelated pre-existing changes instead of overwriting them. -## 6. Public APIs and architecture +## Public APIs and Architecture - Public contracts require deliberate review. - Breaking changes require an RFC or explicit approval. - Prefer composition over inheritance. - Prefer dependency injection over service location. -- Prefer explicit behaviour over hidden magic. +- Prefer explicit behavior over hidden magic. - Respect module ownership and dependency boundaries. - Infrastructure implementations must remain replaceable through contracts. +- Core must not depend on optional outward packages. +- Production packages must not depend on Testing or DevTools. +- Package dependencies are declared through Composer only. +- Bridge remains outside Core. -## 7. Database migrations +## Database Migrations - Do not modify an already released or applied migration. - When a migration needs correction, create a new repair or follow-up migration. - Migration changes require tests and rollback consideration. -## 8. Security +## Security - Do not expose secrets, credentials, tokens or private information. -- Do not weaken validation, authorisation, CSRF, CORS or output escaping. +- Do not weaken validation, authorization, CSRF, CORS or output escaping. - Security-sensitive changes require negative-path tests. -- Never hide a discovered security risk merely to keep the task small; document it and keep unrelated remediation out of scope. +- Document discovered security risks even when remediation is out of scope. -## 9. Dependencies +## Dependencies - Do not add, remove or upgrade dependencies without explaining the need. - Prefer existing standards and packages over custom implementations. -- Dependency changes require compatibility and licence review. -- Do not modernise legacy dependencies during preservation-only tasks. +- Dependency changes require compatibility and license review. +- Do not modernize legacy dependencies during preservation-only work. -## 10. Observability and performance +## Observability and Performance - Do not make performance claims without reproducible measurements. -- Add telemetry to important framework lifecycle operations where required by the roadmap. +- Add telemetry to important framework lifecycle operations only when required by accepted design. - Avoid logging secrets or sensitive payloads. -- Persistent-worker changes must test state reset and memory behaviour. +- Persistent-worker changes must test state reset and memory behavior. -## 11. Documentation +## Documentation -- Documentation must match implemented behaviour. +- Documentation must match implemented behavior. - Do not claim features, compatibility, security or performance without evidence. - Include commands that were actually executed. - Clearly identify historical claims, maintainer-reported facts and independently verified facts. +- Keep public documentation tool-neutral and durable. -## 12. Completion evidence +## Completion Evidence -Every final agent report must include: +Every final agent report must include the same contribution evidence expected from any automated repository work: 1. Audit summary 2. Files created @@ -122,5 +117,3 @@ Every final agent report must include: 8. Remaining risks 9. Deferred work 10. Confirmation that no out-of-scope files were changed - -A statement such as "tests passed" without command and result evidence is insufficient. diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 665ec23..92f5416 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -136,11 +136,11 @@ Run deterministic/offline package release-readiness validation: composer release:validate ``` -Phase 2.10A keeps the release packages mapped explicitly in `release-packages.json`. Phase 6.4E extends that dependency-compatible map to seven packages by appending DevTools after the existing six-package order: contracts, core, module, plugin, http, testing and dev-tools. Package-local README and licence files exist so future split roots carry consumer documentation and legal text naturally. Package-local licences must remain identical to root `LICENSE.md`. +The release packages are mapped explicitly in `release-packages.json`. The dependency-compatible map contains seven packages in this order: contracts, core, module, plugin, http, testing and dev-tools. Package-local README and licence files exist so future split roots carry consumer documentation and legal text naturally. Package-local licences must remain identical to root `LICENSE.md`. No package is being published by this command. No remote repositories are contacted, no tags/releases are created, and no split repositories are synchronized. Package Composer manifests remain authoritative for package metadata. -`release:validate` is distinct from `quality`. It is also distinct from network-dependent `supply-chain`. Package splitting is Phase 2.10B validation work, and prerelease consumer stability is validated by the Phase 2.10B consumer matrix. RFC 0003 remains authoritative for release and version policy. +`release:validate` is distinct from `quality`. It is also distinct from network-dependent `supply-chain`. Package splitting is validated by `release:split:validate`, and prerelease consumer stability is validated by the offline consumer matrix. RFC 0003 remains authoritative for release and version policy. ### Package Split Validation @@ -229,7 +229,7 @@ Dependabot alerts and Dependabot security updates are GitHub settings. `.github/ ## VS Code Developer Experience -VS Code is optional developer tooling, not framework runtime configuration. The repository root is the VS Code workspace; no separate `.code-workspace` file is required for this phase. +VS Code is optional developer tooling, not framework runtime configuration. The repository root is the VS Code workspace; no separate `.code-workspace` file is required. The committed recommendations are intentionally minimal: EditorConfig for shared editor whitespace policy and Intelephense for PHP 8.4 language-analysis support. The Intelephense setting targets PHP 8.4 syntax and symbols for editor feedback, but it does not replace actual PHP 8.4+ execution. @@ -338,7 +338,7 @@ packages/plugin/src packages/testing/src ``` -Package tests are excluded from Phase 2.5 Deptrac boundary analysis so test dependencies cannot weaken production rules. Physical package paths define layers, and package namespaces must match the package paths: +Package tests are excluded from Deptrac boundary analysis so test dependencies cannot weaken production rules. Physical package paths define layers, and package namespaces must match the package paths: ```text Contracts -> packages/contracts/src/.* -> Evolve\Contracts\ @@ -378,7 +378,7 @@ PsrHttpMessage PsrHttpServer ``` -`PsrContainer` represents the approved PSR-11 interoperability layer used by Core and, starting in Phase 5.4, by Contracts specifically for the public `ServiceDefinitionRegistrar` service-definition factory contract. Contracts remains first-party-inward and has no first-party EvolvePHP dependency; the PSR-11 reference documents the optional resolver argument accepted by component service-definition factories and does not make Contracts a container implementation. Core remains the implementation owner for the registry, frozen resolver, execution scopes and restricted registration coordinator. `PsrHttpMessage` represents the approved `Psr\Http\Message` namespace used by Http for PSR-7 message interfaces and PSR-17 factory interfaces, including `psr/http-message` and `psr/http-factory`. `PsrHttpServer` represents the approved PSR-15 server middleware/handler interface layer used by Http. +`PsrContainer` represents the approved PSR-11 interoperability layer used by Core and by Contracts for the public `ServiceDefinitionRegistrar` service-definition factory contract. Contracts remains first-party-inward and has no first-party EvolvePHP dependency; the PSR-11 reference documents the optional resolver argument accepted by component service-definition factories and does not make Contracts a container implementation. Core remains the implementation owner for the registry, frozen resolver, execution scopes and restricted registration coordinator. `PsrHttpMessage` represents the approved `Psr\Http\Message` namespace used by Http for PSR-7 message interfaces and PSR-17 factory interfaces, including `psr/http-message` and `psr/http-factory`. `PsrHttpServer` represents the approved PSR-15 server middleware/handler interface layer used by Http. These PSR HTTP interfaces are external interoperability standards and do not change the first-party Evolve package dependency direction. Adding `psr/http-factory` does not require a new Deptrac external namespace layer because PSR-17 factory interfaces live under `Psr\Http\Message`. Http still depends inward on Contracts and Core, while the other first-party packages do not receive direct PSR HTTP access in this foundation. @@ -434,9 +434,9 @@ The root quality matrix runs PHP 8.4 and PHP 8.5. Each matrix entry validates Co composer quality ``` -The initial Phase 2.6 CI matrix has successfully executed. Root quality passes on PHP 8.4 and PHP 8.5, and the root policy job passes on PHP 8.4 for the current tooling and package foundation. This evidence applies to the current workspace, tooling and package foundation only. +The current CI matrix has successfully executed. Root quality passes on PHP 8.4 and PHP 8.5, and the root policy job passes on PHP 8.4 for the current tooling and package foundation. This evidence applies to the current workspace, tooling and package foundation only. -PHP 8.5 evidence for the current root quality pipeline is recorded by the Phase 2.6 CI matrix. +PHP 8.5 evidence for the current root quality pipeline is recorded by the CI matrix. The EvolvePHP 2 runtime implementation is incomplete, so this is not a broader runtime-production compatibility claim. @@ -446,7 +446,7 @@ Action dependencies are pinned by immutable full-SHA references. The reviewed re ## Compatibility Evidence -EvolvePHP 2 requires PHP 8.4 as the baseline. The Phase 2.6 CI matrix has successfully executed in GitHub Actions: the current root quality pipeline passes on PHP 8.4 and PHP 8.5, and the root policy job passes on PHP 8.4. +EvolvePHP 2 requires PHP 8.4 as the baseline. The current CI matrix has successfully executed in GitHub Actions: the current root quality pipeline passes on PHP 8.4 and PHP 8.5, and the root policy job passes on PHP 8.4. This verifies the current workspace, tooling and package foundation only. The preserved EvolvePHP 1 runtime is excluded, and the EvolvePHP 2 runtime implementation remains incomplete. @@ -454,9 +454,8 @@ This verifies the current workspace, tooling and package foundation only. The pr The following work remains deferred: -- committed-ref package split validation after a reviewed Phase 4.7 commit - package publication, tags and GitHub releases -- Runtime framework implementation beyond the completed Phase 4 HTTP package foundation -- Phase 5 module/plugin runtime work +- Runtime framework implementation beyond the completed HTTP package foundation +- full Module/Plugin runtime managers - broader developer tooling beyond `module:new` and `plugin:new` -- Phase 6.4F broader Testing utilities beyond the command-output recorder +- broader Testing utilities beyond the command-output recorder diff --git a/README.md b/README.md index 58ed10e..b6e785e 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,9 @@ `2.x` is the designated EvolvePHP 2 development branch and the GitHub default branch. -`master` remains the preserved EvolvePHP 1 legacy branch for historical reference and explicitly approved legacy maintenance. EvolvePHP 2 development and proposed changes must not target `master`. +`master` remains the preserved EvolvePHP 1 legacy branch for historical reference and explicitly approved legacy maintenance. EvolvePHP 2 changes must not target `master`. -EvolvePHP 2 is a separate redesign, not an in-place refactor, replacement or rewrite of the EvolvePHP 1 runtime history. - -Phase 4.7 promoted the EvolvePHP 2 Composer, PHPUnit, PHPStan, PHP-CS-Fixer, Deptrac, release-validation and supply-chain development root from `workspace/` to the repository root. The root project is not a release package. That root cutover does not delete `master`, does not delete history and does not change the preserved EvolvePHP 1 line. +EvolvePHP 2 is a separate redesign, not an in-place refactor, replacement or rewrite of the EvolvePHP 1 runtime history. The repository root is the EvolvePHP 2 Composer, PHPUnit, PHPStan, PHP-CS-Fixer, Deptrac, release-validation and supply-chain development root. The root project is not a release package, and this root layout does not delete `master`, delete history or change the preserved EvolvePHP 1 line. ## Project Overview @@ -21,7 +19,7 @@ EvolvePHP 2 requires PHP 8.4. The current root quality pipeline is verified by G - EvolvePHP 2 development branch: `2.x` - GitHub default branch: `2.x` - Legacy line: EvolvePHP 1 on `master` -- Required `2.x` checks: `Policy (PHP 8.4)`, `Workspace quality (PHP 8.4)`, `Workspace quality (PHP 8.5)` +- Required `2.x` checks: `Policy (PHP 8.4)`, `Workspace quality (PHP 8.4)`, `Workspace quality (PHP 8.5)`, `Benchmark policy (PHP 8.4)` - Runtime implementation: not yet complete - Release status: pre-release, not production-ready - Package publication: packages are not yet independently published @@ -32,13 +30,13 @@ EvolvePHP 2 requires PHP 8.4. The current root quality pipeline is verified by G ## Branch Governance -Phase 2.7B completed the external governance transition without replacing history. Repository rulesets actively protect both branch lines: `master` remains preserved, and `2.x` receives default-branch development changes. +Repository rulesets actively protect both branch lines: `master` remains preserved, and `2.x` receives default-branch development changes. The `master` ruleset requires pull requests, blocks deletion, blocks force-pushes, keeps required approvals at zero, requires conversation resolution and has no bypass actors. -The `2.x` ruleset requires pull requests, blocks deletion, blocks force-pushes, enforces required CI status checks with strict up-to-date status-check policy, keeps required approvals at zero, requires conversation resolution and has no bypass actors. +The `2.x` ruleset requires pull requests, blocks deletion, blocks force-pushes, enforces required CI status checks with strict up-to-date status-check policy, keeps required approvals at zero, requires conversation resolution and has no bypass actors. The required checks are `Policy (PHP 8.4)`, `Workspace quality (PHP 8.4)`, `Workspace quality (PHP 8.5)` and `Benchmark policy (PHP 8.4)`. -`master` has not been renamed or deleted, no branch was renamed or deleted, no `main` branch has been created, and no `1.x` branch has been created. Any stable-release branch rename or promotion remains deferred. Phase 2.7 does not replace the legacy history. +`master` has not been renamed or deleted, no branch was renamed or deleted, no `main` branch has been created, and no `1.x` branch has been created. Any stable-release branch rename or promotion remains deferred. The legacy history is preserved. ## Requirements @@ -54,7 +52,8 @@ The preserved EvolvePHP 1 line has different historical requirements. Use `maste evolvephp/ |-- docs/history/ # Preserved EvolvePHP 1 historical documentation |-- docs/rfcs/ # Accepted EvolvePHP 2 RFCs and governance index -|-- packages/ # Six EvolvePHP 2 package boundaries +|-- packages/ # EvolvePHP 2 package boundaries +|-- skeleton/ # End-user application template |-- tests/ # Architecture and documentation policy tests |-- tools/ # Root release and supply-chain validation tools |-- composer.json # EvolvePHP 2 root development manifest @@ -63,7 +62,7 @@ evolvephp/ `-- README.md # EvolvePHP 2 branch entry point ``` -The EvolvePHP 1 runtime files are not present in the `2.x` working tree after Phase 4.7. They remain preserved on `master` and in Git history. +The EvolvePHP 1 runtime files are not present in the `2.x` working tree. They remain preserved on `master` and in Git history. ## Getting Started @@ -83,7 +82,7 @@ Use [DEVELOPMENT.md](DEVELOPMENT.md) for detailed Composer, PHPUnit, PHPStan, PH The current EvolvePHP 2 package set is documented in [packages/README.md](packages/README.md). -The package architecture uses explicit namespace ownership and inward dependency direction. Runtime contracts and framework behavior are added through approved implementation phases. +The package architecture uses explicit namespace ownership and inward dependency direction. Runtime contracts and framework behavior are added through reviewed implementation work. ## EvolvePHP 1 History diff --git a/packages/README.md b/packages/README.md index 1a326fa..111ba40 100644 --- a/packages/README.md +++ b/packages/README.md @@ -2,7 +2,7 @@ `packages/` contains the initial EvolvePHP 2 modular-monorepo package set. -The packages define Composer package identities, namespace ownership, dependency direction, the first Phase 3 lifecycle, configuration, service-container, execution-scope, runtime-neutral execution orchestration, generic Core instrumentation and minimal Core console foundations, plus the Phase 4.1 PSR HTTP middleware foundation, Phase 4.2 routing foundation, Phase 4.3 routed handler dispatch foundation, Phase 4.4 HTTP execution-kernel integration, Phase 4.5 HTTP response/error and health foundation, Phase 4.6 `ResponseEmitter` runtime-neutral response-emission boundary and integration acceptance, Phase 5.1 Component Identity Foundation, the Phase 5.2 Module/Plugin immutable descriptor foundation, the Phase 5.3A dependency/capability graph declaration vocabulary, the Phase 5.3B Core-owned graph validation/resolution foundation, the Phase 5.4 restricted component service-definition registration foundation, the Phase 5.5 component lifecycle entry-point/orchestration foundation, the Phase 5.6A explicit component definition/bootstrap foundation, the Phase 5.6B Composer plugin discovery foundation, the Phase 5.7 Testing component fixture and independent acceptance foundation, and the Phase 6.4E/6.4F development-time skeleton generator and command-output recorder for EvolvePHP 2. Complete runtime implementation is not yet present for the framework, and the packages are not yet published. +The packages define Composer package identities, namespace ownership, dependency direction, lifecycle, configuration, service-container, execution-scope, runtime-neutral execution orchestration, generic Core instrumentation, minimal Core console APIs, PSR HTTP middleware and routing, response/error and health handling, explicit response emission, component identity, descriptors, graph declarations and resolution, restricted registration, lifecycle entry points, explicit component bootstrap, Composer plugin discovery, testing fixtures, development-time generators and command-output recording. Complete runtime implementation is not yet present, and the packages are not yet published. All package manifests require PHP `^8.4`. @@ -10,13 +10,13 @@ All package manifests require PHP `^8.4`. | Package | Namespace | Responsibility | | --- | --- | --- | -| `evolvephp/contracts` | `Evolve\Contracts\` | Foundational public-contract boundary, including the initial application lifecycle, configuration, reset-participant and exception contracts plus the Phase 5.1 experimental shared identity vocabulary, Phase 5.3A graph declaration vocabulary, Phase 5.4 experimental `ServiceDefinitionRegistrar` contribution contract, Phase 5.5 experimental `ComponentEntryPoint` / `ComponentBootContext` lifecycle contracts and Phase 5.6A experimental `ComponentDefinition` bridge. | -| `evolvephp/core` | `Evolve\Core\` | Core orchestration boundary, including the initial minimal application lifecycle kernel, array-backed configuration implementation, PSR-11-readable service container foundation, explicit execution scopes, runtime-neutral execution orchestration outcomes, generic execution-lifecycle observation hooks, the minimal runtime-neutral command foundation, the Phase 5.3B experimental `ComponentGraphResolver`, `ResolvedComponentGraph` and consumer-scoped `CapabilityProviderSelection` graph resolution APIs, the Phase 5.4 internal restricted registration coordinator, Phase 5.5 internal component lifecycle coordinator and Phase 5.6A explicit `ComponentBootstrapper`. | +| `evolvephp/contracts` | `Evolve\Contracts\` | Foundational public-contract boundary, including application lifecycle, configuration, reset-participant and exception contracts, experimental shared identity vocabulary, graph declaration vocabulary, `ServiceDefinitionRegistrar`, `ComponentEntryPoint` / `ComponentBootContext` lifecycle contracts and `ComponentDefinition`. | +| `evolvephp/core` | `Evolve\Core\` | Core orchestration boundary, including the minimal application lifecycle kernel, array-backed configuration, PSR-11-readable service container, explicit execution scopes, runtime-neutral execution outcomes, generic execution-lifecycle observation hooks, runtime-neutral command foundation, `ComponentGraphResolver`, `ResolvedComponentGraph`, consumer-scoped `CapabilityProviderSelection`, restricted registration, component lifecycle coordination and explicit `ComponentBootstrapper`. | | `evolvephp/dev-tools` | `Evolve\DevTools\` | Development-only generator boundary with public experimental `module:new` and `plugin:new` command adapters for application-owned module and plugin starter files. | -| `evolvephp/http` | `Evolve\Http\` | HTTP boundary with the Phase 4.1 PSR HTTP interoperability and `MiddlewarePipeline` foundation, Phase 4.2 route definitions and matching, Phase 4.3 routed handler dispatch with typed routing failures, Phase 4.4 `HttpKernel` integration with Core execution orchestration, Phase 4.5 response/error plus health foundation and Phase 4.6 explicit response-emitter boundary; runtime adapters remain deferred. | -| `evolvephp/module` | `Evolve\Module\` | Module SDK boundary with the public experimental `ModuleDescriptor`, EvolvePHP-major compatibility validation foundation, Phase 5.3A `graphDeclaration()` projection, Phase 5.5 `Module` entry point extending `ComponentEntryPoint` and Phase 5.6A explicit `ModuleDefinition`; discovery remains deferred. | -| `evolvephp/plugin` | `Evolve\Plugin\` | Plugin SDK boundary with the public experimental `PluginDescriptor`, EvolvePHP-major compatibility validation foundation, Phase 5.3A `graphDeclaration()` projection, Phase 5.5 `Plugin` entry point extending `ComponentEntryPoint`, Phase 5.6A explicit `PluginDefinition` and Phase 5.6B `ComposerPluginDiscovery` for packaged plugin metadata. | -| `evolvephp/testing` | `Evolve\Testing\` | Development-only testing-support boundary, including Phase 5.7 public experimental component fixtures, Phase 6.4F public experimental command-output recorder and independent Phase 5 acceptance coverage. | +| `evolvephp/http` | `Evolve\Http\` | HTTP boundary with PSR HTTP interoperability, `MiddlewarePipeline`, route definitions and matching, routed handler dispatch, typed routing failures, `HttpKernel` integration with Core execution orchestration, response/error and health foundations and explicit response-emitter boundary; runtime adapters remain deferred. | +| `evolvephp/module` | `Evolve\Module\` | Module SDK boundary with the public experimental `ModuleDescriptor`, EvolvePHP-major compatibility validation, `graphDeclaration()` projection, `Module` entry point extending `ComponentEntryPoint` and explicit `ModuleDefinition`; discovery remains deferred. | +| `evolvephp/plugin` | `Evolve\Plugin\` | Plugin SDK boundary with the public experimental `PluginDescriptor`, EvolvePHP-major compatibility validation, `graphDeclaration()` projection, `Plugin` entry point extending `ComponentEntryPoint`, explicit `PluginDefinition` and `ComposerPluginDiscovery` for packaged plugin metadata. | +| `evolvephp/testing` | `Evolve\Testing\` | Development-only testing-support boundary, including public experimental component fixtures, public experimental command-output recorder and independent component acceptance coverage. | ## Dependency Direction @@ -34,58 +34,60 @@ There is no production dependency on Testing. No optional package families are present here. Insight, Observe, OpenTelemetry, Bridge, Runtime, Deploy and other optional packages remain deferred to later approved work. Runtime adapters are deferred; Core does not contain `runtime-cli`. -## Current Limitations +## Current Capabilities And Limits -Contracts and Core now contain the first Phase 3 lifecycle, configuration, service-container, execution-scope and orchestration foundations: a narrow application boot/shutdown contract, public lifecycle and configuration exception catch boundaries, read-only configuration lookup contracts, a small validation contract, an explicit reset-participant contract, an immutable Core array-backed configuration implementation, deterministic boot-time validation before readiness, explicit service-registry freezing before readiness and explicit execution scopes created only after successful freeze. +Contracts and Core contain lifecycle, configuration, service-container, execution-scope and orchestration foundations: a narrow application boot/shutdown contract, public lifecycle and configuration exception catch boundaries, read-only configuration lookup contracts, a small validation contract, an explicit reset-participant contract, an immutable Core array-backed configuration implementation, deterministic boot-time validation before readiness, explicit service-registry freezing before readiness and explicit execution scopes created only after successful freeze. Configuration values are application-supplied scalar, null or recursive-array data. Dot-path lookup is supported for associative maps, missing values remain distinct from explicit null values, and validator failure makes that kernel instance terminal; construct a new kernel to retry corrected startup. -Core provides a restricted `ServiceRegistry` bootstrap API for explicit service registration, an idempotent freeze boundary, application-lifetime caching, execution-lifetime caching per explicit scope, transient resolution and PSR-11 read-only `has()`/`get()` interoperability. The root resolver reports known Execution definitions through `has()` but refuses to construct them through root `get()`; execution services resolve only through explicit execution scopes. Application factories always receive the root resolver, Execution factories receive the current scope resolver, and Transient factories receive whichever resolver performed the read. This prevents Application services from capturing shorter-lived Execution services while allowing Execution-to-Application, Execution-to-Transient and scoped Transient-to-Execution resolution. +Core provides a restricted `ServiceRegistry` bootstrap API for explicit service registration, an idempotent freeze boundary, application-lifetime caching, execution-lifetime caching per explicit scope, transient resolution and PSR-11 read-only `has()`/`get()` interoperability. The root resolver reports known Execution definitions through `has()` but refuses to construct them through root `get()`; execution services resolve only through explicit execution scopes. Application factories always receive the root resolver, Execution factories receive the current scope resolver, and Transient factories receive whichever resolver performed the read. Execution scopes expose explicit per-scope `ResetParticipant` registration, reset participants in reverse successful registration order, aggregate reset failures after all participants have been attempted, close terminally and idempotently, and release execution-local references during close even when reset fails. Reset participation is not automatic disposal and is not discovered by scanning services. -Core now provides a runtime-neutral `ExecutionOrchestrator` foundation for one sequential unit of work. Each orchestration creates a locally generated opaque execution identifier, classifies the execution kind, passes an explicit immutable execution context and execution scope to the operation, captures the primary handler result or throwable, closes the scope, records cleanup/reset failure separately and reports whether the process remains reusable or must be quarantined. Handler failure alone does not imply quarantine; cleanup/reset failure requires fail-closed quarantine. +Core provides a runtime-neutral `ExecutionOrchestrator` foundation for one sequential unit of work. Each orchestration creates a locally generated opaque execution identifier, classifies the execution kind, passes an explicit immutable execution context and execution scope to the operation, captures the primary handler result or throwable, closes the scope, records cleanup/reset failure separately and reports whether the process remains reusable or must be quarantined. -Core now provides generic execution-lifecycle observation hooks for the implemented orchestration boundary. Observation is optional, observations contain only safe structured execution facts, sink failure is reported separately as instrumentation failure and instrumentation cannot retry, abort, replace results, suppress cleanup or change reuse/quarantine decisions. +Core provides generic execution-lifecycle observation hooks for the implemented orchestration boundary. Observation is optional, observations contain only safe structured execution facts, sink failure is reported separately as instrumentation failure and instrumentation cannot retry, abort, replace results, suppress cleanup or change reuse/quarantine decisions. -Core now provides a minimal Core console foundation for command selection and dispatch. `CommandRunner` resolves a command from an immutable `CommandRegistry` and executes it through `ExecutionOrchestrator` as an `ExecutionKind::CliCommand` execution. `CommandInput` stores raw ordered token data only, `CommandOutput` remains runtime-neutral, and `CommandResult` carries exit-status semantics without turning non-zero statuses into framework throwables. +Core provides a minimal Core console foundation for command selection and dispatch. `CommandRunner` resolves a command from an immutable `CommandRegistry` and executes it through `ExecutionOrchestrator` as an `ExecutionKind::CliCommand` execution. `CommandInput` stores raw ordered token data only, `CommandOutput` remains runtime-neutral, and `CommandResult` carries exit-status semantics without turning non-zero statuses into framework throwables. -HTTP now contains the Phase 4.1 PSR HTTP interoperability and deterministic middleware pipeline foundation. `MiddlewarePipeline` implements PSR-15 `RequestHandlerInterface`, accepts ordered PSR-15 middleware, consumes PSR-7 request/response interfaces and preserves middleware ordering, short-circuiting, replacement requests and throwable propagation. +HTTP contains PSR HTTP interoperability and a deterministic middleware pipeline foundation. `MiddlewarePipeline` implements PSR-15 `RequestHandlerInterface`, accepts ordered PSR-15 middleware, consumes PSR-7 request/response interfaces and preserves middleware ordering, short-circuiting, replacement requests and throwable propagation. -HTTP also contains the Phase 4.2 routing foundation. `Route`, `RouteCollection`, `RouteMatch` and `RouteMatcher` provide immutable route-definition data, ordered route collections, exact method matching, whole-segment `{parameter}` path templates, raw non-decoded parameter capture, deterministic insertion-order matching and `allowedMethods()` path metadata. Route handlers are stored but not invoked by matching. +HTTP contains route definitions and matching. `Route`, `RouteCollection`, `RouteMatch` and `RouteMatcher` provide immutable route-definition data, ordered route collections, exact method matching, whole-segment `{parameter}` path templates, raw non-decoded parameter capture, deterministic insertion-order matching and `allowedMethods()` path metadata. Route handlers are stored but not invoked by matching. -HTTP now contains the Phase 4.3 routed handler dispatch foundation. `RoutingRequestHandler` implements PSR-15 `RequestHandlerInterface`, invokes the matched route handler after a successful `RouteMatcher` match, exposes the authoritative `RouteMatch` through the `RouteMatch::class` request attribute key, supports ordered post-match middleware and throws typed `RouteNotFound` or `MethodNotAllowed` routing boundaries without constructing 404 or 405 responses. +HTTP contains routed handler dispatch. `RoutingRequestHandler` implements PSR-15 `RequestHandlerInterface`, invokes the matched route handler after a successful `RouteMatcher` match, exposes the authoritative `RouteMatch` through the `RouteMatch::class` request attribute key, supports ordered post-match middleware and throws typed `RouteNotFound` or `MethodNotAllowed` routing boundaries without constructing 404 or 405 responses. -HTTP now contains the Phase 4.4 HTTP execution-kernel integration. `HttpKernel` wraps an already-composed PSR-15 handler, delegates each request to `ExecutionOrchestrator` as an `ExecutionKind::HttpRequest`, attaches authoritative request-local `ExecutionContext` and `ExecutionScope` attributes, returns `ExecutionOutcome`, preserves primary response/throwable identity and leaves cleanup, instrumentation and reuse/quarantine decisions in Core. +HTTP contains HTTP execution-kernel integration. `HttpKernel` wraps an already-composed PSR-15 handler, delegates each request to `ExecutionOrchestrator` as an `ExecutionKind::HttpRequest`, attaches authoritative request-local `ExecutionContext` and `ExecutionScope` attributes, returns `ExecutionOutcome`, preserves primary response/throwable identity and leaves cleanup, instrumentation and reuse/quarantine decisions in Core. -HTTP now contains the Phase 4.5 response/error and health foundation. `ExecutionOutcomeResponseResolver` maps HTTP `ExecutionOutcome` values after `HttpKernel` returns, preserving the original primary response or throwable plus cleanup, instrumentation and reuse/quarantine state. It returns exact successful responses, maps `RouteNotFound` to empty 404, maps `MethodNotAllowed` to empty 405 with exact `Allow` metadata and maps other primary throwables to empty 500. `LivenessHandler`, `ReadinessCheck` and `ReadinessHandler` provide explicit bodyless health-handler building blocks without automatic route registration. +HTTP contains response/error and health handling. `ExecutionOutcomeResponseResolver` maps HTTP `ExecutionOutcome` values after `HttpKernel` returns, preserving the original primary response or throwable plus cleanup, instrumentation and reuse/quarantine state. It returns exact successful responses, maps `RouteNotFound` to empty 404, maps `MethodNotAllowed` to empty 405 with exact `Allow` metadata and maps other primary throwables to empty 500. `LivenessHandler`, `ReadinessCheck` and `ReadinessHandler` provide explicit bodyless health-handler building blocks without automatic route registration. -HTTP now contains the Phase 4.6 runtime-neutral response-emission boundary and integration acceptance. `ResponseEmitter` marks the exact separation between response resolution and emission: `HttpKernel` returns `ExecutionOutcome`, `ExecutionOutcomeResponseResolver` returns `ResponseInterface`, and an outer caller explicitly invokes the emitter. Runtime owns concrete transmission, and Runtime owns quarantine, recycle and termination ordering after inspecting the separately retained outcome. The HTTP package has no concrete SAPI emitter and still has no concrete PSR-7 implementation. +HTTP contains the runtime-neutral response-emission boundary. `ResponseEmitter` marks the exact separation between response resolution and emission: `HttpKernel` returns `ExecutionOutcome`, `ExecutionOutcomeResponseResolver` returns `ResponseInterface`, and an outer caller explicitly invokes the emitter. Runtime owns concrete transmission, and Runtime owns quarantine, recycle and termination ordering after inspecting the separately retained outcome. The HTTP package has no concrete SAPI emitter and still has no concrete PSR-7 implementation. -Contracts now contain the Phase 5.1 Component Identity Foundation. `ComponentIdentifier` and `ComponentType` provide experimental shared identity vocabulary for future Module and Plugin metadata while preserving explicit caller-supplied identifiers without normalization. Phase 5.1 left descriptors deferred, Module/Plugin entry points not implemented, discovery deferred, registration deferred and boot/ready/shutdown behavior deferred. +The HTTP package foundation is complete, but the complete production runtime is still deferred. EvolvePHP 2 does not yet provide complete HTTP runtime adapters, concrete runtime adapters, concrete PSR-7 implementation, SAPI response transmission, SAPI request creation, HTML or JSON error rendering, content negotiation, debug pages, automatic health routes, environment or dotenv loading, configuration files, queue or scheduled-job adapters, retry policy, process recycling or termination, trace propagation or OpenTelemetry propagation. -Phase 5.2 adds package-owned public experimental immutable descriptor types: `Evolve\Module\ModuleDescriptor` and `Evolve\Plugin\PluginDescriptor`. Each descriptor reuses `ComponentIdentifier`, preserves the exact accepted human-readable name, reports its hard-coded `ComponentType`, exposes in-memory schema version `1` and declares a positive EvolvePHP major. `ModuleCompatibilityValidator` and `PluginCompatibilityValidator` perform explicit startup-time EvolvePHP-major compatibility validation for major `2` and raise typed package-owned compatibility failures for incompatible positive majors. This is not a complete lifecycle implementation. +Contracts contain experimental shared component identity vocabulary. `ComponentIdentifier` and `ComponentType` provide Module/Plugin identity vocabulary while preserving explicit caller-supplied identifiers without normalization. The identity value objects do not themselves implement descriptor behavior, discovery, registration or lifecycle callbacks. Module/Plugin entry points are implemented by separate lifecycle contracts and package SDKs; descriptors are separate package responsibilities, and registration, boot, ready and shutdown are Core lifecycle responsibilities. Module discovery remains deferred, while packaged Plugin Composer discovery exists separately through `ComposerPluginDiscovery`. -Phase 5.3A adds public experimental dependency/capability graph declaration vocabulary. `ComponentGraphDeclaration` and `ComponentGraphRelations` declare required or optional component dependencies, declarative conflicts, required capabilities with `ExactlyOne` or `OneOrMore` cardinality and provided capability identifiers. Module and Plugin descriptors expose `graphDeclaration()` while preserving existing three-argument construction. Relation ordering is canonical and deterministic, but declaration order has no startup-order semantics. +Module and Plugin provide package-owned public experimental immutable descriptor types: `Evolve\Module\ModuleDescriptor` and `Evolve\Plugin\PluginDescriptor`. Each descriptor reuses `ComponentIdentifier`, preserves the exact accepted human-readable name, reports its hard-coded `ComponentType`, exposes in-memory schema version `1` and declares a positive EvolvePHP major. `ModuleCompatibilityValidator` and `PluginCompatibilityValidator` perform explicit startup-time EvolvePHP-major compatibility validation for major `2` and raise typed package-owned compatibility failures for incompatible positive majors. This is not a complete lifecycle implementation; dependency resolution and graph validation are handled by Core graph resolution. -Phase 5.3B adds Core-owned graph validation and deterministic resolution while consuming only the Contracts graph declarations. The experimental `ComponentGraphResolver` validates duplicate active identifiers, missing required dependencies, active conflicts, capability-provider satisfaction, `ExactlyOne` ambiguity, consumer-scoped provider selection, `OneOrMore` provider sets and dependency cycles. It produces a `ResolvedComponentGraph` with deterministic dependency-first ordering and resolved provider lookups, uses effective-edge deduplication for repeated prerequisite-to-dependent relationships, and reports canonical cycle diagnostics. Required dependencies and present optional dependencies order prerequisites before consumers; absent optional dependencies remain nonfatal. Conflicts validate incompatibility but do not create ordering. The graph resolver remains separate from registration and boot. +Contracts provide public experimental dependency/capability graph declaration vocabulary. `ComponentGraphDeclaration` and `ComponentGraphRelations` declare required or optional component dependencies, declarative conflicts, required capabilities with `ExactlyOne` or `OneOrMore` cardinality and provided capability identifiers. Module and Plugin descriptors expose `graphDeclaration()` while preserving existing three-argument construction. Relation ordering is canonical and deterministic, but declaration order has no startup-order semantics. Declaration vocabulary does not itself resolve dependency graphs. -Phase 5.4 adds restricted service-definition contribution. Contracts exposes the public experimental `ServiceDefinitionRegistrar`, a contribution-only boundary for Application, Execution and Transient service definitions whose factories accept zero or one active PSR-11 resolver argument. Core owns the internal `ComponentRegistrationCoordinator`, exact `ComponentGraphDeclaration` object-identity binding, `ResolvedComponentGraph` ordering, staging, reserved service identifiers and atomic publication into `ServiceRegistry`. Registration failure publishes nothing from the staged batch, registration does not resolve or construct services, and successful registration does not freeze the registry. `ApplicationKernel` integration, Module/Plugin entry-point lifecycle, boot and ready were deferred from Phase 5.4 to Phase 5.5; explicit application-controlled enablement is introduced in Phase 5.6A while discovery remains deferred. +Core owns graph validation and deterministic resolution while consuming only the Contracts graph declarations. The experimental `ComponentGraphResolver` validates duplicate active identifiers, missing required dependencies, active conflicts, capability-provider satisfaction, `ExactlyOne` ambiguity, consumer-scoped provider selection, `OneOrMore` provider sets and dependency cycles. It produces a `ResolvedComponentGraph` with deterministic dependency-first ordering, resolved provider lookups and canonical cycle diagnostics. The graph resolver remains separate from registration and boot. Graph resolution does not itself perform registration or lifecycle work. -Phase 5.5 adds public experimental lifecycle entry points and Core-owned application-lifecycle orchestration. Contracts exposes `ComponentEntryPoint` with exactly `register`, `boot`, `ready` and `shutdown`, plus `ComponentBootContext` for application-lifecycle-only frozen PSR-11 access and boot-failure cleanup. Module and Plugin SDKs expose `Evolve\Module\Module` and `Evolve\Plugin\Plugin`, both extending `ComponentEntryPoint` without package-specific lifecycle methods. +Contracts expose the public experimental `ServiceDefinitionRegistrar`, a contribution-only boundary for Application, Execution and Transient service definitions whose factories accept zero or one active PSR-11 resolver argument. Core owns the restricted registrar implementation, exact `ComponentGraphDeclaration` object-identity binding, `ResolvedComponentGraph` ordering, staging, reserved service identifiers and staged atomic publication into `ServiceRegistry`. Registration failure publishes nothing, registration does not resolve or construct services, and successful registration does not freeze the registry. + +Contracts expose public experimental lifecycle entry points. `ComponentEntryPoint` has exactly `register`, `boot`, `ready` and `shutdown`, plus `ComponentBootContext` for application-lifecycle-only frozen PSR-11 access and boot-failure cleanup. Module and Plugin SDKs expose `Evolve\Module\Module` and `Evolve\Plugin\Plugin`, both extending `ComponentEntryPoint` without package-specific lifecycle methods. Core coordinates the deterministic lifecycle sequence `registration -> freeze -> boot -> ready` through `ApplicationKernel`, keeps Core independent of Module and Plugin packages, boots and readies dependency-first from `ResolvedComponentGraph`, and shuts down in reverse successful boot order. Boot failure runs deferred startup cleanup for the failing component, skips normal shutdown for that failed component, shuts down previously booted components best-effort and preserves the original primary failure. Ready failure shuts down all booted components. Normal shutdown aggregates attributed failures after all components are attempted. No request or per-execution component lifecycle work is introduced. -Phase 5.6A adds explicit component definitions and app-controlled enablement. Contracts exposes `ComponentDefinition`; Module and Plugin expose `ModuleDefinition` and `PluginDefinition`; Core exposes `ComponentBootstrapper` and accepts it through `ApplicationKernel`. Applications provide definition objects explicitly and enable component identifiers through the `evolve.components.enabled` configuration list. Absent enablement means zero active components, disabled definitions remain inert, enabled definitions validate before entry-point creation, and the existing graph resolver and Phase 5.5 lifecycle coordinator remain the ordering and lifecycle authorities. +Contracts, Module, Plugin and Core provide explicit component definitions and app-controlled enablement. Contracts exposes `ComponentDefinition`; Module and Plugin expose `ModuleDefinition` and `PluginDefinition`; Core exposes `ComponentBootstrapper` and accepts it through `ApplicationKernel`. Applications provide definition objects explicitly and enable component identifiers through the `evolve.components.enabled` configuration list. Absent enablement means zero active components, disabled definitions remain inert, enabled definitions validate before entry-point creation, and the existing graph resolver and lifecycle coordinator remain the ordering and lifecycle authorities. -Phase 5.6B adds packaged plugin metadata discovery inside `evolvephp/plugin`. The public experimental `ComposerPluginDiscovery` reads Composer 2 `vendor/composer/installed.json` only, recognizes `extra.evolvephp.plugin`, treats the Composer package name as the authoritative identifier, returns sorted `PluginDefinition` objects and keeps application-controlled enablement through `evolve.components.enabled`. Disabled discovered definitions remain inert; discovery does not instantiate entry points, check classes, inspect root composer.json modules, use Composer 1 metadata, access the network, add caching, or require the Composer runtime API. It uses installed.json only with no recursive package scanning and no new Composer dependency. +Plugin provides packaged plugin metadata discovery through `ComposerPluginDiscovery`. It reads Composer 2 `vendor/composer/installed.json` only, recognizes `extra.evolvephp.plugin`, treats the Composer package name as the authoritative identifier, returns sorted `PluginDefinition` objects and keeps application-controlled enablement through `evolve.components.enabled`. Disabled discovered definitions remain inert; discovery does not instantiate entry points, check classes, inspect root composer.json modules, use Composer 1 metadata, access the network, add caching, or require the Composer runtime API. It uses installed.json only with no recursive package scanning and no new Composer dependency. -Phase 5.7 adds the first concrete `evolvephp/testing` SDK surface for component tests. `ComponentDefinitionFixture` and `ComponentEntryPointFixture` are public experimental test fixtures that reuse the real component contracts while remaining simple closure-backed wrappers. The Testing package also carries an independent integration acceptance test covering explicit `ModuleDefinition`, `ComposerPluginDiscovery`, application-controlled enablement, Core `ComponentBootstrapper`, real dependency ordering, restricted registration, `ServiceRegistry` freeze, `ApplicationKernel` boot, component boot, ready and reverse shutdown. These fixtures do not replace Module or Plugin definitions, lifecycle orchestration, discovery, graph resolution, developer tooling, mocks or PHPUnit assertion APIs. +Testing provides concrete SDK surfaces for component tests. `ComponentDefinitionFixture` and `ComponentEntryPointFixture` are public experimental test fixtures that reuse the real component contracts while remaining simple closure-backed wrappers. The Testing package also carries independent integration acceptance coverage for explicit definitions, Composer plugin discovery, application-controlled enablement, Core bootstrap, dependency ordering, restricted registration, registry freeze, application boot, component boot, ready and reverse shutdown. These fixtures do not replace Module or Plugin definitions, lifecycle orchestration, discovery, graph resolution, developer tooling, mocks or PHPUnit assertion APIs. -Phase 6.4E adds `evolvephp/dev-tools` with public experimental `ModuleNewCommand` and `PluginNewCommand`. The commands accept an explicit project root and one ASCII StudlyCase name token, generate application-owned module or plugin starter files with deterministic `app/...` component identifiers, refuse invalid usage and existing targets, and do not run Composer, run Git, edit Composer manifests, discover components or auto-enable generated definitions. +DevTools provides public experimental `ModuleNewCommand` and `PluginNewCommand`. The commands accept an explicit project root and one ASCII StudlyCase name token, generate application-owned module or plugin starter files with deterministic `app/...` component identifiers, refuse invalid usage and existing targets, and do not run Composer, run Git, edit Composer manifests, discover components or auto-enable generated definitions. -Phase 6.4F adds `Evolve\Testing\Console\RecordingCommandOutput`, a public experimental in-memory `CommandOutput` implementation for command tests. It records normal and error lines in order without replacing PHPUnit assertions or becoming a general CLI testing framework. +Testing also provides `Evolve\Testing\Console\RecordingCommandOutput`, a public experimental in-memory `CommandOutput` implementation for command tests. It records normal and error lines in order without replacing PHPUnit assertions or becoming a general CLI testing framework. -The Phase 4 HTTP package foundation is complete after this slice, but the complete production runtime is still deferred. EvolvePHP 2 does not yet provide complete HTTP runtime adapters, concrete runtime adapters, concrete PSR-7 implementation, SAPI response transmission, SAPI request creation, HTML or JSON error rendering, content negotiation, debug pages, automatic health routes, environment or dotenv loading, configuration files, queue or scheduled-job adapters, retry policy, process recycling or termination, full module/plugin runtime, runtime CLI adapters beyond the explicit skeleton shell, Symfony Console integration, broad Doctor checks, generator discovery, automatic component enablement, Insight, Observe, OpenTelemetry propagation, tracing, metrics, logs, telemetry storage/export, persistent-worker concurrency guarantees or production-ready framework runtime behavior. These capabilities remain deferred. Phase 5.6B does not implement descriptor serialization, component versions, dependency version ranges or Composer semantic-version constraint evaluation. Phase 6.4E does not add automatic discovery, application bootstrapping, route generation, Composer mutation or package publication. Phase 6.4F does not add a general testing framework. +EvolvePHP 2 does not yet provide full module/plugin runtime managers, descriptor serialization, component versions, dependency version ranges, Composer semantic-version constraint evaluation, runtime CLI adapters beyond the explicit skeleton shell, Symfony Console integration, broad Doctor checks, generator discovery, automatic component enablement, Bridge, Insight, Observe, telemetry storage/export, persistent-worker concurrency guarantees or production-ready framework runtime behavior. DevTools does not add automatic discovery, application bootstrapping, route generation, Composer mutation or package publication. Testing does not add a general testing framework. The EvolvePHP 2 repository root resolves and validates these local packages. See [../DEVELOPMENT.md](../DEVELOPMENT.md) for setup, testing, quality commands, lockfile, static-analysis, coding-standard and architecture-boundary policy. diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 41b31e6..3af3542 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -4,7 +4,7 @@ Foundational public contracts for EvolvePHP 2. ## Public Contracts -Phase 3.4 extends the initial stable public contract surface: +The stable public contract surface includes: - `Evolve\Contracts\Lifecycle\ApplicationLifecycle` - `Evolve\Contracts\Configuration\Configuration` @@ -22,7 +22,7 @@ Configuration files, environment and dotenv loading, container access, service d ## Experimental Component Identity -Phase 5.1 adds experimental component identity vocabulary shared by future Module and Plugin metadata: +The package includes experimental component identity vocabulary shared by Module and Plugin metadata: - `Evolve\Contracts\Component\ComponentIdentifier` - `Evolve\Contracts\Component\ComponentType` @@ -41,11 +41,11 @@ alnum = lowercase ASCII letter | ASCII digit The grammar allows either `side` or `side/side`, with at most one `/`. Each side is non-empty, starts and ends with lowercase ASCII alphanumeric characters, and may contain lowercase ASCII alphanumeric characters, `.`, `_` or `-` internally. Accepted input is preserved exactly; no trimming, lowercasing or normalization occurs. -`ComponentType` is the experimental Module/Plugin identity vocabulary only. `ApplicationLifecycle` remains application lifecycle, and `ResetParticipant` remains execution reset. Phase 5.1 does not implement Module/Plugin lifecycle entry points; lifecycle, descriptor behavior, discovery, registration, boot, ready and shutdown behavior remain deferred. +`ComponentType` is the experimental Module/Plugin identity vocabulary only. `ApplicationLifecycle` remains application lifecycle, and `ResetParticipant` remains execution reset. These identity value objects do not implement Module/Plugin lifecycle entry points, descriptor behavior, discovery, registration, boot, ready or shutdown behavior. ## Experimental Component Graph Declarations -Phase 5.3A adds public experimental declaration vocabulary for component dependency, conflict and capability metadata: +The package includes public experimental declaration vocabulary for component dependency, conflict and capability metadata: - `Evolve\Contracts\Component\ComponentGraphDeclaration` - `Evolve\Contracts\Component\ComponentGraphRelations` @@ -71,19 +71,19 @@ capability = alnum | alnum { alnum | "." | "_" | "-" } alnum alnum = lowercase ASCII letter | ASCII digit ``` -Phase 5.3A does not implement dependency resolution, missing dependency errors, optional dependency activation, conflict resolution, capability-provider indexing or selection, ambiguity resolution, cycle detection, startup ordering, component versions, version constraints or Composer SemVer graph decisions. Phase 5.3B owns graph validation and resolution behavior; later Phase 5 slices own registration, lifecycle and discovery. +These declaration contracts do not implement dependency resolution, missing dependency errors, optional dependency activation, conflict resolution, capability-provider indexing or selection, ambiguity resolution, cycle detection, startup ordering, component versions, version constraints or Composer SemVer graph decisions. Core owns graph validation and resolution behavior; registration, lifecycle and discovery are separate concerns. ## Experimental Component Service Registration -Phase 5.4 adds `Evolve\Contracts\Component\Registration\ServiceDefinitionRegistrar` as a public experimental, contribution-only service-definition boundary. Components may contribute Application, Execution or Transient service definitions through the three explicit registration methods only. +`Evolve\Contracts\Component\Registration\ServiceDefinitionRegistrar` is a public experimental, contribution-only service-definition boundary. Components may contribute Application, Execution or Transient service definitions through the three explicit registration methods only. Factories are documented as accepting zero or one active PSR-11 resolver argument. This gives Contracts deliberate external-standard access to `Psr\Container\ContainerInterface` for the public factory contract, but Contracts remains first-party-inward and is not a container implementation. -Core owns the restricted registrar implementation, registration ordering, staging and atomic publication. Registration order comes from `ResolvedComponentGraph`, service definitions are staged before publication, registration failure publishes nothing, registration does not resolve or construct services, and registration does not freeze the registry. `ApplicationKernel` integration, entry-point lifecycle, boot and ready were deferred from Phase 5.4 to Phase 5.5; explicit application-controlled enablement is introduced in Phase 5.6A while discovery remains deferred. +Core owns the restricted registrar implementation, registration ordering, staging and staged atomic publication. Registration order comes from `ResolvedComponentGraph`, service definitions are staged before publication, registration failure publishes nothing, registration does not resolve or construct services, and registration does not freeze the registry. `ApplicationKernel` integration, entry-point lifecycle, boot and ready are handled by the component lifecycle coordinator; explicit application-controlled enablement is handled by component definitions and Core bootstrap while discovery remains deferred. ## Experimental Component Lifecycle Entry Points -Phase 5.5 adds `Evolve\Contracts\Component\ComponentEntryPoint` and `Evolve\Contracts\Component\ComponentBootContext` as public experimental Module/Plugin lifecycle contracts. +`Evolve\Contracts\Component\ComponentEntryPoint` and `Evolve\Contracts\Component\ComponentBootContext` are public experimental Module/Plugin lifecycle contracts. `ComponentEntryPoint` exposes exactly four lifecycle callbacks: `register(ServiceDefinitionRegistrar $registrar): void`, `boot(ComponentBootContext $context): void`, `ready(): void` and `shutdown(): void`. Registration remains the contribution-only service-definition phase; boot runs only after registration succeeds and Core freezes the service-definition graph; ready runs only after every enabled component boots successfully; shutdown is the application-lifetime cleanup callback for successfully booted components. @@ -93,11 +93,11 @@ Discovery remains deferred. These contracts do not provide runtime auto-discover ## Experimental Component Definitions -Phase 5.6A adds `Evolve\Contracts\Component\ComponentDefinition` as a public experimental bridge between already-created descriptor metadata and lifecycle entry-point creation. +`Evolve\Contracts\Component\ComponentDefinition` is a public experimental bridge between already-created descriptor metadata and lifecycle entry-point creation. A component definition exposes the component identifier, component type, exact `ComponentGraphDeclaration` object, explicit startup validation and explicit `ComponentEntryPoint` creation. Implementations are application-supplied and are prepared before Core resolves the active component graph or runs lifecycle callbacks. -This contract does not define package discovery, Composer `extra` metadata, descriptor file formats, automatic installation scanning, component self-enablement, component versions, dependency version ranges or SemVer graph decisions. Those concerns remain outside Phase 5.6A. +This contract does not define package discovery, Composer `extra` metadata, descriptor file formats, automatic installation scanning, component self-enablement, component versions, dependency version ranges or SemVer graph decisions. ## Package diff --git a/packages/core/README.md b/packages/core/README.md index 8fcb6fc..14fb568 100644 --- a/packages/core/README.md +++ b/packages/core/README.md @@ -4,7 +4,7 @@ Application kernel and runtime-neutral orchestration for EvolvePHP 2. ## Runtime Foundation -Phase 3.1 introduced `Evolve\Core\ApplicationKernel` as the initial lifecycle implementation for the public `ApplicationLifecycle` contract. Phases 3.2 through 3.5 extended the runtime foundation with boot-time configuration validation, service-registry freezing, explicit execution scopes and, in Phase 3.5, runtime-neutral execution orchestration. +`Evolve\Core\ApplicationKernel` is the lifecycle implementation for the public `ApplicationLifecycle` contract. The runtime foundation includes boot-time configuration validation, service-registry freezing, explicit execution scopes and runtime-neutral execution orchestration. The current lifecycle remains intentionally minimal: @@ -37,17 +37,17 @@ The generic instrumentation foundation is deliberately not Evolve Insight, Evolv Service identifiers follow PSR-11 opaque-string semantics: the empty string is invalid, while other strings are case-sensitive and not trimmed or normalized. Circular dependencies, unknown services, root Execution access and ordinary factory failures are deterministic and catchable through PSR-11 exception interfaces where they occur during service resolution; factory throwables are preserved as previous exceptions when wrapped. -Core now contains the Phase 5.3B Core-owned graph validation and deterministic resolution foundation under `Evolve\Core\Component`. `ComponentGraphResolver` consumes the public experimental Contracts `ComponentGraphDeclaration` model and produces an immutable experimental `ResolvedComponentGraph` without importing Module or Plugin source types. It validates duplicate active component identifiers, missing required dependencies, active one-sided conflicts, required capability providers, provider ambiguity and dependency cycles before later registration or boot work begins. +Core contains the Core-owned graph validation and deterministic resolution foundation under `Evolve\Core\Component`. `ComponentGraphResolver` consumes the public experimental Contracts `ComponentGraphDeclaration` model and produces an immutable experimental `ResolvedComponentGraph` without importing Module or Plugin source types. It validates duplicate active component identifiers, missing required dependencies, active one-sided conflicts, required capability providers, provider ambiguity and dependency cycles before later registration or boot work begins. Required component dependencies must be active and create dependency-first ordering edges. Optional component dependencies are ignored when absent, but when present they create the same dependency-first edge and participate in cycle detection. Conflicts are validation-only and do not create ordering edges. Required capabilities are resolved from active providers: `ExactlyOne` requires one provider or a valid consumer-scoped `CapabilityProviderSelection`, while `OneOrMore` requires all active providers and does not accept provider selection. Self-provided capabilities can satisfy a requirement without creating a self edge. Effective edges are deduplicated, so the same prerequisite-to-dependent relationship from multiple declaration reasons is counted once. The resolver emits deterministic dependency-first ordering using lexical `ComponentIdentifier` tie-breaking, independent of declaration or selection input order. Cycle failures expose a canonical detected dependency chain through `ComponentDependencyCycle`, rotated to the lexically smallest identifier and closed by repeating the start identifier. These APIs are public experimental and pre-beta; they are graph validation/resolution only. -Core now contains the Phase 5.4 restricted component service-definition registration foundation under `Evolve\Core\Component\Registration`. `ComponentRegistrationCoordinator` consumes `ResolvedComponentGraph::orderedDeclarations()` as the sole ordering authority and binds each callback to the exact resolved `ComponentGraphDeclaration` object before any callback runs. The restricted registrar implements the public experimental Contracts `ServiceDefinitionRegistrar` but exposes only contribution-only methods for Application, Execution and Transient definitions. +Core contains the restricted component service-definition registration foundation under `Evolve\Core\Component\Registration`. `ComponentRegistrationCoordinator` consumes `ResolvedComponentGraph::orderedDeclarations()` as the sole ordering authority and binds each callback to the exact resolved `ComponentGraphDeclaration` object before any callback runs. The restricted registrar implements the public experimental Contracts `ServiceDefinitionRegistrar` but exposes only contribution-only methods for Application, Execution and Transient definitions. -Definitions are staged with existing registry identifiers reserved before callbacks, so duplicates, pre-existing service collisions and cross-component conflicts fail deterministically against the active contributor and publish nothing from the staged batch. After all callbacks succeed, Core preflights the complete staged batch against the live `ServiceRegistry` and publishes atomically. Registration invokes no service factories, does not resolve services, does not construct services and does not freeze the registry. The failed coordinator is terminal and retained registrar references become inert after callback completion. +Definitions are staged with existing registry identifiers reserved before callbacks, so duplicates, pre-existing service collisions and cross-component conflicts fail deterministically against the active contributor and publish nothing from the staged batch. After all callbacks succeed, Core preflights the complete staged batch against the live `ServiceRegistry` and publishes atomically; this is staged atomic publication. Registration invokes no service factories, does not resolve services, does not construct services and does not freeze the registry. The failed coordinator is terminal and retained registrar references become inert after callback completion. -Core owns the Phase 5.5 component lifecycle orchestration under `Evolve\Core\Component\Lifecycle` without depending on Module or Plugin packages. `ComponentLifecycleCoordinator` consumes a resolved component graph and exact `ComponentGraphDeclaration` object bindings to `ComponentEntryPoint` instances, validates that binding set before side effects, reuses `ComponentRegistrationCoordinator` for registration and then performs the application startup sequence as registration -> freeze -> boot -> ready through `ApplicationKernel`. +Core owns the component lifecycle orchestration under `Evolve\Core\Component\Lifecycle` without depending on Module or Plugin packages. `ComponentLifecycleCoordinator` consumes a resolved component graph and exact `ComponentGraphDeclaration` object bindings to `ComponentEntryPoint` instances, validates that binding set before side effects, reuses `ComponentRegistrationCoordinator` for registration and then performs the application startup sequence as registration -> freeze -> boot -> ready through `ApplicationKernel`. Boot receives a frozen PSR-11 resolver through a restricted `ComponentBootContext`; freezing still does not construct services or create execution scope. Boot and ready run dependency-first according to the already resolved graph order. Normal shutdown runs in reverse successful boot order and is best-effort: every booted component is attempted at most once, failures are attributed and a single `ComponentShutdownFailed` reports all failures after the sequence completes. @@ -57,13 +57,13 @@ The runtime CLI composition helpers `Evolve\Core\Console\Runtime\CliApplication` This slice provides no argument or option parsing, Doctor generators or developer commands. It also does not provide environment or dotenv loading, configuration files, autowiring, aliases, service tags, decorators, service-locator globals, HTTP handling, queue or scheduled-job adapters, retry policy, process termination or recycling, module/plugin runtime, telemetry products or integrations, streaming or persistent-worker concurrency guarantees. -Phase 5.5 did not implement discovery, enablement, component instantiation, component versions, dependency version ranges, Composer semantic-version constraint evaluation or Module/Plugin runtime managers. Phase 5.6A adds explicit application-controlled enablement while discovery remains deferred. +Lifecycle orchestration does not implement discovery, component versions, dependency version ranges, Composer semantic-version constraint evaluation or Module/Plugin runtime managers. Explicit application-controlled enablement is handled by the component bootstrap boundary while discovery remains deferred. -Core now owns the Phase 5.6A explicit component bootstrap boundary under `Evolve\Core\Component\ComponentBootstrapper`. Applications pass explicit `ComponentDefinition` objects to the bootstrapper and control activation through the `evolve.components.enabled` configuration list. When that configuration path is absent, no components are active. Unknown, duplicated, malformed or non-string enabled identifiers fail configuration validation before any component validation or entry-point creation occurs. +Core owns the explicit component bootstrap boundary under `Evolve\Core\Component\ComponentBootstrapper`. Applications pass explicit `ComponentDefinition` objects to the bootstrapper and control activation through the `evolve.components.enabled` configuration list. When that configuration path is absent, no components are active. Unknown, duplicated, malformed or non-string enabled identifiers fail configuration validation before any component validation or entry-point creation occurs. -The bootstrapper keeps disabled definitions inert. For enabled definitions, Core validates every enabled definition before creating any entry point, resolves the existing graph from those validated definitions, creates entry points in resolved dependency-first order and hands exact declaration-to-entry-point bindings to the existing Phase 5.5 lifecycle coordinator. Definition validation and entry-point construction failures preserve the previous throwable and expose the affected `ComponentIdentifier`. +The bootstrapper keeps disabled definitions inert. For enabled definitions, Core validates every enabled definition before creating any entry point, resolves the existing graph from those validated definitions, creates entry points in resolved dependency-first order and hands exact declaration-to-entry-point bindings to the existing lifecycle coordinator. Definition validation and entry-point construction failures preserve the previous throwable and expose the affected `ComponentIdentifier`. -Phase 5.6A does not provide Composer discovery, package scanning, descriptor file loading, Composer `extra` schema, automatic enablement, component versions, dependency version ranges, SemVer graph decisions, hot reload or per-execution component lifecycle work. +The bootstrap boundary does not provide Composer discovery, package scanning, descriptor file loading, Composer `extra` schema, automatic enablement, component versions, dependency version ranges, SemVer graph decisions, hot reload or per-execution component lifecycle work. Core now depends on `evolvephp/contracts` and `psr/container`. The concrete lifecycle implementation, service definition model, frozen container implementation and internal state enum are not an invitation for consumers to depend on internal runtime classes. diff --git a/packages/dev-tools/README.md b/packages/dev-tools/README.md index b56e1b6..4909738 100644 --- a/packages/dev-tools/README.md +++ b/packages/dev-tools/README.md @@ -18,7 +18,7 @@ PHP `^8.4` ## Generator Commands -Phase 6.4E adds two public experimental command adapters: +The package provides two public experimental command adapters: - `Evolve\DevTools\Console\ModuleNewCommand` - `Evolve\DevTools\Console\PluginNewCommand` diff --git a/packages/http/README.md b/packages/http/README.md index 5ffa6c6..058e168 100644 --- a/packages/http/README.md +++ b/packages/http/README.md @@ -23,13 +23,13 @@ This package also consumes the selected external PSR HTTP interoperability inter ## Runtime Foundation -Phase 4.1 provides a deterministic PSR-15 middleware pipeline foundation through `Evolve\Http\Middleware\MiddlewarePipeline`. +The package provides a deterministic PSR-15 middleware pipeline through `Evolve\Http\Middleware\MiddlewarePipeline`. The pipeline consumes PSR-7 `ServerRequestInterface` and `ResponseInterface` objects and implements PSR-15 `RequestHandlerInterface`. Middleware entries must implement PSR-15 `MiddlewareInterface`. Middleware runs in the order supplied at construction. For middleware `A`, middleware `B` and terminal handler `T`, execution is `A before`, `B before`, `T`, `B after`, `A after`. Middleware may short-circuit by returning a response without invoking the next handler; remaining middleware and the terminal handler are then not executed. -Phase 4.2 adds the routing foundation through `Evolve\Http\Routing\Route`, `RouteCollection`, `RouteMatch` and `RouteMatcher`. +The package provides route definitions and matching through `Evolve\Http\Routing\Route`, `RouteCollection`, `RouteMatch` and `RouteMatcher`. Routes store an exact ordered method list, a path-only template and a PSR-15 request handler instance. The handler is retained as route-definition data only; route matching does not invoke it. @@ -39,13 +39,13 @@ HTTP method matching is exact and case-sensitive. Methods remain as supplied, `G `RouteMatcher` traverses routes in collection insertion order. The first route whose path template and method both match wins; static routes are not automatically prioritized over parameter routes. If a path template matches but the method does not, matching continues to later routes. `allowedMethods()` exposes path-level routing metadata by aggregating exact methods for matching templates without creating 405 responses or `Allow` headers. -Phase 4.3 adds routed handler dispatch through `Evolve\Http\Routing\RoutingRequestHandler`, a PSR-15 `RequestHandlerInterface` implementation. It accepts a `RouteMatcher` and optional ordered post-match middleware. Existing `MiddlewarePipeline` instances may wrap `RoutingRequestHandler` for pre-routing/global middleware, while middleware supplied directly to `RoutingRequestHandler` runs only after a successful route match. +The package provides routed handler dispatch through `Evolve\Http\Routing\RoutingRequestHandler`, a PSR-15 `RequestHandlerInterface` implementation. It accepts a `RouteMatcher` and optional ordered post-match middleware. Existing `MiddlewarePipeline` instances may wrap `RoutingRequestHandler` for pre-routing/global middleware, while middleware supplied directly to `RoutingRequestHandler` runs only after a successful route match. On successful matching, `RoutingRequestHandler` attaches the exact authoritative `RouteMatch` to the derived request with `RouteMatch::class` as the request attribute key, replacing any stale incoming value at that key. Route parameters remain inside `RouteMatch::parameters()` and are not injected as top-level request attributes. Dispatch then runs post-match middleware in order and terminates at the exact `Route::handler()` instance stored on the matched route. Unsuccessful routing now has typed public exception boundaries. `Evolve\Http\Exception\RouteNotFound` is thrown when no path template matches, and `Evolve\Http\Exception\MethodNotAllowed` is thrown when the path matches at least one route template but the request method does not match any route. `MethodNotAllowed::allowedMethods()` returns the exact allowed methods reported by `RouteMatcher`, preserving order and case without adding implicit `HEAD`, automatic `OPTIONS` or an `Allow` header. -Phase 4.4 adds HTTP execution-kernel integration through `Evolve\Http\HttpKernel`. `HttpKernel` is the outer HTTP execution boundary: it wraps an already-composed PSR-15 `RequestHandlerInterface`, delegates every `handle()` call to the Core `ExecutionOrchestrator` as `ExecutionKind::HttpRequest` and returns the resulting `ExecutionOutcome`. +The package provides HTTP execution-kernel integration through `Evolve\Http\HttpKernel`. `HttpKernel` is the outer HTTP execution boundary: it wraps an already-composed PSR-15 `RequestHandlerInterface`, delegates every `handle()` call to the Core `ExecutionOrchestrator` as `ExecutionKind::HttpRequest` and returns the resulting `ExecutionOutcome`. `HttpKernel` does not itself implement PSR-15 because PSR-15 handlers must return `ResponseInterface`, while the execution boundary must preserve the full `ExecutionOutcome` so callers can inspect the primary response or throwable, cleanup/reset failure, instrumentation failures and the reuse/quarantine decision. The wrapped PSR-15 handler remains inside the execution. @@ -57,7 +57,7 @@ When the wrapped handler returns a response, that exact `ResponseInterface` inst Callers must inspect `ExecutionOutcome` and its reuse decision. `HttpKernel` does not emit a response, terminate a process, recycle a worker or convert failures into HTTP responses. -Phase 4.5 adds the response/error and health foundation. `Evolve\Http\Response\ExecutionOutcomeResponseResolver` converts only `ExecutionKind::HttpRequest` outcomes into PSR-7 `ResponseInterface` instances after `HttpKernel` has returned its `ExecutionOutcome`. Keeping response resolution after `HttpKernel` preserves the original primary response or throwable, cleanup/reset failure, instrumentation failures and reuse/quarantine decision for callers and runtime policy. +The package provides response/error and health handling. `Evolve\Http\Response\ExecutionOutcomeResponseResolver` converts only `ExecutionKind::HttpRequest` outcomes into PSR-7 `ResponseInterface` instances after `HttpKernel` has returned its `ExecutionOutcome`. Keeping response resolution after `HttpKernel` preserves the original primary response or throwable, cleanup/reset failure, instrumentation failures and reuse/quarantine decision for callers and runtime policy. Successful HTTP outcomes must contain a `ResponseInterface`; the resolver returns that exact response instance without cloning, wrapping, rebuilding, changing headers or consulting the response factory. Non-HTTP outcomes are rejected as programming errors. A successful HTTP outcome with a non-response primary result is also rejected as an unexpected programming error. @@ -65,23 +65,23 @@ Failed HTTP outcomes use the PSR-17 `ResponseFactoryInterface` from `psr/http-fa Cleanup failures, instrumentation failures and the process reuse/quarantine state remain stored on `ExecutionOutcome` and do not change response resolution. Runtime callers must still inspect the reuse decision. `ExecutionStartFailed` can happen before an `ExecutionOutcome` exists and remains a runtime concern rather than a resolver concern. -Phase 4.5 also adds explicit health-handler building blocks. `Evolve\Http\Health\LivenessHandler` is a PSR-15 handler that always returns an empty `200` response and performs no dependency checks. `Evolve\Http\Health\ReadinessCheck` is the minimal public readiness contract with `isReady(): bool`. `Evolve\Http\Health\ReadinessHandler` consumes and validates its check iterable during construction, preserves insertion order and returns an empty `200` response for zero checks or all-ready checks. The first false or throwing readiness check short-circuits later checks and returns an empty `503` response. +The package also provides explicit health-handler building blocks. `Evolve\Http\Health\LivenessHandler` is a PSR-15 handler that always returns an empty `200` response and performs no dependency checks. `Evolve\Http\Health\ReadinessCheck` is the minimal public readiness contract with `isReady(): bool`. `Evolve\Http\Health\ReadinessHandler` consumes and validates its check iterable during construction, preserves insertion order and returns an empty `200` response for zero checks or all-ready checks. The first false or throwing readiness check short-circuits later checks and returns an empty `503` response. Health handlers are not auto-routed and no health paths or route names are reserved. Applications explicitly place `LivenessHandler` or `ReadinessHandler` in whichever route definitions they choose. -Framework-created Phase 4.5 responses are intentionally empty and bodyless. Error and health responses do not expose request data, route parameters, exception messages, traces, readiness-check failure details or other sensitive runtime data. The only required protocol metadata added here is the `Allow` header for 405 responses. +Framework-created responses are intentionally empty and bodyless. Error and health responses do not expose request data, route parameters, exception messages, traces, readiness-check failure details or other sensitive runtime data. The only required protocol metadata added here is the `Allow` header for 405 responses. -Phase 4.6 adds `Evolve\Http\Response\ResponseEmitter` as the public runtime-neutral response-emission boundary. Its conceptual contract is `emit(ResponseInterface $response): void`. The emitter receives only the resolved response; it does not receive `ExecutionOutcome`, `ServerRequestInterface`, execution context, execution scope, route metadata or runtime-specific values. The caller retains the original `ExecutionOutcome` separately for cleanup, instrumentation and reuse/quarantine decisions. +`Evolve\Http\Response\ResponseEmitter` is the public runtime-neutral response-emission boundary. Its conceptual contract is `emit(ResponseInterface $response): void`. The emitter receives only the resolved response; it does not receive `ExecutionOutcome`, `ServerRequestInterface`, execution context, execution scope, route metadata or runtime-specific values. The caller retains the original `ExecutionOutcome` separately for cleanup, instrumentation and reuse/quarantine decisions. -The canonical Phase 4.6 flow is `HttpKernel` -> `ExecutionOutcome` -> `ExecutionOutcomeResponseResolver` -> `ResponseInterface` -> `ResponseEmitter`. Emission is explicit: `HttpKernel` does not auto-emit, and the resolver does not auto-emit. `ResponseEmitter` has no built-in implementation in this package. Runtime-specific implementations are deferred, including concrete SAPI, FPM, FrankenPHP and RoadRunner transmission adapters. Direct SAPI/header/output functions are not provided here; ownership of headers, body output, cookies and transmission failure policy belongs to future runtime adapters. +The canonical flow is `HttpKernel` -> `ExecutionOutcome` -> `ExecutionOutcomeResponseResolver` -> `ResponseInterface` -> `ResponseEmitter`. Emission is explicit: `HttpKernel` does not auto-emit, and the resolver does not auto-emit. `ResponseEmitter` has no built-in implementation in this package. Runtime-specific implementations are deferred, including concrete SAPI, FPM, FrankenPHP and RoadRunner transmission adapters. Direct SAPI/header/output functions are not provided here; ownership of headers, body output, cookies and transmission failure policy belongs to future runtime adapters. Cleanup and quarantine decisions remain on `ExecutionOutcome`. Runtime decides whether and when to transmit, recycle or terminate after inspecting reuse and quarantine state. The emitter/transmission failure handling policy is deferred and is not converted into Core execution outcome state by this package. -The package does not bundle a concrete PSR-7 implementation. Applications or later runtime slices must provide concrete request and response objects. Phase 4.5 requires only `ResponseFactoryInterface`; it does not use `StreamFactoryInterface`. +The package does not bundle a concrete PSR-7 implementation. Applications or later runtime slices must provide concrete request and response objects. Response resolution requires only `ResponseFactoryInterface`; it does not use `StreamFactoryInterface`. -Phase 4.6 closes the reviewed Phase 4 HTTP package foundation. HTML and JSON error rendering, problem-details DTOs, content negotiation, debug pages, automatic health routes, SAPI request creation, concrete response transmission, process termination/recycle adapters, runtime adapters, trace propagation and OpenTelemetry propagation remain deferred to later reviewed slices. +The reviewed HTTP package foundation is complete. HTML and JSON error rendering, problem-details DTOs, content negotiation, debug pages, automatic health routes, SAPI request creation, concrete response transmission, process termination/recycle adapters, runtime adapters, trace propagation and OpenTelemetry propagation remain deferred to later reviewed work. -Phase 6.4C adds `Evolve\Http\Routing\Console\RouteListCommand` as a caller-registerable console command adapter for route inspection. The command receives an explicit `RouteCollection` through constructor injection and uses the existing Core Console API, keeping the dependency direction as HTTP -> Core. +`Evolve\Http\Routing\Console\RouteListCommand` is a caller-registerable console command adapter for route inspection. The command receives an explicit `RouteCollection` through constructor injection and uses the existing Core Console API, keeping the dependency direction as HTTP -> Core. `route:list` renders only configured route methods and paths. It preserves `RouteCollection` order, method order, method case and route path text exactly, and it does not invoke or expose route handlers. Empty collections write `No routes are configured.` to normal output. diff --git a/packages/module/README.md b/packages/module/README.md index b715270..af37cfa 100644 --- a/packages/module/README.md +++ b/packages/module/README.md @@ -2,38 +2,31 @@ Application module SDK and lifecycle support for EvolvePHP 2. -## Phase 5.2 Descriptor Foundation +## Descriptor Foundation -Phase 5.2 adds the public experimental `Evolve\Module\ModuleDescriptor` in-memory immutable descriptor and `Evolve\Module\ModuleCompatibilityValidator`. +The package provides the public experimental `Evolve\Module\ModuleDescriptor` in-memory immutable descriptor and `Evolve\Module\ModuleCompatibilityValidator`. `ModuleDescriptor` reuses `Evolve\Contracts\Component\ComponentIdentifier`, preserves the exact accepted human-readable name, always reports the hard-coded Module type through `ComponentType::Module`, exposes descriptor schema version `1`, and declares an EvolvePHP major as a positive integer. Structural descriptor validity is separate from framework compatibility: positive non-2 majors are valid descriptor metadata, while `ModuleCompatibilityValidator` performs explicit EvolvePHP major compatibility validation for the currently supported major `2`. -Phase 5.3A extends `ModuleDescriptor` with `graphDeclaration()`, returning an immutable `Evolve\Contracts\Component\ComponentGraphDeclaration` built during descriptor construction. Existing three-argument descriptor construction remains valid and creates an empty graph declaration. A caller may also supply `ComponentGraphRelations` to declare required or optional dependencies, declarative conflicts, required capabilities with `ExactlyOne` or `OneOrMore` cardinality and provided capability identifiers. +`ModuleDescriptor` exposes `graphDeclaration()`, returning an immutable `Evolve\Contracts\Component\ComponentGraphDeclaration` built during descriptor construction. Existing three-argument descriptor construction remains valid and creates an empty graph declaration. A caller may also supply `ComponentGraphRelations` to declare required or optional dependencies, declarative conflicts, required capabilities with `ExactlyOne` or `OneOrMore` cardinality and provided capability identifiers. -The graph declaration is declaration vocabulary only. Relation ordering is canonical and deterministic, but declaration order has no startup-order semantics. Phase 5.3A does not resolve dependencies, activate optional dependencies, resolve conflicts, select capability providers, detect cycles, discover components, register components, order boot or introduce component versions, version constraints or SemVer graph decisions. +The graph declaration is declaration vocabulary only. Relation ordering is canonical and deterministic, but declaration order has no startup-order semantics. It does not resolve dependencies, activate optional dependencies, resolve conflicts, select capability providers, detect cycles, discover components, register components, order boot or introduce component versions, version constraints or SemVer graph decisions. Dependency resolution and graph validation are handled by Core graph resolution. -These APIs are PUBLIC EXPERIMENTAL and pre-beta. +These APIs are PUBLIC EXPERIMENTAL and pre-beta. Descriptor discovery, descriptor loading and descriptor serialization remain deferred. -At the time of the Phase 5.2 descriptor foundation, graph validation and dependency resolution were deferred to Phase 5.3B, while registration, entry-point contracts, lifecycle and boot behavior were still deferred to later Phase 5 slices. Phase 5.5 now adds the shared entry-point interface, and Phase 5.6A adds explicit application-supplied definitions while descriptor discovery still remains deferred. +## Entry Point -## Phase 5.5 Entry Point - -Phase 5.5 adds the public experimental `Evolve\Module\Module` entry-point interface. `Module` extends `Evolve\Contracts\Component\ComponentEntryPoint` and introduces no Module-specific lifecycle methods. +The package provides the public experimental `Evolve\Module\Module` entry-point interface. `Module` extends `Evolve\Contracts\Component\ComponentEntryPoint` and introduces no Module-specific lifecycle methods. An enabled module entry point participates in the shared component sequence: `register()` contributes service definitions through `ServiceDefinitionRegistrar`, Core freezes the service-definition graph, `boot()` receives an application-lifecycle-only `ComponentBootContext`, `ready()` runs after all enabled components boot, and `shutdown()` releases application-lifetime resources in reverse successful boot order. Deferred failure cleanup registered during `boot()` is only for that module's boot failure; successful modules still clean up through normal shutdown. -Deferred from this slice: - -- descriptor discovery and descriptor loading remain deferred; -- descriptor serialization remains deferred. - -## Phase 5.6A Explicit Definition +## Explicit Definition -Phase 5.6A adds the public experimental `Evolve\Module\ModuleDefinition`. It wraps an already-created `ModuleDescriptor` and an explicit module entry-point class name. +The package provides the public experimental `Evolve\Module\ModuleDefinition`. It wraps an already-created `ModuleDescriptor` and an explicit module entry-point class name. `ModuleDefinition` preserves the descriptor's identifier, type and exact graph declaration object. Its `validate()` method runs the existing `ModuleCompatibilityValidator` and checks that the configured entry-point class exists and implements `Evolve\Module\Module`. Entry-point instances are created only when Core prepares an explicitly enabled definition for lifecycle execution. -Discovery, descriptor loading, Composer `extra` metadata, automatic package scanning, module self-enablement, descriptor serialization and version-constraint evaluation remain deferred from this slice. +Discovery, descriptor loading, Composer `extra` metadata, automatic package scanning, module self-enablement, descriptor serialization and version-constraint evaluation remain deferred. ## Package diff --git a/packages/plugin/README.md b/packages/plugin/README.md index 4cfdec5..7efc86b 100644 --- a/packages/plugin/README.md +++ b/packages/plugin/README.md @@ -2,44 +2,37 @@ Framework plugin SDK and lifecycle support for EvolvePHP 2. -## Phase 5.2 Descriptor Foundation +## Descriptor Foundation -Phase 5.2 adds the public experimental `Evolve\Plugin\PluginDescriptor` in-memory immutable descriptor and `Evolve\Plugin\PluginCompatibilityValidator`. +The package provides the public experimental `Evolve\Plugin\PluginDescriptor` in-memory immutable descriptor and `Evolve\Plugin\PluginCompatibilityValidator`. `PluginDescriptor` reuses `Evolve\Contracts\Component\ComponentIdentifier`, preserves the exact accepted human-readable name, always reports the hard-coded Plugin type through `ComponentType::Plugin`, exposes descriptor schema version `1`, and declares an EvolvePHP major as a positive integer. Structural descriptor validity is separate from framework compatibility: positive non-2 majors are valid descriptor metadata, while `PluginCompatibilityValidator` performs explicit EvolvePHP major compatibility validation for the currently supported major `2`. -Phase 5.3A extends `PluginDescriptor` with `graphDeclaration()`, returning an immutable `Evolve\Contracts\Component\ComponentGraphDeclaration` built during descriptor construction. Existing three-argument descriptor construction remains valid and creates an empty graph declaration. A caller may also supply `ComponentGraphRelations` to declare required or optional dependencies, declarative conflicts, required capabilities with `ExactlyOne` or `OneOrMore` cardinality and provided capability identifiers. +`PluginDescriptor` exposes `graphDeclaration()`, returning an immutable `Evolve\Contracts\Component\ComponentGraphDeclaration` built during descriptor construction. Existing three-argument descriptor construction remains valid and creates an empty graph declaration. A caller may also supply `ComponentGraphRelations` to declare required or optional dependencies, declarative conflicts, required capabilities with `ExactlyOne` or `OneOrMore` cardinality and provided capability identifiers. -The graph declaration is declaration vocabulary only. Relation ordering is canonical and deterministic, but declaration order has no startup-order semantics. Phase 5.3A does not resolve dependencies, activate optional dependencies, resolve conflicts, select capability providers, detect cycles, discover components, register components, order boot or introduce component versions, version constraints or SemVer graph decisions. +The graph declaration is declaration vocabulary only. Relation ordering is canonical and deterministic, but declaration order has no startup-order semantics. It does not resolve dependencies, activate optional dependencies, resolve conflicts, select capability providers, detect cycles, discover components, register components, order boot or introduce component versions, version constraints or SemVer graph decisions. Dependency resolution and graph validation are handled by Core graph resolution. -These APIs are PUBLIC EXPERIMENTAL and pre-beta. +These APIs are PUBLIC EXPERIMENTAL and pre-beta. Descriptor loading, descriptor serialization and non-Composer discovery remain deferred. -At the time of the Phase 5.2 descriptor foundation, graph validation and dependency resolution were deferred to Phase 5.3B, while registration, entry-point contracts, lifecycle and boot behavior were still deferred to later Phase 5 slices. Phase 5.5 now adds the shared entry-point interface, and Phase 5.6A adds explicit application-supplied definitions while descriptor discovery still remains deferred. +## Entry Point -## Phase 5.5 Entry Point - -Phase 5.5 adds the public experimental `Evolve\Plugin\Plugin` entry-point interface. `Plugin` extends `Evolve\Contracts\Component\ComponentEntryPoint` and introduces no Plugin-specific lifecycle methods. +The package provides the public experimental `Evolve\Plugin\Plugin` entry-point interface. `Plugin` extends `Evolve\Contracts\Component\ComponentEntryPoint` and introduces no Plugin-specific lifecycle methods. An enabled plugin entry point participates in the shared component sequence: `register()` contributes service definitions through `ServiceDefinitionRegistrar`, Core freezes the service-definition graph, `boot()` receives an application-lifecycle-only `ComponentBootContext`, `ready()` runs after all enabled components boot, and `shutdown()` releases application-lifetime resources in reverse successful boot order. Deferred failure cleanup registered during `boot()` is only for that plugin's boot failure; successful plugins still clean up through normal shutdown. -Deferred from this slice: - -- descriptor discovery and descriptor loading remain deferred; -- descriptor serialization remains deferred. - -## Phase 5.6A Explicit Definition +## Explicit Definition -Phase 5.6A adds the public experimental `Evolve\Plugin\PluginDefinition`. It wraps an already-created `PluginDescriptor` and an explicit plugin entry-point class name. +The package provides the public experimental `Evolve\Plugin\PluginDefinition`. It wraps an already-created `PluginDescriptor` and an explicit plugin entry-point class name. `PluginDefinition` preserves the descriptor's identifier, type and exact graph declaration object. Its `validate()` method runs the existing `PluginCompatibilityValidator` and checks that the configured entry-point class exists and implements `Evolve\Plugin\Plugin`. Entry-point instances are created only when Core prepares an explicitly enabled definition for lifecycle execution. -Discovery, descriptor loading, Composer `extra` metadata, automatic package scanning, plugin self-enablement, descriptor serialization and version-constraint evaluation remain deferred from this slice. +Descriptor loading, automatic package scanning, plugin self-enablement, descriptor serialization and version-constraint evaluation remain deferred. -## Phase 5.6B Composer Plugin Discovery +## Composer Plugin Discovery -Phase 5.6B adds the public experimental `Evolve\Plugin\Discovery\ComposerPluginDiscovery` for packaged plugins. The caller must supply the explicit local Composer 2 `installed.json` filesystem path, for example a known `vendor/composer/installed.json` path. Discovery does not infer `vendor/`, the project root, the current working directory or environment configuration. +The package provides the public experimental `Evolve\Plugin\Discovery\ComposerPluginDiscovery` for packaged plugins. The caller must supply the explicit local Composer 2 `installed.json` filesystem path, for example a known `vendor/composer/installed.json` path. Discovery does not infer `vendor/`, the project root, the current working directory or environment configuration. -Composer metadata is recognized at `extra.evolvephp.plugin`, and one Composer package exposes at most one Phase 5.6B plugin. The Composer package name is the authoritative identifier; plugin metadata must not declare an `identifier` field. Discovery returns `PluginDefinition` instances sorted by package name, maps optional graph metadata into the existing Contracts graph declarations, and does not validate, instantiate or check entry-point classes while discovering metadata. +Composer metadata is recognized at `extra.evolvephp.plugin`, and one Composer package exposes at most one discovered plugin. The Composer package name is the authoritative identifier; plugin metadata must not declare an `identifier` field. Discovery returns `PluginDefinition` instances sorted by package name, maps optional graph metadata into the existing Contracts graph declarations, and does not validate, instantiate or check entry-point classes while discovering metadata. Schema 1 requires `schema`, `type`, `name`, `evolve_major` and `entry_point`. The optional `graph` object may contain `dependencies`, `conflicts`, `requires` and `provides`. Dependency `kind` values are `required` and `optional`; capability `cardinality` values are `exactly_one` and `one_or_more`. diff --git a/packages/testing/README.md b/packages/testing/README.md index 0021c7c..a2e6cc5 100644 --- a/packages/testing/README.md +++ b/packages/testing/README.md @@ -18,7 +18,7 @@ PHP `^8.4` ## Component Fixtures -Phase 5.7 adds the first small Testing SDK surface for component-oriented tests. +The package provides a small Testing SDK surface for component-oriented tests. The package provides two public experimental fixtures: @@ -48,7 +48,7 @@ policy, discovery, developer tooling, mocks or PHPUnit assertions. ## Console Output Recorder -Phase 6.4F adds `Evolve\Testing\Console\RecordingCommandOutput`, a public +`Evolve\Testing\Console\RecordingCommandOutput` is a public experimental in-memory implementation of Core's `CommandOutput` contract. It starts empty, records normal lines through `lines()`, records error lines diff --git a/skeleton/README.md b/skeleton/README.md index 45c90ef..9a1de9d 100644 --- a/skeleton/README.md +++ b/skeleton/README.md @@ -39,7 +39,7 @@ That validation exercises Composer create-project against local prerelease packa ## Generators -Phase 6.4E adds development-only module and plugin starter commands through +Development-only module and plugin starter commands are provided through `evolvephp/dev-tools`. `module:new Billing` creates `app/billing` under `src/Modules/Billing/`, and `plugin:new Cache` creates `app/cache` under `src/Plugins/Cache/`. Generated files remain application-owned and are not @@ -52,7 +52,7 @@ starter tests can run through: composer test ``` -Phase 6.4F provides the Testing package command-output recorder used by the +The Testing package provides the command-output recorder used by the generator command tests; the skeleton keeps broader testing utilities deferred. ## Deferred diff --git a/tests/Architecture/EvolvePhp2ApplicationSkeletonTest.php b/tests/Architecture/EvolvePhp2ApplicationSkeletonTest.php index 18de486..800fe29 100644 --- a/tests/Architecture/EvolvePhp2ApplicationSkeletonTest.php +++ b/tests/Architecture/EvolvePhp2ApplicationSkeletonTest.php @@ -157,8 +157,6 @@ public function testSkeletonReadmeDocumentsAcceptedBoundariesWithoutPublicationC 'Core remains independent of HTTP', 'composer release:skeleton:validate', 'Packagist', - '6.4E', - '6.4F', 'module:new', 'plugin:new', 'composer test', diff --git a/tests/Architecture/EvolvePhp2ArchitectureAndDependencyBoundariesTest.php b/tests/Architecture/EvolvePhp2ArchitectureAndDependencyBoundariesTest.php index df331bb..8c86910 100644 --- a/tests/Architecture/EvolvePhp2ArchitectureAndDependencyBoundariesTest.php +++ b/tests/Architecture/EvolvePhp2ArchitectureAndDependencyBoundariesTest.php @@ -153,13 +153,13 @@ public function testDocumentationRecordsArchitectureBoundaryPolicy(): void '/deptrac\/deptrac/i', '/qossmic\/deptrac/i', '/production source directories/i', - '/tests? (?:are|is) excluded|excluded from Phase 2\.5/i', + '/tests? (?:are|is) excluded|excluded from Deptrac/i', '/physical package paths?/i', '/no production dependency on Testing/i', '/uncovered dependencies fail/i', '/no baseline|baseline.*not/i', '/no graph|graph.*not/i', - '/PHP 8\.5.*Phase 2\.6/i', + '/PHP 8\.5.*CI matrix|CI matrix.*PHP 8\.5/i', ) as $developmentPattern) { $this->assertMatchesPattern($developmentPattern, $developmentGuide); } @@ -180,14 +180,14 @@ public function testDocumentationRecordsArchitectureBoundaryPolicy(): void $this->assertMatchesPattern('/no production dependency on Testing/i', $packagesReadme); $this->assertMatchesPattern('/DEVELOPMENT\.md/i', $packagesReadme); $this->assertMatchesPattern('/runtime implementation.*not yet present|not yet present.*runtime implementation/i', $packagesReadme); - $this->assertMatchesPattern('/Phase 4\.1.*PSR.*HTTP.*middleware/is', $packagesReadme); + $this->assertMatchesPattern('/PSR HTTP.*middleware|middleware.*PSR HTTP|PSR-15.*middleware/is', $packagesReadme); $this->assertMatchesPattern('/MiddlewarePipeline/i', $packagesReadme); - $this->assertMatchesPattern('/Phase 4\.2.*routing foundation/is', $packagesReadme); + $this->assertMatchesPattern('/route definitions.*matching|RouteCollection.*RouteMatcher/is', $packagesReadme); $this->assertMatchesPattern('/packages.*not yet published|not yet published.*packages/i', $packagesReadme); $this->assertMatchesPattern('/PsrContainer/i', $developmentGuide); $this->assertMatchesPattern('/Contracts external standards.*PsrContainer|PsrContainer.*Contracts external standards/is', $developmentGuide); - $this->assertMatchesPattern('/Phase 5\.4.*service-definition factory contract|service-definition factory contract.*Phase 5\.4/is', $developmentGuide); + $this->assertMatchesPattern('/ServiceDefinitionRegistrar.*service-definition factory contract|service-definition factory contract.*ServiceDefinitionRegistrar/is', $developmentGuide); $this->assertMatchesPattern('/PsrHttpMessage/i', $developmentGuide); $this->assertMatchesPattern('/PsrHttpServer/i', $developmentGuide); $this->assertMatchesPattern('/PSR HTTP interfaces.*external interoperability standards|external interoperability standards.*PSR HTTP interfaces/is', $developmentGuide); @@ -296,8 +296,8 @@ private function deptracRulesets($content) } foreach (array('Contracts', 'Core', 'DevTools', 'Module', 'Plugin', 'Testing') as $layerName) { - $this->assertNotContains('PsrHttpMessage', $rulesets[$layerName], $layerName . ' must not access PSR-7 HTTP message interfaces directly in Phase 4.1.'); - $this->assertNotContains('PsrHttpServer', $rulesets[$layerName], $layerName . ' must not access PSR-15 HTTP server interfaces directly in Phase 4.1.'); + $this->assertNotContains('PsrHttpMessage', $rulesets[$layerName], $layerName . ' must not access PSR-7 HTTP message interfaces directly.'); + $this->assertNotContains('PsrHttpServer', $rulesets[$layerName], $layerName . ' must not access PSR-15 HTTP server interfaces directly.'); } return $rulesets; diff --git a/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php b/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php index bc1b6f2..fb7d5fa 100644 --- a/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php +++ b/tests/Architecture/EvolvePhp2ContinuousIntegrationTest.php @@ -214,7 +214,7 @@ public function testDocumentationRecordsContinuousIntegrationCompatibilityEviden '/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', + '/CI matrix.*successfully executed|successfully executed.*CI matrix/i', '/ quality.*passes.*PHP 8\.4.*PHP 8\.5|PHP 8\.4.*PHP 8\.5.*root 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', diff --git a/tests/Architecture/EvolvePhp2PackageSkeletonTest.php b/tests/Architecture/EvolvePhp2PackageSkeletonTest.php index 680dcca..3ddc975 100644 --- a/tests/Architecture/EvolvePhp2PackageSkeletonTest.php +++ b/tests/Architecture/EvolvePhp2PackageSkeletonTest.php @@ -188,30 +188,30 @@ public function testPackageOverviewDocumentsSkeletonBoundariesAndCompatibilityLi $this->assertMatchesPattern('/minimal Core console foundation|runtime-neutral command foundation/i', $content); $this->assertMatchesPattern('/CommandRunner|CommandInput|CommandOutput|CommandResult/i', $content); $this->assertMatchesPattern('/CliCommand/i', $content); - $this->assertMatchesPattern('/Phase 4\.1.*PSR.*HTTP.*middleware/is', $content); + $this->assertMatchesPattern('/PSR HTTP.*middleware|PSR-15.*middleware|middleware.*PSR HTTP/is', $content); $this->assertMatchesPattern('/MiddlewarePipeline/i', $content); - $this->assertMatchesPattern('/Phase 4\.2.*routing foundation/is', $content); + $this->assertMatchesPattern('/route definitions.*matching|RouteCollection.*RouteMatcher/is', $content); $this->assertMatchesPattern('/RouteCollection|RouteMatcher/i', $content); - $this->assertMatchesPattern('/Phase 4\.3.*routed handler dispatch/is', $content); + $this->assertMatchesPattern('/routed handler dispatch/is', $content); $this->assertMatchesPattern('/RoutingRequestHandler/i', $content); $this->assertMatchesPattern('/RouteNotFound|MethodNotAllowed/i', $content); - $this->assertMatchesPattern('/Phase 4\.4.*HTTP execution-kernel integration/is', $content); + $this->assertMatchesPattern('/HTTP execution-kernel integration/is', $content); $this->assertMatchesPattern('/HttpKernel/i', $content); $this->assertMatchesPattern('/ExecutionOrchestrator.*HttpRequest|HttpRequest.*ExecutionOrchestrator/is', $content); $this->assertMatchesPattern('/ExecutionOutcome/i', $content); $this->assertMatchesPattern('/RouteNotFound.*empty 404|empty 404.*RouteNotFound/is', $content); $this->assertMatchesPattern('/MethodNotAllowed.*empty 405|empty 405.*MethodNotAllowed/is', $content); - $this->assertMatchesPattern('/Phase 4\.5.*response\/error.*health foundation/is', $content); + $this->assertMatchesPattern('/response\/error.*health|response.*health handling/is', $content); $this->assertMatchesPattern('/ExecutionOutcomeResponseResolver/i', $content); $this->assertMatchesPattern('/LivenessHandler|ReadinessHandler/i', $content); - $this->assertMatchesPattern('/Phase 4\.6.*ResponseEmitter/is', $content); + $this->assertMatchesPattern('/ResponseEmitter/i', $content); $this->assertMatchesPattern('/runtime-neutral response-emission boundary|runtime-neutral response emission boundary/i', $content); $this->assertMatchesPattern('/response resolution.*emission|emission.*response resolution/is', $content); $this->assertMatchesPattern('/Runtime.*concrete transmission|concrete transmission.*Runtime/is', $content); $this->assertMatchesPattern('/Runtime.*quarantine.*recycle.*termination|quarantine.*recycle.*termination.*Runtime/is', $content); $this->assertMatchesPattern('/no concrete SAPI emitter|concrete SAPI emitter.*not/i', $content); $this->assertMatchesPattern('/concrete PSR-7 implementation.*not|no concrete PSR-7 implementation/i', $content); - $this->assertMatchesPattern('/Phase 4 HTTP package foundation.*complete|complete.*Phase 4 HTTP package foundation/i', $content); + $this->assertMatchesPattern('/HTTP package foundation.*complete|complete.*HTTP package foundation/i', $content); $this->assertMatchesPattern('/complete production runtime.*deferred|production runtime.*still.*deferred/i', $content); $this->assertMatchesPattern('/OpenTelemetry propagation.*deferred|trace context.*OpenTelemetry.*deferred/is', $content); $this->assertMatchesPattern('/Runtime adapters.*deferred|Runtime.*adapters.*deferred/i', $content); @@ -229,7 +229,7 @@ public function testPackageOverviewDocumentsSkeletonBoundariesAndCompatibilityLi $this->assertDoesNotMatchPattern('/' . preg_quote($probeHistory, '/') . '/i', $content); } - public function testContractsReadmeDocumentsPhase51ComponentIdentityFoundation(): void + public function testContractsReadmeDocumentsComponentIdentityFoundation(): void { $content = $this->readProjectFile('packages/contracts/README.md'); @@ -239,41 +239,48 @@ public function testContractsReadmeDocumentsPhase51ComponentIdentityFoundation() $this->assertMatchesPattern('/identifier grammar/i', $content); $this->assertMatchesPattern('/no normalization|not normaliz/i', $content); $this->assertMatchesPattern('/Module.*Plugin.*identity vocabulary|Plugin.*Module.*identity vocabulary/is', $content); - $this->assertMatchesPattern('/lifecycle.*deferred|deferred.*lifecycle/is', $content); - $this->assertMatchesPattern('/descriptor.*deferred|deferred.*descriptor/is', $content); + $this->assertMatchesPattern( + '/identity value objects.*do not implement.*lifecycle entry points.*descriptor behavior.*discovery.*registration.*boot.*ready.*shutdown/is', + $content + ); } - public function testPackageOverviewDocumentsPhase51ComponentIdentityFoundationBoundary(): void + public function testPackageOverviewDocumentsComponentIdentityFoundationBoundary(): void { $content = $this->readProjectFile('packages/README.md'); - $this->assertMatchesPattern('/Phase 5\.1.*Component Identity Foundation/is', $content); + $this->assertMatchesPattern('/component identity vocabulary|shared identity vocabulary/i', $content); $this->assertMatchesPattern('/experimental shared identity vocabulary|shared experimental identity vocabulary/i', $content); - $this->assertMatchesPattern('/Module\/Plugin entry points.*not implemented|not implemented.*Module\/Plugin entry points/is', $content); - $this->assertMatchesPattern('/descriptors?.*deferred|deferred.*descriptors?/i', $content); - $this->assertMatchesPattern('/discovery.*deferred|deferred.*discovery/i', $content); - $this->assertMatchesPattern('/registration.*deferred|deferred.*registration/i', $content); - $this->assertMatchesPattern('/boot.*ready.*shutdown.*deferred|deferred.*boot.*ready.*shutdown/is', $content); + $this->assertMatchesPattern('/ComponentIdentifier.*ComponentType|ComponentType.*ComponentIdentifier/is', $content); + $this->assertMatchesPattern('/identity value objects.*do not.*descriptor.*discovery.*registration.*lifecycle|do not.*descriptor.*discovery.*registration.*lifecycle.*identity value objects/is', $content); + $this->assertMatchesPattern('/Module\/Plugin entry points.*implemented by separate lifecycle contracts|entry points.*separate lifecycle contracts/is', $content); + $this->assertMatchesPattern('/descriptors?.*separate package|separate package.*descriptors?/i', $content); + $this->assertMatchesPattern('/registration.*Core|Core.*registration/i', $content); + $this->assertMatchesPattern('/Module discovery.*deferred|discovery remains deferred/i', $content); + $this->assertMatchesPattern('/ComposerPluginDiscovery|packaged Plugin Composer discovery/i', $content); } - public function testPhase52ModuleAndPluginDescriptorFoundationIsDocumented(): void + public function testModuleAndPluginDescriptorFoundationIsDocumented(): void { $moduleReadme = $this->readProjectFile('packages/module/README.md'); $pluginReadme = $this->readProjectFile('packages/plugin/README.md'); $packagesReadme = $this->readProjectFile('packages/README.md'); $changelog = $this->readProjectFile('CHANGELOG.md'); - foreach (array($moduleReadme, $pluginReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.2/i', $content); + foreach (array($moduleReadme, $pluginReadme, $packagesReadme) as $content) { $this->assertMatchesPattern('/experimental/i', $content); $this->assertMatchesPattern('/immutable descriptor/i', $content); $this->assertMatchesPattern('/EvolvePHP-major compatibility validation|EvolvePHP major compatibility validation/i', $content); - $this->assertMatchesPattern('/graph validation.*resolution.*deferred|dependency resolution.*deferred|deferred.*dependency resolution/is', $content); - $this->assertMatchesPattern('/entry-point.*deferred|deferred.*entry-point|lifecycle.*deferred|deferred.*lifecycle/is', $content); + $this->assertMatchesPattern('/graph validation.*resolution.*Core|Core.*graph validation.*resolution|handled by Core graph resolution/is', $content); + $this->assertDoesNotMatchPattern('/(?:dependency resolution|graph validation).*remain deferred to Core graph resolution/i', $content); + $this->assertMatchesPattern('/entry-point|lifecycle/is', $content); $this->assertMatchesPattern('/discovery.*deferred|deferred.*discovery/is', $content); - $this->assertMatchesPattern('/enablement.*deferred|deferred.*enablement/is', $content); + $this->assertMatchesPattern('/enablement/i', $content); } + $this->assertMatchesPattern('/Phase 5\.2/i', $changelog); + $this->assertMatchesPattern('/immutable descriptor/i', $changelog); + $this->assertMatchesPattern('/ModuleDescriptor/i', $moduleReadme); $this->assertMatchesPattern('/ComponentIdentifier/i', $moduleReadme); $this->assertMatchesPattern('/ComponentType::Module|hard-coded Module type/i', $moduleReadme); @@ -291,7 +298,7 @@ public function testPhase52ModuleAndPluginDescriptorFoundationIsDocumented(): vo $this->assertMatchesPattern('/Phase 5\.2.*PluginDescriptor|PluginDescriptor.*Phase 5\.2/is', $changelog); } - public function testPhase53aGraphDeclarationVocabularyIsDocumentedWithoutResolutionClaims(): void + public function testGraphDeclarationVocabularyIsDocumentedWithoutResolutionClaims(): void { $contractsReadme = $this->readProjectFile('packages/contracts/README.md'); $moduleReadme = $this->readProjectFile('packages/module/README.md'); @@ -299,19 +306,30 @@ public function testPhase53aGraphDeclarationVocabularyIsDocumentedWithoutResolut $packagesReadme = $this->readProjectFile('packages/README.md'); $changelog = $this->readProjectFile('CHANGELOG.md'); - foreach (array($contractsReadme, $moduleReadme, $pluginReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.3A/i', $content); + foreach (array($contractsReadme, $moduleReadme, $pluginReadme, $packagesReadme) as $content) { $this->assertMatchesPattern('/experimental/i', $content); $this->assertMatchesPattern('/declaration vocabulary|graph declaration vocabulary/i', $content); $this->assertMatchesPattern('/required.*optional|optional.*required/is', $content); $this->assertMatchesPattern('/conflicts?.*declarative|declarative.*conflicts?/is', $content); - $this->assertMatchesPattern('/ExactlyOne|ExactlyOne/i', $content); + $this->assertMatchesPattern('/ExactlyOne/i', $content); $this->assertMatchesPattern('/OneOrMore/i', $content); $this->assertMatchesPattern('/provided capability identifiers?|capability identifiers?.*provided/is', $content); $this->assertMatchesPattern('/canonical.*order|order.*canonical/is', $content); $this->assertMatchesPattern('/startup-order semantics|startup order.*semantics|no startup-order/i', $content); - $this->assertMatchesPattern('/does not.*resolve|resolution.*deferred|resolve.*deferred/is', $content); - $this->assertMatchesPattern('/Phase 5\.3B/i', $content); + $this->assertMatchesPattern( + '/do(?:es)? not.*(?:resolve|implement dependency resolution)|handled by Core graph resolution|Core owns graph validation/is', + $content + ); + $this->assertMatchesPattern('/graph validation|graph resolution|dependency resolution/i', $content); + } + + $this->assertMatchesPattern('/Phase 5\.3A/i', $changelog); + $this->assertMatchesPattern('/Phase 5\.3B/i', $changelog); + $this->assertMatchesPattern('/ComponentGraphDeclaration|graph declaration vocabulary/i', $changelog); + + foreach (array($moduleReadme, $pluginReadme) as $content) { + $this->assertMatchesPattern('/graph validation.*resolution.*handled by Core|handled by Core graph resolution/i', $content); + $this->assertDoesNotMatchPattern('/remain deferred to Core graph resolution/i', $content); } foreach (array($moduleReadme, $pluginReadme, $packagesReadme, $changelog) as $content) { @@ -336,7 +354,7 @@ public function testContractsAndCoreDeclarePsrContainerInteroperabilityMetadata( $this->assertSame( '^1.1 || ^2.0', $contractsManifest['require']['psr/container'] ?? null, - 'Contracts should require PSR-11 for the Phase 5.4 public service-definition factory contract.' + 'Contracts should require PSR-11 for the public service-definition factory contract.' ); $this->assertArrayNotHasKey( 'provide', @@ -374,15 +392,14 @@ public function testContractsAndCoreDeclarePsrContainerInteroperabilityMetadata( } } - public function testPhase54RestrictedRegistrationIsDocumentedWithinAcceptedBoundaries(): void + public function testRestrictedRegistrationIsDocumentedWithinAcceptedBoundaries(): void { $contractsReadme = $this->readProjectFile('packages/contracts/README.md'); $coreReadme = $this->readProjectFile('packages/core/README.md'); $packagesReadme = $this->readProjectFile('packages/README.md'); $changelog = $this->readProjectFile('CHANGELOG.md'); - foreach (array($contractsReadme, $coreReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.4/i', $content); + foreach (array($contractsReadme, $coreReadme, $packagesReadme) as $content) { $this->assertMatchesPattern('/ServiceDefinitionRegistrar/i', $content); $this->assertMatchesPattern('/experimental/i', $content); $this->assertMatchesPattern('/contribution-only|definition contribution/i', $content); @@ -391,14 +408,17 @@ public function testPhase54RestrictedRegistrationIsDocumentedWithinAcceptedBound $this->assertMatchesPattern('/publish(?:es)? nothing|nothing.*publish/is', $content); $this->assertMatchesPattern('/does not.*resolve services|does not.*construct services|no service construction/is', $content); $this->assertMatchesPattern('/does not.*freeze|no automatic.*freeze/is', $content); - $this->assertMatchesPattern('/ApplicationKernel.*Phase 5\.5|Phase 5\.5.*ApplicationKernel/is', $content); - $this->assertMatchesPattern('/entry-point.*Phase 5\.5|lifecycle.*Phase 5\.5/is', $content); + $this->assertMatchesPattern('/ApplicationKernel/i', $content); + $this->assertMatchesPattern('/entry-point|lifecycle/i', $content); $this->assertMatchesPattern('/discovery.*deferred|deferred.*discovery/is', $content); - $this->assertMatchesPattern('/enablement.*Phase 5\.6A|Phase 5\.6A.*enablement/is', $content); + $this->assertMatchesPattern('/enablement/i', $content); } + + $this->assertMatchesPattern('/Phase 5\.4/i', $changelog); + $this->assertMatchesPattern('/ServiceDefinitionRegistrar/i', $changelog); } - public function testPhase55ComponentLifecycleOrchestrationIsDocumentedWithinAcceptedBoundaries(): void + public function testComponentLifecycleOrchestrationIsDocumentedWithinAcceptedBoundaries(): void { $contractsReadme = $this->readProjectFile('packages/contracts/README.md'); $moduleReadme = $this->readProjectFile('packages/module/README.md'); @@ -408,12 +428,11 @@ public function testPhase55ComponentLifecycleOrchestrationIsDocumentedWithinAcce $changelog = $this->readProjectFile('CHANGELOG.md'); foreach (array($contractsReadme, $moduleReadme, $pluginReadme, $coreReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.5/i', $content); $this->assertMatchesPattern('/experimental/i', $content); $this->assertMatchesPattern('/ComponentEntryPoint/i', $content); $this->assertMatchesPattern('/register.*boot.*ready.*shutdown|boot.*ready.*shutdown/is', $content); $this->assertMatchesPattern('/discovery.*deferred|deferred.*discovery/is', $content); - $this->assertMatchesPattern('/enablement.*Phase 5\.6A|Phase 5\.6A.*enablement/is', $content); + $this->assertMatchesPattern('/enablement/i', $content); } $this->assertMatchesPattern('/ComponentBootContext/i', $contractsReadme); @@ -440,16 +459,15 @@ public function testPhase55ComponentLifecycleOrchestrationIsDocumentedWithinAcce $this->assertDoesNotMatchPattern('/package publication|production readiness|hot unloading|runtime auto-discovery|telemetry integration|persistent runtime certification|benchmark superiority/i', $changelog); } - public function testPhase53bCoreGraphResolutionIsDocumentedWithinAcceptedBoundaries(): void + public function testCoreGraphResolutionIsDocumentedWithinAcceptedBoundaries(): void { $coreReadme = $this->readProjectFile('packages/core/README.md'); $packagesReadme = $this->readProjectFile('packages/README.md'); $changelog = $this->readProjectFile('CHANGELOG.md'); - foreach (array($coreReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.3B/i', $content); + foreach (array($coreReadme, $packagesReadme) as $content) { $this->assertMatchesPattern('/experimental/i', $content); - $this->assertMatchesPattern('/Core-owned graph validation|Core-owned.*graph.*resolution|graph validation.*Core-owned/is', $content); + $this->assertMatchesPattern('/Core-owned graph validation|Core-owned.*graph.*resolution|Core owns graph validation|graph validation.*Core owns/is', $content); $this->assertMatchesPattern('/ComponentGraphResolver/i', $content); $this->assertMatchesPattern('/ResolvedComponentGraph/i', $content); $this->assertMatchesPattern('/CapabilityProviderSelection/i', $content); @@ -459,21 +477,30 @@ public function testPhase53bCoreGraphResolutionIsDocumentedWithinAcceptedBoundar $this->assertMatchesPattern('/OneOrMore/i', $content); $this->assertMatchesPattern('/deterministic.*dependency-first|dependency-first.*deterministic/is', $content); $this->assertMatchesPattern('/cycle.*canonical|canonical.*cycle/i', $content); - $this->assertMatchesPattern('/registration.*deferred|deferred.*registration/is', $content); - $this->assertMatchesPattern('/boot.*ready.*shutdown.*deferred|deferred.*boot.*ready.*shutdown/is', $content); - $this->assertMatchesPattern('/discovery.*deferred|deferred.*discovery/is', $content); - $this->assertMatchesPattern('/enablement.*Phase 5\.6A|Phase 5\.6A.*enablement|app-controlled enablement/i', $content); + $this->assertMatchesPattern( + '/graph resolver remains separate from registration and boot|graph resolution.*separate from registration.*boot|graph validation\/resolution only/is', + $content + ); + $this->assertMatchesPattern( + '/graph resolution does not itself perform registration|does not.*perform registration|graph validation\/resolution only/is', + $content + ); + $this->assertMatchesPattern('/discovery.*separate concern|discovery remains.*deferred|deferred.*discovery/is', $content); + $this->assertMatchesPattern('/enablement|app-controlled enablement|application-controlled enablement/i', $content); $this->assertDoesNotMatchPattern('/composer\/semver/i', $content); $this->assertDoesNotMatchPattern('/implements?.*SemVer|SemVer.*implemented/i', $content); } + $this->assertMatchesPattern('/Phase 5\.3B/i', $changelog); + $this->assertMatchesPattern('/ComponentGraphResolver/i', $changelog); + foreach (array($coreReadme, $packagesReadme) as $content) { $this->assertMatchesPattern('/Contracts declarations|Contracts graph declarations|consumes.*ComponentGraphDeclaration/is', $content); $this->assertMatchesPattern('/conflict.*validation|validates.*conflicts?/is', $content); } } - public function testPhase56aExplicitComponentBootstrapIsDocumentedWithinAcceptedBoundaries(): void + public function testExplicitComponentBootstrapIsDocumentedWithinAcceptedBoundaries(): void { $contractsReadme = $this->readProjectFile('packages/contracts/README.md'); $moduleReadme = $this->readProjectFile('packages/module/README.md'); @@ -483,7 +510,6 @@ public function testPhase56aExplicitComponentBootstrapIsDocumentedWithinAccepted $changelog = $this->readProjectFile('CHANGELOG.md'); foreach (array($contractsReadme, $moduleReadme, $pluginReadme, $coreReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.6A/i', $content); $this->assertMatchesPattern('/experimental|pre-beta/i', $content); $this->assertMatchesPattern('/explicit/i', $content); $this->assertMatchesPattern('/Composer `extra`|Composer discovery|package scanning|automatic package scanning/i', $content); @@ -509,18 +535,17 @@ public function testPhase56aExplicitComponentBootstrapIsDocumentedWithinAccepted $this->assertMatchesPattern('/disabled.*inert|inert.*disabled/i', $content); $this->assertMatchesPattern('/validat.*before.*entry-point creation|validat.*before.*creating|validate-all-before-create/i', $content); $this->assertMatchesPattern('/existing graph|graph resolver/i', $content); - $this->assertMatchesPattern('/Phase 5\.5 lifecycle coordinator|lifecycle coordinator/i', $content); + $this->assertMatchesPattern('/lifecycle coordinator/i', $content); } } - public function testPhase56bComposerPluginDiscoveryIsDocumentedWithinAcceptedBoundaries(): void + public function testComposerPluginDiscoveryIsDocumentedWithinAcceptedBoundaries(): void { $pluginReadme = $this->readProjectFile('packages/plugin/README.md'); $packagesReadme = $this->readProjectFile('packages/README.md'); $changelog = $this->readProjectFile('CHANGELOG.md'); foreach (array($pluginReadme, $packagesReadme, $changelog) as $content) { - $this->assertMatchesPattern('/Phase 5\.6B/i', $content); $this->assertMatchesPattern('/ComposerPluginDiscovery/i', $content); $this->assertMatchesPattern('/vendor\/composer\/installed\.json|installed\.json/i', $content); $this->assertMatchesPattern('/extra\.evolvephp\.plugin/i', $content); @@ -535,7 +560,7 @@ public function testPhase56bComposerPluginDiscoveryIsDocumentedWithinAcceptedBou $this->assertMatchesPattern('/caller.*suppl.*explicit local.*installed\.json|explicit local.*installed\.json.*caller.*suppl/is', $pluginReadme); $this->assertMatchesPattern('/does not infer.*vendor.*project root.*current working directory.*environment configuration/is', $pluginReadme); - $this->assertMatchesPattern('/at most one.*Phase 5\.6B plugin/is', $pluginReadme); + $this->assertMatchesPattern('/at most one.*(?:discovered )?plugin/is', $pluginReadme); $this->assertMatchesPattern('/schema.*type.*name.*evolve_major.*entry_point/is', $pluginReadme); $this->assertMatchesPattern('/dependencies.*conflicts.*requires.*provides/is', $pluginReadme); $this->assertMatchesPattern('/required.*optional/is', $pluginReadme); @@ -567,12 +592,12 @@ public function testHttpPackageDeclaresPsr17ResponseFactoryWithoutConcretePsr7Im $this->assertArrayNotHasKey( $packageName, $httpManifest['require'], - 'HTTP must not require a concrete PSR-7 implementation for Phase 4.5.' + 'HTTP must not require a concrete PSR-7 implementation.' ); } } - public function testHttpReadmeDocumentsPhase46ResponseEmitterAndRuntimeBoundaries(): void + public function testHttpReadmeDocumentsResponseEmitterAndRuntimeBoundaries(): void { $content = $this->readProjectFile('packages/http/README.md'); diff --git a/tests/Documentation/EvolvePhp2DeveloperExperienceTest.php b/tests/Documentation/EvolvePhp2DeveloperExperienceTest.php index 74d972b..422eacf 100644 --- a/tests/Documentation/EvolvePhp2DeveloperExperienceTest.php +++ b/tests/Documentation/EvolvePhp2DeveloperExperienceTest.php @@ -19,7 +19,7 @@ public function testRequiredRepositoryEditorAssetsExist(): void '.vscode/settings.json', '.vscode/tasks.json', ) as $path) { - $this->assertFileExists($this->projectPath($path), $path . ' should be committed for the Phase 2.8 developer-experience foundation.'); + $this->assertFileExists($this->projectPath($path), $path . ' should be committed for the developer-experience foundation.'); } } diff --git a/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php b/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php index 3ed6179..ef64ebf 100644 --- a/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php +++ b/tests/Documentation/EvolvePhp2ReadmeAndMetadataConsistencyTest.php @@ -115,6 +115,52 @@ public function testCanonicalReadmesAvoidMachinePathsAndTemporaryProbeHistory(): } } + public function testCanonicalLiveDocumentationAvoidsInternalRoadmapAndPrivateAutomationWording(): void + { + foreach ($this->canonicalLiveDocumentationFiles() as $path) { + $content = $this->readProjectFile($path); + + $this->assertDoesNotMatchPattern($this->roadmapIdentifierPattern(), $content); + $this->assertDoesNotMatchPattern('/\b(?:Codex|ChatGPT)\b/i', $content); + $this->assertDoesNotMatchPattern('/\bNotion\b.*(?:workflow|page|project|task|acceptance)|(?:workflow|page|project|task|acceptance).*?\bNotion\b/i', $content); + $this->assertDoesNotMatchPattern('/assistant review/i', $content); + $this->assertDoesNotMatchPattern('/usage limit/i', $content); + $this->assertDoesNotMatchPattern('/AI-generated/i', $content); + $this->assertDoesNotMatchPattern('/AUDIT ACCEPTED|ASSISTANT REVIEW|LOCAL \/ PRE-COMMIT|COMMITTED-REF GATE|PR MERGE GATE|FULLY ACCEPTED/i', $content); + $this->assertDoesNotMatchPattern('/(?:tool|assistant|automation)\s+credits?|credits?\s+(?:for|on|remaining|used by)\s+(?:tool|assistant|automation)/i', $content); + } + } + + public function testCanonicalRoadmapGuardrailRecognizesInternalIdentifierForms(): void + { + foreach (array( + 'Phase 2', + 'Phase 5.6B', + 'Phase 6.5B1', + 'Phase 6.5C2', + ) as $roadmapIdentifier) { + $this->assertMatchesPattern($this->anchoredRoadmapIdentifierPattern(), $roadmapIdentifier); + } + + foreach (array( + 'registration phase', + 'boot phase', + ) as $technicalPhrase) { + $this->assertDoesNotMatchPattern($this->roadmapIdentifierPattern(), $technicalPhrase); + } + } + + public function testAgentsGuideIsPublicNeutralAutomationGuidance(): void + { + $content = $this->readProjectFile('AGENTS.md'); + + $this->assertMatchesPattern('/# Automation and Contribution Guide/i', $content); + $this->assertMatchesPattern('/automation tools|coding assistants/i', $content); + $this->assertMatchesPattern('/contributors/i', $content); + $this->assertMatchesPattern('/repository work/i', $content); + $this->assertMatchesPattern('/RED -> GREEN -> REFACTOR/', $content); + } + public function testPackagesReadmeLinksToDevelopmentGuideAndDoesNotOwnDetailedDeptracPolicy(): void { $content = $this->readProjectFile('packages/README.md'); @@ -125,22 +171,17 @@ public function testPackagesReadmeLinksToDevelopmentGuideAndDoesNotOwnDetailedDe $this->assertDoesNotMatchPattern('/Before Phase 2\.3/i', $content); } - public function testCoreReadmeDocumentsCorrectPhase3LifecycleChronology(): void + public function testCoreReadmeDocumentsRuntimeFoundationCapabilities(): void { $content = $this->readProjectFile('packages/core/README.md'); - $this->assertDoesNotMatchPattern( - '/Phase 3\.5\s+extends\s+`Evolve\\\\Core\\\\ApplicationKernel`\s+as the initial lifecycle implementation/i', - $content, - ); - $this->assertMatchesPattern( - '/Phase 3\.1 introduced `Evolve\\\\Core\\\\ApplicationKernel` as the initial lifecycle implementation/i', + '/`Evolve\\\\Core\\\\ApplicationKernel`.*lifecycle implementation|lifecycle implementation.*`Evolve\\\\Core\\\\ApplicationKernel`/i', $content, ); $this->assertMatchesPattern( - '/in Phase 3\.5, runtime-neutral execution orchestration/i', + '/runtime foundation.*boot-time configuration validation.*service-registry freezing.*explicit execution scopes.*runtime-neutral execution orchestration/is', $content, ); } @@ -232,6 +273,34 @@ private function expectedArchitectureMatrixLines() ); } + private function canonicalLiveDocumentationFiles() + { + return array( + 'AGENTS.md', + 'README.md', + 'DEVELOPMENT.md', + 'packages/README.md', + 'packages/contracts/README.md', + 'packages/core/README.md', + 'packages/http/README.md', + 'packages/module/README.md', + 'packages/plugin/README.md', + 'packages/testing/README.md', + 'packages/dev-tools/README.md', + 'skeleton/README.md', + ); + } + + private function roadmapIdentifierPattern() + { + return '/\bPhase\s+\d+(?:\.\d+[A-Z0-9]*)*\b/'; + } + + private function anchoredRoadmapIdentifierPattern() + { + return '/^\bPhase\s+\d+(?:\.\d+[A-Z0-9]*)*\b$/'; + } + private function trackedReadmes() { $readmes = array(); diff --git a/tests/Documentation/EvolvePhp2ReleaseReadinessTest.php b/tests/Documentation/EvolvePhp2ReleaseReadinessTest.php index e764443..d7a3383 100644 --- a/tests/Documentation/EvolvePhp2ReleaseReadinessTest.php +++ b/tests/Documentation/EvolvePhp2ReleaseReadinessTest.php @@ -143,7 +143,7 @@ public function testWorkspaceReadmeDocumentsReleaseValidationBoundaries(): void '/## Release Validation/', '/composer release:validate/', '/deterministic\/offline|offline.*deterministic/i', - '/seven packages.*mapped explicitly|mapped explicitly.*seven packages|extends.*map.*seven packages/i', + '/seven packages.*mapped explicitly|mapped explicitly.*seven packages|map contains seven packages/i', '/dependency-compatible/i', '/package-local README/i', '/package-local.*licen[cs]es/i', @@ -154,9 +154,9 @@ public function testWorkspaceReadmeDocumentsReleaseValidationBoundaries(): void '/package Composer manifests remain authoritative/i', '/distinct from `quality`/i', '/distinct from.*`supply-chain`/i', - '/package splitting is Phase 2\.10B/i', + '/package splitting.*release:split:validate|release:split:validate.*package splitting/i', '/remote synchronization.*Packagist.*deferred/i', - '/prerelease consumer stability.*2\.10B/i', + '/prerelease consumer stability.*offline consumer matrix|offline consumer matrix.*prerelease consumer stability/i', '/RFC 0003 remains authoritative/i', ) as $pattern) { $this->assertMatchesPattern($pattern, $content); diff --git a/tests/Documentation/EvolvePhp2ReleaseSplitAndConsumerValidationTest.php b/tests/Documentation/EvolvePhp2ReleaseSplitAndConsumerValidationTest.php index f49ec64..9f6578f 100644 --- a/tests/Documentation/EvolvePhp2ReleaseSplitAndConsumerValidationTest.php +++ b/tests/Documentation/EvolvePhp2ReleaseSplitAndConsumerValidationTest.php @@ -403,8 +403,6 @@ public function testWorkspaceReadmeDocumentsAlphaConsumerPolicyAndDeferredPublic 'application CLI composition is explicit', 'Core remains independent of HTTP', 'Packagist create-project availability is not yet claimed', - 'Phase 6.4E', - 'Phase 6.4F', 'minimum-stability: alpha', 'prefer-stable: true', 'Explicit root `@alpha` flags', diff --git a/tests/Documentation/EvolvePhp2RepositoryGovernanceTest.php b/tests/Documentation/EvolvePhp2RepositoryGovernanceTest.php index f4d4061..9d4ff66 100644 --- a/tests/Documentation/EvolvePhp2RepositoryGovernanceTest.php +++ b/tests/Documentation/EvolvePhp2RepositoryGovernanceTest.php @@ -58,14 +58,14 @@ public function testCanonicalDocumentationKeepsStableCutoverDeferred() $this->assertMatchesPattern('/no `?main`? branch has been created|`?main`? branch has not been created/is', $content); $this->assertMatchesPattern('/no `?1\.x`? branch has been created|`?1\.x`? branch has not been created/is', $content); $this->assertMatchesPattern('/stable-release.*(?:rename|promotion).*deferred|deferred.*stable-release.*(?:rename|promotion)/is', $content); - $this->assertMatchesPattern('/Phase 2\.7.*does not replace.*legacy history|legacy history.*not.*replaced.*Phase 2\.7/is', $content); + $this->assertMatchesPattern('/legacy history.*preserved|preserved.*legacy history/i', $content); } public function testFinalGovernanceRulesetsAreDocumented() { $content = $this->readProjectFile('README.md') . "\n" . $this->readProjectFile('CHANGELOG.md'); - $this->assertMatchesPattern('/Phase 2\.7B.*completed.*external governance transition|external governance transition.*completed.*Phase 2\.7B/is', $content); + $this->assertMatchesPattern('/repository rulesets? (?:actively )?protect.*branch lines|branch lines.*protect.*repository rulesets?/is', $content); $this->assertMatchesPattern('/repository rulesets? (?:are )?active|active repository rulesets?/i', $content); $this->assertMatchesPattern('/master.*pull request.*deletion.*force-push|master.*force-push.*deletion.*pull request/is', $content); $this->assertMatchesPattern('/2\.x.*pull request.*deletion.*force-push.*required CI status checks.*strict|2\.x.*strict.*required CI status checks.*force-push.*deletion.*pull request/is', $content); @@ -75,6 +75,7 @@ public function testFinalGovernanceRulesetsAreDocumented() $this->assertMatchesPattern('/Policy \(PHP 8\.4\)/', $content); $this->assertMatchesPattern('/Workspace quality \(PHP 8\.4\)/', $content); $this->assertMatchesPattern('/Workspace quality \(PHP 8\.5\)/', $content); + $this->assertMatchesPattern('/Benchmark policy \(PHP 8\.4\)/', $content); $this->assertDoesNotMatchPattern($this->staleTransitionPattern(), $content); $this->assertDoesNotMatchPattern('/classic branch protection|branch protection is active/i', $content); $this->assertDoesNotMatchPattern('/master (?:was|has been) (?:renamed|deleted|replaced)/i', $content);