Skip to content

engineer: add EnforceResourceDataValidatorOptInRule (queue #55 Phase-2 promotion)#20

Merged
Goosterhof merged 1 commit intomainfrom
engineer/enforce-resource-data-validator-opt-in
May 7, 2026
Merged

engineer: add EnforceResourceDataValidatorOptInRule (queue #55 Phase-2 promotion)#20
Goosterhof merged 1 commit intomainfrom
engineer/enforce-resource-data-validator-opt-in

Conversation

@Goosterhof
Copy link
Copy Markdown
Contributor

Summary

Promotes kendo PR #1084's Pest arch test (third instance of the arch test detects misuse but not omission enforcement shape) from Level-1 territory-local to Level-2 cross-territory PHPStan rule. Fires on classes extending App\Http\Resources\ResourceData that declare a non-empty EAGER_LOAD_COUNT / EAGER_LOAD_SUM constant but never call validateRelationsLoaded() — the silent-zero bug closed by kendo PR #1079 (KD-0494) re-introduces silently without the opt-in call.

  • Doctrine: ADR-0009 §EAGER_LOAD validator opt-in.
  • Identifier: enforceResourceDataValidatorOptIn.missingValidatorCall.
  • Configurable base FQCN: resourceDataBaseClass parameter (default App\Http\Resources\ResourceData).

Provenance

  • Source-of-truth Pest arch test: kendo backend/tests/Arch/ResourcesTest.php lines 157-265 at commit db20ea9cf (merged 2026-05-07).
  • War-room enforcement queue: #55.
  • /intel campaign: campaigns/war-room/2026-05-07-intel-pattern-analysis.md.
  • Commander disposition (2026-05-07): extract now, do not wait for a fourth instance — criterion was procedural, not principled; rule shape is namespace-agnostic and PHPStan-portable today.

Scope

This dispatch covers only the ResourceData shape. Sister extractions surfaced by queue #55 are deferred to separate dispatches:

Implementation notes

  • Registers on InClassNode (not bare Class_) so $node->getClassReflection() is reliably available — at the bare Class_ node, scope has not yet entered the class, so reflection may be null.
  • Inheritance gate uses PHPStan's ClassReflection::isSubclassOf() — FQCN-based, not short-name, so App\Unrelated\ResourceData collisions do not match.
  • Empty-array constants (EAGER_LOAD_COUNT = []) are no-ops and do not fire.
  • Compliant call shapes: self::, static::, and $this-> (instance form accepted for liberal compatibility with kendo's Pest matcher even though the production base method is protected static).

CHANGELOG

Entry filed under [Unreleased] / Added flagging this as a candidate Major bump per ADR-0021 §Versioning ("any rule that would surface new errors in already-clean code waits for a major bump"). The release PR (separate dispatch) will determine whether this collapses into the same Major 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 — kendo's standing proof point (ProjectGithubRepoResourceData) was already closed in PR #1084, but other consumer territories may carry undetected violators.

Verification gate (all green locally)

  • composer test — 41/41 (9 new test cases on this rule)
  • composer phpstan (self-analysis at level max) — 0 errors
  • composer format:check — Pint pass
  • composer coverage:check — 85.67% line coverage > 83% threshold (up from 83.92% baseline)
  • composer mutation:ci — 79.57% MSI > 75% threshold (up from 78.5% baseline; 10 escapes on the new rule, all in shared walkNodes boilerplate already accepted on the parity-acknowledged copy in EnforceAuditSnapshotOnRetryRule)
  • Commit count: 1 (rule + tests + docs bundled per order's "≤2" cap)

Test plan

  • CI green on both PHP 8.4 and 8.5 matrix legs.
  • Coverage gate ratchet headroom preserved (already 2.67pp above the 83% threshold post-merge).
  • Mutation gate ratchet headroom preserved (4.57pp above the 75% threshold post-merge).
  • Pre-cascade audit before tagging the release PR — surface any App\Http\Resources\* subclasses across emmie / kendo / entreezuil / ublgenie / brick-inventory that declare EAGER_LOAD_COUNT / EAGER_LOAD_SUM without calling validateRelationsLoaded(). kendo's standing violators were already closed by kendo PR #1084.

🤖 Generated with Claude Code

…2 promotion)

Promotes kendo PR #1084's Pest arch test (third instance of the "arch test
detects misuse but not omission" enforcement shape) from Level-1 territory-
local to Level-2 cross-territory PHPStan rule. Fires on classes extending
`App\Http\Resources\ResourceData` that declare a non-empty
`EAGER_LOAD_COUNT` / `EAGER_LOAD_SUM` constant but never call
`validateRelationsLoaded()` — the silent-zero bug closed by kendo PR #1079
(KD-0494) re-introduces silently without the opt-in call.

Doctrine: ADR-0009 §EAGER_LOAD validator opt-in.
Identifier: enforceResourceDataValidatorOptIn.missingValidatorCall.
Configurable base FQCN: `resourceDataBaseClass` parameter (default
`App\Http\Resources\ResourceData`).

Source-of-truth Pest arch test: kendo `tests/Arch/ResourcesTest.php`
lines 157-265 at commit `db20ea9cf` (merged 2026-05-07).
War-room enforcement queue: #55.
/intel campaign: campaigns/war-room/2026-05-07-intel-pattern-analysis.md.
Commander disposition (2026-05-07): extract now, do not wait for a fourth
instance — criterion was procedural, not principled; rule shape is namespace-
agnostic and PHPStan-portable today.

This dispatch covers ONLY the ResourceData shape. Sister extractions for
the FormRequest `toDto()` omission shape (queue #55 instance #2 — entreezuil
already has the stronger Pest arch test form) and the routes `->can()`
omission shape (queue #55 instance #1) are deferred to separate dispatches.

Implementation notes:
- Registers on `InClassNode` (not bare `Class_`) so `getClassReflection()`
  is reliably available — at the bare `Class_` node, scope has not yet
  entered the class so reflection may be null.
- Inheritance gate uses PHPStan's `ClassReflection::isSubclassOf()` —
  FQCN-based, not short-name, so `App\Unrelated\ResourceData` collisions
  do not match.
- Empty-array constants (`EAGER_LOAD_COUNT = []`) are no-ops and do not
  fire.
- Compliant call shapes: `self::`, `static::`, and `$this->` (instance
  form accepted for liberal compatibility with kendo Pest matcher even
  though the production base method is `protected static`).

CHANGELOG entry filed under `[Unreleased] / Added` flagging this as a
candidate Major bump per ADR-0021 §Versioning. Release PR (separate
dispatch) will determine whether this collapses into the same Major as
the staged LogRule static-call expansion or cuts a separate Major.

Verification gate (all green):
- composer test: 41/41 (added 9 new test cases on this rule)
- composer phpstan (self-analysis at level max): 0 errors
- composer format:check: pass
- composer coverage:check: 85.67% > 83% threshold (up from 83.92% baseline)
- composer mutation:ci: 79.57% MSI > 75% threshold (up from 78.5% baseline)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Goosterhof Goosterhof force-pushed the engineer/enforce-resource-data-validator-opt-in branch from 68edf2b to 1bbdf80 Compare May 7, 2026 16:58
@Goosterhof Goosterhof merged commit 4f50136 into main May 7, 2026
2 checks passed
@Goosterhof Goosterhof deleted the engineer/enforce-resource-data-validator-opt-in branch May 7, 2026 17:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants