phpstan-warroom-rules — The Inspector General's Office
Single Composer package distributing canonical PHPStan rules that enforce war-room doctrine across script-development Laravel territories. Sister to fs-packages on the PHP side — fs-packages equips territories, phpstan-warroom-rules audits them.
Tech Stack
- Language: PHP 8.3+ (uses
private const stringsyntax) - Target framework: PHPStan 2.x (the package extends it)
- Test: PHPUnit 11 with
PHPStan\Testing\RuleTestCasefixtures - Format: Pint (canonical config from war-room
templates/pint.json) - Publish: Packagist via OIDC Trusted Publishing — no stored tokens
- CI: test → phpstan-self → format-check → release-on-tag
Architecture Overview
The package ships rules that sit at Level 2 of the war-room enforcement ladder (static analysis). Each rule maps to a doctrine source — an ADR or a war-room principle — and the class-level docblock names that source. When a rule is added, the docblock is the contract.
- Namespace.
ScriptDevelopment\PhpstanWarroomRules\(PSR-4,src/). - Action namespace assumption. Rules that scope to Actions match
App\Actions\*— Laravel convention used by every consuming territory. Onboarding a territory with a different namespace lifts this into a parameter rather than forks the rule. - No territory-specific exceptions in rule code. Per-territory false positives are suppressed via consumer
phpstan.neonignoreErrorsblocks. The donor patterns from emmie that hardcoded class names (Terminology::class) were dropped during promotion. - Type extensions ride alongside rules.
ConnectionTransactionReturnTypeExtensionresolves$connection->transaction(fn () => $foo)to the closure's return type, enabling strict typing of transaction call sites.
Rules Shipped (Phase 1)
| Rule | Identifier | Doctrine | Detects / Forbids |
|---|---|---|---|
EnforceActionTransactionsRule | enforceActionTransactions.missingTransaction | ADR-0011 | Action execute() with ≥2 write operations not wrapped in ->transaction(). |
ForbidDatabaseManagerInActionsRule | forbidDatabaseManager.inAction | ADR-0021 | Action constructors injecting DatabaseManager. Inject ConnectionInterface instead. |
ForbidAbortHelperRule | forbidAbortHelper.abortUsed | War-room "Explicit over implicit" | abort() / abort_if() / abort_unless() anywhere in App\*. Throw an explicit HttpException subclass. |
LogRule | logRule.logModification | ADR-0001 §append-only | update() / delete() calls on classes whose name contains "Log" / "logs" (case-insensitive). |
ConnectionTransactionReturnTypeExtension | — | — | Type extension; resolves transaction() closure return types. |
Phase 2 will add EnforceExplicitHydrationRule for ADR-0019.
Key Decisions
| Decision | Status | Impact |
|---|---|---|
| Canonical PHPStan Rules Package | Accepted | This territory is the canonical home of the package — origin doctrine. |
| Action Class Architecture | Accepted | EnforceActionTransactionsRule and ForbidDatabaseManagerInActionsRule enforce ADR-0011 at Level 2. |
| Audit Logging System | Accepted | LogRule codifies ADR-0001 §append-only across all territories. |
| ADR Governance | Accepted | Governance applies; projections live in this territory's CLAUDE.md. |
Versioning
Semantic versioning per ADR-0021:
- Major — a rule changes in a way that surfaces new errors in code that previously passed (e.g., expanding the write-method list, tightening
LogRule's match). - Minor — a new rule is added, or a rule gains an option that does not change defaults.
- Patch — bug fixes, false-positive suppression, performance.
Consuming territories pin ^1.0. Any rule that would surface new errors in already-clean code waits for a major bump.
Publishing
- Packagist under
script-development/phpstan-warroom-rules. - Trusted Publishing via Packagist's GitHub integration — no stored tokens.
- Branch protection on
main: required PR + 1 approval, dismiss stale reviews, enforce on admins. mainis always release-ready. Release PRs move[Unreleased]inCHANGELOG.mdto a versioned heading and tag the merge commit (v1.x.y).- Tag push triggers the Packagist publish pipeline.
Documentation
Public documentation lives in the package README.md on Packagist. The doctrine source for every rule is the corresponding ADR at adrs.script.nl. This territory does not maintain a separate documentation site — the rules are too few and the audience too narrow to justify one.
What This Territory Does Not Do
- Does not enforce its rules on its own source. The rules target Laravel application code (
App\Actions,App\*), not a static-analysis package. - Does not ship operational PHP code or services. It is a static-analysis library only.
- Does not own doctrine. Doctrine lives in ADRs; this territory is the enforcement vehicle.