Skip to content

Commit 0fc4ce3

Browse files
Goosterhofclaude
andcommitted
docs: update CHANGELOG / README / CLAUDE.md for EnforceCurrentUserAttributeRule
- CHANGELOG.md [Unreleased] § Added — new entry capturing detection shape, scope gate, doctrine source, identifier, origin recon yield, versioning posture (candidate Major bump per ADR-0021 §Versioning), pre-cascade audit demand, multi-guard ergonomics note, and v1 out-of-scope items (Auth::guard('name')->user()). - README.md ## Rules table — new row in the canonical seven-rule table. - CLAUDE.md ## Rules shipped table — new row matching the rules-shipped format (rule | doctrine | identifier). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent e49f05c commit 0fc4ce3

3 files changed

Lines changed: 3 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
88

99
### Added
1010

11+
- `EnforceCurrentUserAttributeRule` — flags calls to `Request::user()` / `Auth::user()` / `auth()->user()` inside classes extending `Illuminate\Routing\Controller`. The canonical fix is Laravel's `#[\Illuminate\Container\Attributes\CurrentUser]` container attribute on the method parameter — eliminates the implicit-nullable-then-assert dance (`$user = $request->user(); assert($user instanceof User);`) introduced by emmie PR #263 (EMMIE-0197) and recurring across the war-room fleet. Doctrine: war-room §Architectural Principles — Explicit over implicit. Identifier: `enforceCurrentUserAttribute.useAttributeInsteadOfRequestUser`. Detection branches on three call shapes via `CallLike` registration (mirrors `LogRule` v0.3.0 shape): `MethodCall` on `Illuminate\Http\Request` subtype receiver (type-based via `ObjectType::isSuperTypeOf()`); `MethodCall` whose receiver is a `FuncCall('auth')` (AST-shape match — the helper's return type is unloaded in stub-only environments); `StaticCall` resolving to `Illuminate\Support\Facades\Auth` (FQCN comparison via `$scope->resolveName()`). Scoped to Controller descendants via `ClassReflection::isSubclassOf(Illuminate\Routing\Controller)` — FormRequest descendants (where `$this->user()` is canonical because container-attribute injection does not apply to `FormRequest::rules()` / `toDto()` / `authorize()` invocations), middleware, services, Actions, jobs, and console commands are silent. Origin: war-room cross-territory recon 2026-05-22 (50+ violations across codebook, ublgenie, entreezuil, emmie; kendo already clean with 30 adopted sites). **Versioning: per ADR-0021 §Versioning, candidate Major bump (the rule surfaces new errors in already-clean code wherever a consumer territory has un-migrated controllers). The release PR will determine whether this collapses into the existing v0.3.0 [Unreleased] block (already a Major) or cuts as a separate Major (v0.4.0) after v0.3.0 ships.** **Pre-cascade audit required before tagging** — consuming territories will need either Medic dispatches to migrate (ublgenie 6 sites, entreezuil 3 sites, emmie 2 sites) or PHPStan-baseline staging (codebook ~40+ sites — safer than mass-edit on lightly-staffed AVG/NEN-7510-downstream territory). Kendo gets a constraint bump only (zero violations). Multi-guard ergonomics (`#[CurrentUser] Client $client` resolves via client guard, `#[CurrentUser] User $user` via user guard — verified in emmie's `ClientController::me`) work as expected: Laravel dispatches by typed parameter. Out of scope v1: `Auth::guard('name')->user()` and other guard-specific resolution paths — rare, substitution is more nuanced (`#[CurrentUser(guard: 'name')]`), do not appear in the recon yield.
1112
- `EnforceResourceDataValidatorOptInRule` — flags classes extending `App\Http\Resources\ResourceData` that declare a non-empty `EAGER_LOAD_COUNT` or `EAGER_LOAD_SUM` constant but do not call `validateRelationsLoaded()` anywhere in their method bodies. Without the call, missing eager-load aggregates fail open as `0` / `null` instead of throwing — silently re-introducing the silent-zero bug closed by kendo PR #1079 (KD-0494). Doctrine: ADR-0009 §EAGER_LOAD validator opt-in. Identifier: `enforceResourceDataValidatorOptIn.missingValidatorCall`. Promoted from kendo PR #1084's Pest arch test (Armorer, merged 2026-05-07 at `db20ea9cf`) — the third instance of the "arch test detects misuse but not omission" enforcement shape, dispositioned for Phase-2 promotion under war-room enforcement queue #55 by the Commander on 2026-05-07. Inheritance is matched via PHPStan reflection (FQCN ancestor traversal) — short-name collisions in unrelated namespaces do NOT match. The base FQCN is parameterizable via the `resourceDataBaseClass` PHPStan parameter (default: `App\Http\Resources\ResourceData`); territories whose `ResourceData` lives elsewhere can override per consumer `phpstan.neon`. Compliant call shapes: `self::validateRelationsLoaded($model)`, `static::validateRelationsLoaded($model)`, `$this->validateRelationsLoaded($model)` (instance form accepted for liberal compatibility with the source-of-truth Pest matcher, even though the base method is `protected static`). Empty-array constants (`EAGER_LOAD_COUNT = []`) do NOT fire — they are no-ops. **Versioning: per ADR-0021 §Versioning, candidate Major bump (the rule surfaces new errors in already-clean code wherever a consumer territory has a `ResourceData` subclass declaring the aggregate constants without the validator call). The release PR will determine whether v0.3.0 collapses this rule into the same Major bump as the staged `LogRule` static-call expansion, or cuts a separate Major.** **Pre-cascade audit required across emmie, kendo, entreezuil, ublgenie, brick-inventory before tagging** — the kendo arch test already closed kendo's standing proof point (`ProjectGithubRepoResourceData`) in PR #1084, but other consumer territories may carry undetected violators. Sister extractions for the FormRequest `toDto()` omission shape (queue #55 instance #2) and the routes `->can()` middleware omission shape (queue #55 instance #1) are deferred to separate dispatches.
1213

1314
### Security

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Composer package distributing war-room-doctrine PHPStan rules across `script-dev
2727
| `LogBuilderTruncateRule` | ADR-0001 §Append-only | `logRule.logModification` (shared with `LogRule`; covers `Builder->truncate()` on Log-named tables — ships with v0.3.0 per `[Unreleased]`) |
2828
| `EnforceAuditSnapshotOnRetryRule` | ADR-0001 §Snapshot-on-Retry Safety | `enforceAuditSnapshotOnRetry.firstStatementMustResetState` |
2929
| `EnforceResourceDataValidatorOptInRule` | ADR-0009 §EAGER_LOAD validator opt-in | `enforceResourceDataValidatorOptIn.missingValidatorCall` |
30+
| `EnforceCurrentUserAttributeRule` | War-room §Explicit over implicit | `enforceCurrentUserAttribute.useAttributeInsteadOfRequestUser` |
3031
| `ConnectionTransactionReturnTypeExtension` | (type extension, no rule) ||
3132

3233
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. `EnforceExplicitHydrationRule` (ADR-0019) is the next Phase 2 candidate.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ includes:
4444
| `LogBuilderTruncateRule` | `logRule.logModification` | `Builder->truncate()` calls | If the fluent chain's most recent `table()` call targets a Log-named table (string-literal argument matching `"log"` / `"logs"`, case-insensitive), error. Sibling rule to `LogRule`; shares the `logRule.logModification` identifier so a single `ignoreErrors` entry covers both. Eloquent `from()` chains and Model-`$table`-property-driven tables are acceptable misses. Doctrine: ADR-0001 §Append-only. |
4545
| `EnforceAuditSnapshotOnRetryRule` | `enforceAuditSnapshotOnRetry.firstStatementMustResetState` | `App\Actions\*` whose constructor injects an entity audit logger | The first statement inside `$connection->transaction(...)` must reset the model's in-memory state (`$model->refresh()`, fresh fetch, or fresh instantiation). Doctrine: ADR-0001 §Snapshot-on-Retry Safety. |
4646
| `EnforceResourceDataValidatorOptInRule` | `enforceResourceDataValidatorOptIn.missingValidatorCall` | Classes extending `App\Http\Resources\ResourceData` | If the class declares a non-empty `EAGER_LOAD_COUNT` / `EAGER_LOAD_SUM` constant but never calls `validateRelationsLoaded()` in any method, error. |
47+
| `EnforceCurrentUserAttributeRule` | `enforceCurrentUserAttribute.useAttributeInsteadOfRequestUser` | `Request::user()` / `Auth::user()` / `auth()->user()` calls inside Controller descendants | Use `#[\Illuminate\Container\Attributes\CurrentUser] User $user` on the method parameter. FormRequest descendants, middleware, services, Actions, jobs, and console commands are silent — container-attribute injection does not apply to FormRequest methods, and the other contexts have their own canonical resolution paths. |
4748

4849
### `EnforceActionTransactionsRule` — write-method list
4950

0 commit comments

Comments
 (0)