Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 16 additions & 16 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,19 @@ Composer package distributing war-room-doctrine PHPStan rules across `script-dev
| `EnforceActionTransactionsRule` | ADR-0011 | `enforceActionTransactions.missingTransaction` |
| `ForbidDatabaseManagerInActionsRule` | ADR-0021 §Why ConnectionInterface | `forbidDatabaseManager.inAction` |
| `ForbidAbortHelperRule` | War-room §Explicit over implicit | `forbidAbortHelper.abortUsed` |
| `ForbidHttpExceptionInActionsRule` | War-room §Explicit over implicit + §FormRequest → DTO → Action | `forbidHttpExceptionInActions.httpExceptionInAction` (type-aware sibling of `ForbidAbortHelperRule`; bans throwing the `Symfony\…\HttpException` family from `App\Actions\*`. `Illuminate\Validation\ValidationException` out of scope. `[Unreleased]`) |
| `ForbidResourceWrappedInJsonResponseRule` | War-room §Explicit over implicit + ADR-0009 | `forbidResourceWrappedInJsonResponse.resourceWrapped` (type-aware; bans wrapping a `JsonResource` in `response()->json()` / `new JsonResponse()` in `App\Http\Controllers\*`. Named-envelope nesting excluded. `[Unreleased]`) |
| `ForbidInlineArrayJsonResponseInControllersRule` | ADR-0009 | `forbidInlineArrayJsonResponseInControllers.arrayPayload` (type-aware; bans constructing the base `JsonResponse` (exact-FQCN, NOT subclasses) / `response()->json()` with an ARRAY payload in `App\Http\Controllers\*`. Inverse of `ForbidResourceWrappedInJsonResponseRule`. `fromJsonString` a deliberate miss. Seed kendo PR #1653. `[Unreleased]`) |
| `LogRule` | ADR-0001 §Append-only | `logRule.logModification` (covers instance `update`/`delete`/`forceDelete`/`forceDeleteQuietly`; static `Model::destroy()` / `Model::forceDestroy()` ship with v0.3.0 per `[Unreleased]`) |
| `LogBuilderTruncateRule` | ADR-0001 §Append-only | `logRule.logModification` (shared with `LogRule`; covers `Builder->truncate()` on Log-named tables — ships with v0.3.0 per `[Unreleased]`) |
| `ForbidHttpExceptionInActionsRule` | War-room §Explicit over implicit + §FormRequest → DTO → Action | `forbidHttpExceptionInActions.httpExceptionInAction` (type-aware sibling of `ForbidAbortHelperRule`; bans throwing the `Symfony\…\HttpException` family from `App\Actions\*`. `Illuminate\Validation\ValidationException` out of scope. shipped v0.5.0) |
| `ForbidResourceWrappedInJsonResponseRule` | War-room §Explicit over implicit + ADR-0009 | `forbidResourceWrappedInJsonResponse.resourceWrapped` (type-aware; bans wrapping a `JsonResource` in `response()->json()` / `new JsonResponse()` in `App\Http\Controllers\*`. Named-envelope nesting excluded. shipped v0.5.0) |
| `ForbidInlineArrayJsonResponseInControllersRule` | ADR-0009 | `forbidInlineArrayJsonResponseInControllers.arrayPayload` (type-aware; bans constructing the base `JsonResponse` (exact-FQCN, NOT subclasses) / `response()->json()` with an ARRAY payload in `App\Http\Controllers\*`. Inverse of `ForbidResourceWrappedInJsonResponseRule`. `fromJsonString` a deliberate miss. Seed kendo PR #1653. on `main`, `[Unreleased]` — pending v0.8.0 tag (release PR #53)) |
| `LogRule` | ADR-0001 §Append-only | `logRule.logModification` (covers instance `update`/`delete`/`forceDelete`/`forceDeleteQuietly`; static `Model::destroy()` / `Model::forceDestroy()` shipped in v0.3.0) |
| `LogBuilderTruncateRule` | ADR-0001 §Append-only | `logRule.logModification` (shared with `LogRule`; covers `Builder->truncate()` on Log-named tables — shipped in v0.3.0) |
| `EnforceAuditSnapshotOnRetryRule` | ADR-0001 §Snapshot-on-Retry Safety | `enforceAuditSnapshotOnRetry.firstStatementMustResetState` |
| `EnforceAuditTransactionScopeRule` | ADR-0029 | `enforceAuditTransactionScope.nonTransactionalMutationInClosure` |
| `ForbidEloquentMutationInControllersRule` | ADR-0011 + ADR-0019 | `forbidEloquentMutationInControllers.eloquentMutationInController` |
| `EnforceResourceDataValidatorOptInRule` | ADR-0009 §EAGER_LOAD validator opt-in | `enforceResourceDataValidatorOptIn.missingValidatorCall` |
| `EnforceFormRequestToDtoRule` | ADR-0012 §FormRequest → DTO Flow | `enforceFormRequestToDto.missingToDtoMethod` |
| `EnforceCurrentUserAttributeRule` | War-room §Explicit over implicit | `enforceCurrentUserAttribute.useAttributeInsteadOfRequestUser` |
| `EnforceAuditModelProtectionsRule` | ADR-0001 §Append-only | `enforceAuditModelProtections.hasFactoryForbidden` / `.softDeletesForbidden` / `.updatedAtNotDisabled` (denylist-inversion; discovers audit models by shape — `auditModelNameSuffixes` default `AuditLog` OR `auditModelNamespacePrefixes` default `App\Models\Audit` — and flags `HasFactory` / `SoftDeletes` / missing `const UPDATED_AT = null`. `[Unreleased]`) |
| `EnforceActionResultDtoRule` | ADR-0020 + ADR-0011 | `enforceActionResultDto.arrayReturnFromExecute` (signature-only; flags an `array` / `?array` / `array\|Dto` union / `iterable` native return type on `App\Actions\*` `execute()`. Phpdoc-only `@return array{...}` is a deliberate miss; no `list<T>` carve-out. Seed kendo PR #1653. `[Unreleased]`) |
| `EnforceAuditModelProtectionsRule` | ADR-0001 §Append-only | `enforceAuditModelProtections.hasFactoryForbidden` / `.softDeletesForbidden` / `.updatedAtNotDisabled` (denylist-inversion; discovers audit models by shape — `auditModelNameSuffixes` default `AuditLog` OR `auditModelNamespacePrefixes` default `App\Models\Audit` — and flags `HasFactory` / `SoftDeletes` / missing `const UPDATED_AT = null`. shipped v0.7.0) |
| `EnforceActionResultDtoRule` | ADR-0020 + ADR-0011 | `enforceActionResultDto.arrayReturnFromExecute` (signature-only; flags an `array` / `?array` / `array\|Dto` union / `iterable` native return type on `App\Actions\*` `execute()`. Phpdoc-only `@return array{...}` is a deliberate miss; no `list<T>` carve-out. Seed kendo PR #1653. on `main`, `[Unreleased]` — pending v0.8.0 tag (release PR #53)) |
| `ConnectionTransactionReturnTypeExtension` | (type extension, no rule) | — |

Phase 2 expands the rule set: `EnforceAuditSnapshotOnRetryRule` (ADR-0001 §Snapshot-on-Retry Safety) was the first Phase 2 addition, promoted from cross-territory Pest arch tests (emmie PR #187, entreezuil PR #139, ublgenie PR #166, kendo PR #1029). `EnforceResourceDataValidatorOptInRule` (ADR-0009 §EAGER_LOAD validator opt-in) is the second Phase 2 addition, promoted from kendo PR #1084 under war-room enforcement queue #55. `EnforceFormRequestToDtoRule` (ADR-0012) is the third Phase 2 addition, promoted from entreezuil's `tests/Arch/FormRequestsTest.php` under the same queue #55 (instance 2). `EnforceExplicitHydrationRule` (ADR-0019) is the next Phase 2 candidate.
Expand Down Expand Up @@ -85,7 +85,7 @@ SemVer per ADR-0021:

> Distilled operational rules from cross-project Architecture Decision Records.
> Canonical full ADRs at [adrs.script.nl](https://adrs.script.nl). This section is owned by the war room — do not edit directly.
> Last synced: 2026-06-15
> Last synced: 2026-07-14 (latest released tag v0.7.0; `EnforceActionResultDtoRule` + `ForbidInlineArrayJsonResponseInControllersRule` on `main`, pending the v0.8.0 tag / release PR #53)

### Applicable

Expand All @@ -96,22 +96,22 @@ SemVer per ADR-0021:

> Each bullet's rule→doctrine mapping is authoritative per the rule class's docblock "Doctrine source" line (ADR-0021 §Doctrine source in docblock).

- ADR-0001 (Audit Logging) — package distributes `LogRule` + `LogBuilderTruncateRule` (both §Append-only), `EnforceAuditSnapshotOnRetryRule` (§Snapshot-on-Retry Safety), and `EnforceAuditModelProtectionsRule` (§Append-only — flags audit-log models, discovered by shape, that use `HasFactory` / `SoftDeletes` or fail to disable `updated_at`; a denylist inversion of the consumer-side audit-model arch tests, `[Unreleased]`); does not itself maintain audit logs.
- ADR-0001 (Audit Logging) — package distributes `LogRule` + `LogBuilderTruncateRule` (both §Append-only), `EnforceAuditSnapshotOnRetryRule` (§Snapshot-on-Retry Safety), and `EnforceAuditModelProtectionsRule` (§Append-only — flags audit-log models, discovered by shape, that use `HasFactory` / `SoftDeletes` or fail to disable `updated_at`; a denylist inversion of the consumer-side audit-model arch tests, shipped v0.7.0); does not itself maintain audit logs.
- ADR-0002 (Cascade Deletion) — no application surface.
- ADR-0009 (Unified ResourceData Pattern) — package distributes `EnforceResourceDataValidatorOptInRule` (§EAGER_LOAD validator opt-in, shipped in v0.3.0), `ForbidResourceWrappedInJsonResponseRule` (resources own their own response serialization — bans wrapping a `JsonResource` in `response()->json()` / `new JsonResponse()` inside controllers, `[Unreleased]`), and `ForbidInlineArrayJsonResponseInControllersRule` (the inverse — bans building the base `JsonResponse` / `response()->json()` from an ARRAY payload inside controllers; response shapes belong to a Resource / dedicated JsonResponse subclass, `[Unreleased]`); does not itself ship API resources.
- ADR-0011 (Action Class Architecture) — package distributes `EnforceActionTransactionsRule` + `ForbidDatabaseManagerInActionsRule`, and `ForbidEloquentMutationInControllersRule` (ADR-0011 + ADR-0019, `[Unreleased]`); itself has no Actions.
- ADR-0012 (FormRequest → DTO) — package distributes `EnforceFormRequestToDtoRule` (§FormRequest → DTO Flow, `[Unreleased]`); itself has no HTTP surface.
- ADR-0009 (Unified ResourceData Pattern) — package distributes `EnforceResourceDataValidatorOptInRule` (§EAGER_LOAD validator opt-in, shipped in v0.3.0), `ForbidResourceWrappedInJsonResponseRule` (resources own their own response serialization — bans wrapping a `JsonResource` in `response()->json()` / `new JsonResponse()` inside controllers, shipped v0.5.0), and `ForbidInlineArrayJsonResponseInControllersRule` (the inverse — bans building the base `JsonResponse` / `response()->json()` from an ARRAY payload inside controllers; response shapes belong to a Resource / dedicated JsonResponse subclass, on `main`, `[Unreleased]` — pending v0.8.0 tag / PR #53); does not itself ship API resources.
- ADR-0011 (Action Class Architecture) — package distributes `EnforceActionTransactionsRule` + `ForbidDatabaseManagerInActionsRule`, and `ForbidEloquentMutationInControllersRule` (ADR-0011 + ADR-0019, shipped v0.4.0); itself has no Actions.
- ADR-0012 (FormRequest → DTO) — package distributes `EnforceFormRequestToDtoRule` (§FormRequest → DTO Flow, shipped v0.4.0; `toDtos()` plural support v0.6.1); itself has no HTTP surface.
- ADR-0014 (Domain-Driven Frontend) — no frontend.
- ADR-0016 (Config Attribute Injection) — no Laravel container surface.
- ADR-0017 (Page Integration Tests) — no pages.
- ADR-0019 (Explicit Model Hydration) — package distributes `ForbidEloquentMutationInControllersRule` (ADR-0011 + ADR-0019, `[Unreleased]`) covering the controller mutation surface; itself has no models. (The earlier Phase-2 `EnforceExplicitHydrationRule` candidate has been subsumed by the controller-mutation rule for the controller surface; a broader application-wide hydration rule remains a future candidate.)
- ADR-0020 (Input/Result DTO Split) — package distributes `EnforceActionResultDtoRule` (bans an `array` native return type on `App\Actions\*` `execute()` — a compound result is a Result DTO, not a bag of string keys; ADR-0020 + ADR-0011, `[Unreleased]`); itself has no DTOs.
- ADR-0019 (Explicit Model Hydration) — package distributes `ForbidEloquentMutationInControllersRule` (ADR-0011 + ADR-0019, shipped v0.4.0) covering the controller mutation surface; itself has no models. (The earlier Phase-2 `EnforceExplicitHydrationRule` candidate has been subsumed by the controller-mutation rule for the controller surface; a broader application-wide hydration rule remains a future candidate.)
- ADR-0020 (Input/Result DTO Split) — package distributes `EnforceActionResultDtoRule` (bans an `array` native return type on `App\Actions\*` `execute()` — a compound result is a Result DTO, not a bag of string keys; ADR-0020 + ADR-0011, on `main`, `[Unreleased]` — pending v0.8.0 tag / PR #53); itself has no DTOs.
- ADR-0024 (Automated External Provisioning) — no provisioning surface.
- ADR-0029 (Audit Row Durability Contract) — package distributes `EnforceAuditTransactionScopeRule` (§Decision rule 3 — flags non-transactional state mutations inside `transaction(...)` closures in `App\Actions\*`, `[Unreleased]`); itself maintains no audit rows.
- ADR-0029 (Audit Row Durability Contract) — package distributes `EnforceAuditTransactionScopeRule` (§Decision rule 3 — flags non-transactional state mutations inside `transaction(...)` closures in `App\Actions\*`, shipped v0.4.0); itself maintains no audit rows.

### War-room Architectural Principle rules (no published ADR)

- **Explicit over implicit** — package distributes `ForbidAbortHelperRule` (bans `abort()` / `abort_if()` / `abort_unless()`; shipped), `EnforceCurrentUserAttributeRule` (flags `Request::user()` / `Auth::user()` / `auth()->user()` in `App\Http\Controllers`, steering to the `#[CurrentUser]` container attribute per Architectural Principle #9; `[Unreleased]`), `ForbidHttpExceptionInActionsRule` (type-aware sibling of `ForbidAbortHelperRule` — bans throwing the `Symfony\…\HttpException` family from `App\Actions\*`; HTTP status concerns belong to the HTTP layer per Principles #1 + #3; `ValidationException` deliberately out of scope; `[Unreleased]`), and `ForbidResourceWrappedInJsonResponseRule` (bans wrapping a `JsonResource` in `response()->json()` / `new JsonResponse()` inside controllers per Principle #1 + ADR-0009; `[Unreleased]`). These enforce war-room §Architectural Principles (the last two also touching numbered ADRs) — each rule's docblock "Doctrine source" line names its authority.
- **Explicit over implicit** — package distributes `ForbidAbortHelperRule` (bans `abort()` / `abort_if()` / `abort_unless()`; shipped), `EnforceCurrentUserAttributeRule` (flags `Request::user()` / `Auth::user()` / `auth()->user()` in `App\Http\Controllers`, steering to the `#[CurrentUser]` container attribute per Architectural Principle #9; shipped v0.4.0), `ForbidHttpExceptionInActionsRule` (type-aware sibling of `ForbidAbortHelperRule` — bans throwing the `Symfony\…\HttpException` family from `App\Actions\*`; HTTP status concerns belong to the HTTP layer per Principles #1 + #3; `ValidationException` deliberately out of scope; shipped v0.5.0), and `ForbidResourceWrappedInJsonResponseRule` (bans wrapping a `JsonResource` in `response()->json()` / `new JsonResponse()` inside controllers per Principle #1 + ADR-0009; shipped v0.5.0). These enforce war-room §Architectural Principles (the last two also touching numbered ADRs) — each rule's docblock "Doctrine source" line names its authority.

### War-room internal ADRs

Expand Down
Loading