Development
Enter the reproducible development environment and install ordinary mutable Composer dependencies for interactive work:
nix develop
composer install
Before submitting a change, run the complete normal validation suite:
nix flake check --keep-going -L
That command runs the supported-PHP PHPUnit matrix, PHPStan, php-cs-fixer, Composer and PHP linting, documentation formatting, the mdBook build and link check, Larastan and minimum-PHPStan consumer checks, and the pinned Laravel runtime audits. It does not run mutation testing.
Focused Composer commands remain useful while developing:
composer exec phpunit
composer exec phpstan analyse
composer cs
composer validate --strict
Choose a test layer with Testing and Runtime Verification.
Documentation
Build and check the mdBook:
composer docs
composer docs:check
composer docs:serve
composer docs:check builds the book and fails on broken same-site
relative links in the generated HTML.
The sidebar keeps every page’s h2/h3 outline open. mdBook only injects
headings for the active page, so docs/theme/phpstan-laravel-validation.js
adds the remaining outlines from headingsByChapter. Update that map when
public headings change.
The optional Heliogenesis control is mounted from
docs/theme/phpstan-laravel-validation.js. The unmodified Doctrine
runtime lives under docs/pages/assets/heliogenesis/. The theme marks the
reading plane so the event can light the article and run document
tomography.
The Document Looks Back integration is mounted separately from
docs/pages/assets/document-looks-back/. After it mounts,
window.documentLooksBack is the Doctrine controller, so
window.documentLooksBack.summon() requests one immediate eye. A mount
or renderer failure of either integration leaves the documentation usable.
The copied-runtime tests live in the documentation PHPUnit group. They
compare those assets to the root Composer Doctrine pin. Laravel-matrix
and minimum-PHPStan Nix jobs exclude the group because those lockfiles
do not install that pin.
Akashi formats inline PHP fences in the README, changelog, and docs/:
composer docs:format
composer docs:format:fix
composer cs:fix formats PHP source and those documentation fences.
Akashi checks formatting. It does not execute illustrative fragments as standalone programs.
The published site is
https://jbboehr.github.io/phpstan-laravel-validation/.
.github/workflows/pages.yml
builds the book on develop and master, and deploys only from master.
Mutation testing
Mutation testing uses an isolated toolchain because Infection requires
PHP 8.3 or newer while this package supports PHP 8.1. It is excluded from
nix flake check. Run it explicitly:
nix build -L .#mutation
The package supplies PHP 8.5 with PCOV and preserves the thresholds,
timeouts, test exclusions, and worker count from infection.json5.dist.
It divides the source into five cached shard derivations, each using four
Infection workers, then aggregates the project-wide thresholds. The exhaustive
GitHub workflow schedules those derivations as independent jobs and checks
their uploaded summaries in a final aggregate job. Each runner therefore uses
at most four Infection workers while the five shards can progress in parallel.
The aggregate JSON and a failed timeout-budget check identify each timed-out
mutant by shard, source location, mutator, and Infection ID.
PHPStan type-inference fixtures that cover extension code must run their
first gatherAssertTypes() analysis inside the test body. Data providers
run before PHPUnit starts coverage and can warm PHPStan’s process-level
caches. The test-only AssertsFixtureUnderCoverage trait implements this
pattern. Infection runs only the individual test cases that cover each
mutant.
Tests in the subprocess group remain in the normal suite but are
excluded from mutation testing because child processes cannot observe the
active in-process mutant. The property group is also excluded: rerunning the
complete finite catalogs for each mutant would be disproportionate.
Promoted deterministic regressions remain available to Infection.
The Infection configuration also ignores mutations that make
RuleTreeNode::resolvePath() recurse without consuming input. Those
mutants can exhaust PHP’s native stack before Infection’s timeout can
stop the process.
The php85 Nix development shell includes PCOV for focused manual
investigation.
Nix dependency hashes
Nix builds offline Composer repositories from committed lockfiles. When
Composer dependencies change, update the hashes in
nix/vendor-hashes.nix as described in
CONTRIBUTING.md.
CI
.github/workflows/ci.yml has two
surfaces:
- a conventional PHP baseline job on PHP 8.5 (Composer, PHPUnit, PHPStan, php-cs-fixer);
- an exhaustive Nix matrix generated from flake checks, plus mutation.
The conventional job uploads PHPUnit’s JUnit XML even when the test step fails, preserving per-test outcomes and timings for diagnosis and feedback-loop audits.
Nix matrix jobs use the daemon’s default build directory under
/nix/var/nix/builds. A directory under the runner’s temporary directory can
be inaccessible to Nix build users and fail before the check starts. The report
collector runs with sudo on the hosted Ubuntu runner so it can recover
reports from daemon-owned failed-build directories as well as successful
outputs. PHPUnit and mutation jobs retain failed build directories. Mutation
jobs upload infection.log, infection-summary.json, and
infection-summary.log when available, including after a timeout-threshold
failure. These artifacts preserve mutant identities and diagnostics for
reproduction; an incomplete report still causes aggregation to fail.
Before the Nix matrix fans out, one job builds every pinned Composer vendor closure and saves the resulting Nix store under a derivation-specific cache key. Every matrix job requires an exact cache restore. This keeps the jobs independent without making each runner download the same Composer archives.
A newer run supersedes an older run for the same branch or pull-request ref. Push and pull-request refs remain separate because they validate different Git states.
A documentation failure is a flake-check failure. It is not silent.
Downstream investigations
Pinned application investigations live under
docs/development/. They are evidence for
specific experiments, not user-facing support promises.