Skip to main content

D23 — Fundamentals-readiness hardening (triaged from the 2026-06-03 audit)

Architecture decision record, relocated verbatim from the retired single-file docs/tenancy/ decision log. Status, thematic clusters, and how to record a new ADR: the decision log index.

A 20-agent readiness audit (9 dimensions, adversarially verified + a completeness critic) found the architecture mature but flagged enforcement/DX gaps. The findings were triaged with the user into devops (deferred to a pipeline workstream), parallel features, and do-now hardening. This entry records the do-now work; the rest is tracked under docs/tracking/.

Done now (all green — full suite + arch + PHPStan level 6 + Pint + scramble:export):

  • Starter-kit settings removed. The Laravel starter-kit operator settings Livewire screens (Profile/Security/DeleteUserForm/Appearance/TwoFactor\RecoveryCodes) mutated Eloquent directly, bypassing the bus. Deleted them + their views, the settings/* routes, the Livewire registrations, the global settings layout/partials, and their feature tests. Operator auth (login/2FA/passkeys/reset) and the dashboard are kept; the backoffice UI will be rebuilt afresh with every mutation dispatched through the CommandBus. (Logout is kept — it touches only Auth/session, not Eloquent.)
  • Self-service registration disabled on the control plane (Features::registration() removed, register view + binding + the sign-up link removed, RegistrationTest deleted). Operators are provisioned, never self-signed-up. (Audit blocker.)
  • CQRS read/write architecture guards (tests/Architecture/CqrsBoundaryTest.php): the raw DB facade is confined to the bus + sanctioned internals, and the module Http + Livewire presentation layers may not import Eloquent models / the query builder / DB — so every domain read and write must flow through the bus. New presentation namespaces slot into the list (same per-module convention as ModuleBoundaryTest).
  • PHPStan + Larastan added (larastan/larastan ^3.0, phpstan.neon.dist level 6, currently green; composer stan / make stan). 16 pre-existing type issues fixed in the process — including a real bug in the ported JsonApiEnvelopeExtension (getArg() never returns null, so a spurious message field leaked into list-envelope docs). No CI wiring (per instruction; the CI gap is tracked in docs/tracking/devops-hardening.md).
  • JSON:API collection contract (Modules\Foundation\Http\Resources\JsonApiCollection, returned by JsonApiResource::collection()): list responses now carry the same meta block as single resources plus JSON:API-shaped pagination (meta.page counts + top-level links), under application/vnd.api+json. Reference shape for every future index endpoint (test: tests/Feature/Foundation/JsonApiCollectionTest.php).
  • DX: tenant-aware test helpers (Tests\TestCase::actingAsClient() + runInTenant()); make stan / composer stan.

Tracked separately (see docs/tracking/): devops (CI PHPStan step, RLS deploy gate, prod Horizon/scheduler, version parity), the durable audit trail (parallel feature), and the API tooling/SPA-types/env-parity task. Deferred follow-ups: a CQRS module/command scaffolding generator; the JSON:API collection Scramble extension + a filter/sort whitelist (land with the first index endpoint); AuthenticateSession on auth routes; error tracking/observability.


← Decision log index