actionFragmentContext was built spec-driven in step 1 to handle the
anticipated drawer-fragment pattern: authorize + finder → status DTO.
The action-context rollout (steps 2-11) found that none of the three
real drawer fragments (users/view-fragment, addressbook/view-fragment,
helpdesk/ticket-fragment) match that pattern — all three delegate to
domain services that own their own status models. Step 11 declared
them structural exceptions; the helper code remained unused.
This commit removes the dead spec:
* core/Support/helpers/action_context.php drops actionFragmentContext
(~9 LOC) and its building block actionFragmentResolveOrStatus
(~30 LOC) plus their docblocks. The MUST-call-actionRequireCsrf
warning, present in three aggregator docblocks before, now appears
twice (one per remaining aggregator).
* tests/Support/Helpers/ActionContextHelperTest.php drops the six
unit tests that exercised these functions (~83 LOC).
* tests/Architecture/ActionContextHelperContractTest.php drops the
fragment building block from the buildingBlocks() data provider
(5 entries instead of 6) and removes testFragmentResolveReturnDocblockIsFrozen.
The CSRF-warning expectation is updated from 3 to 2 with a code
comment explaining the rollback.
Verified:
* No production caller exists in pages/ or modules/.
* All 9 aggregator callers (5 actionEditContext + 4 actionCreateContext)
remain unchanged.
* ActionContextCsrfPairingContractTest and DetailDrawerFragmentContractTest
are deliberately left untouched: their allowlists/recognizer regexes
still mention actionFragmentContext, but the patterns now match an
empty set — harmless dead text. Documented as open items in the run
report; future cleanup is optional and orthogonal to this removal.
* QGs all green (PHPUnit 2088 tests, PHPStan level 5, CS-fixer 0 diffs).
Net: 3 files, +31/-198 LOC, behavior unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pilot migration of pages/admin/departments/edit($id).php onto the
actionEditContext aggregator introduced in step 1. The CONTEXT-stage
vorspiel (lookup → authorize → tenant-scope → can_view_page → viewAuth)
collapses into a single declarative call; the POST branch (CSRF →
SUBMIT-authorize → can_update gate → service call → PRG) stays
callsite-specific as planned.
Three deliberate touches beyond a 1:1 lift:
* Additive aggregator extension: actionEditContext gains an optional
notFoundFlashScopeKey arg so the dedup scope-key 'department_not_found'
is preserved without widening the frozen actionResolveModelOrFail
building-block signature. Pattern is documented as the
forward-compatibility mechanism for future cluster migrations.
* t() consistency: the not-found message now flows through t() via the
aggregator. To avoid a partial-translation mix, Flash::success calls
for 'Department updated' (×2) are also wrapped — German users now see
fully translated messages instead of a German/English mix.
* Defensive scope consumption: the action now consults
$tenantScope['scope'] before falling through to the strict-mode
fallback. The Departments policy never emits can_manage_all_tenants
today (so behavior is identical), but the action is now resilient to
future policies that might.
New ActionContextCsrfPairingContractTest enforces actionRequireCsrf()
before any POST body access for actions that use the aggregators —
preventing CSRF-pairing regressions during the cluster-wide rollout
(step 3). The step-1 testNoProductionCallSitesYet guard is removed,
since departments-edit is now the first legitimate caller; the new
pairing test takes over its protective role with a more substantive
guarantee.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Six orthogonal building blocks plus three cluster aggregators in
core/Support/helpers/action_context.php, preparing consolidation of the
~40-60 line vorspiel duplicated across edit/create/view-fragment actions.
Step 1 of a planned 3-step rollout: no production call sites yet —
pages/ and modules/ are untouched. Architecture tests freeze the
building-block signatures and verify drawer-fragment AuthZ parity.
GR-SEC-009 is structurally enforced via the actionDeriveTenantScope
return shape (PHPStan array{scope: 'all'|'list', ids: list<int>});
'all' is unreachable without an explicit can_manage_all_tenants flag.
Aggregator docblocks carry a mandatory CSRF-pairing warning per
GR-SEC-001; actionBuildViewAuth flags the e()-escape obligation per
GR-SEC-010.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>