TenantAtlas/specs/182-platform-relocation/plan.md
ahmido ce0615a9c1 Spec 182: relocate Laravel platform to apps/platform (#213)
## Summary
- move the Laravel application into `apps/platform` and keep the repository root for orchestration, docs, and tooling
- update the local command model, Sail/Docker wiring, runtime paths, and ignore rules around the new platform location
- add relocation quickstart/contracts plus focused smoke coverage for bootstrap, command model, routes, and runtime behavior

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PlatformRelocation`
- integrated browser smoke validated `/up`, `/`, `/admin`, `/admin/choose-workspace`, and tenant route semantics for `200`, `403`, and `404`

## Remaining Rollout Checks
- validate Dokploy build context and working-directory assumptions against the new `apps/platform` layout
- confirm web, queue, and scheduler processes all start from the expected working directory in staging/production
- verify no legacy volume mounts or asset-publish paths still point at the old root-level `public/` or `storage/` locations

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #213
2026-04-08 08:40:47 +00:00

20 KiB

Implementation Plan: Platform Relocation to apps/platform

Branch: 182-platform-relocation | Date: 2026-04-07 | Spec: /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/182-platform-relocation/spec.md Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/182-platform-relocation/spec.md

Summary

Relocate the entire Laravel platform application into apps/platform while turning the repo root into a true orchestration and metadata layer. The implementation keeps the app whole instead of splitting Composer, Vite, PHPUnit, Filament, Tailwind, or bootstrap files across root and app, so existing internal relative paths stay stable once moved. The main technical work is therefore at the boundaries: root docker-compose.yml, Sail invocation, repo documentation, MCP or agent configs, editor tasks, and rollback or smoke validation.

Key approach: move all app-owned Laravel files together, keep docker-compose.yml at repo root, let app-local Sail target the root compose file via SAIL_FILES, mount only ./apps/platform into the application containers, and update every repo-level command surface to either cd apps/platform && ... or transparently delegate there. Filament remains v5 on Livewire v4, provider registration remains in apps/platform/bootstrap/providers.php, no global-search contract changes are introduced, no destructive action semantics change, and deployment keeps the existing php artisan filament:assets step executed from apps/platform.

Technical Context

Language/Version: PHP 8.4.15, Laravel 12, Blade, Livewire v4, Filament v5.2.x, Tailwind CSS v4, Vite 7
Primary Dependencies: laravel/framework, filament/filament, livewire/livewire, laravel/sail, laravel-vite-plugin, tailwindcss, vite, pestphp/pest, drizzle-kit, PostgreSQL, Redis, Docker Compose
Storage: PostgreSQL, Redis, filesystem storage under the Laravel app storage/ tree, plus existing Vite build artifacts in public/build; no new database persistence planned
Testing: Pest v4 feature, unit, and browser tests plus PHPUnit config files, all executed through Sail after relocation
Target Platform: Containerized Laravel web app and queue workers for local Sail on macOS and Linux containers in staging or production Project Type: Laravel monolith relocated under a repo-root orchestration layer
Performance Goals: Preserve current boot, asset-build, queue-worker, and panel-load behavior; keep clean-checkout setup within the success criteria window; avoid extra path-indirection layers in runtime code
Constraints: No product behavior changes; no apps/website; no shared packages; no pnpm, Turbo, Nx, or CI/CD rebuild in this slice; one official command model only; root docker-compose.yml remains repo-level; external deploy assumptions must stay explicit
Scale/Scope: One full-app relocation affecting every Laravel-owned directory and app-local tooling file, plus root docs, tasks, agent configs, compose wiring, smoke validation, and rollback guidance

Constitution Check

GATE: Passed before Phase 0 research. Re-checked after Phase 1 design and still passing.

Principle Status Notes
Inventory-first Pass No inventory or snapshot ownership changes; only repo topology changes
Read/write separation Pass No new product mutation flow is introduced
Graph contract path Pass No Graph endpoint or contract registry change
Deterministic capabilities Pass Capability registry and RBAC remain unchanged
RBAC-UX planes and 404 vs 403 Pass /admin, /admin/t/{tenant}/..., and /system semantics must remain identical after relocation
Workspace isolation Pass No workspace-context broadening or alternate route path is introduced
Tenant isolation Pass Tenant-scoped routes remain tenant-scoped; relocation must not bypass existing guards
Global search safety Pass No searchable resource behavior is changed; any existing global-search contract remains as-is
Dangerous and destructive confirmations Pass No new destructive actions or placement changes are part of this slice
Run observability Pass Existing queued and remote flows keep current OperationRun semantics; smoke pack explicitly validates queue startup and run-adjacent runtime integrity
Ops-UX 3-surface feedback Pass Existing operation feedback flows remain unchanged because no operation UX redesign is introduced
Data minimization Pass No new logs, payload mirrors, or extra persisted truth are added
Proportionality (PROP-001) Pass The plan avoids website apps, workspaces, packages, and orchestration frameworks
No premature abstraction (ABSTR-001) Pass No new platform engine or repo framework is introduced; only relocation contracts and narrow delegation helpers are allowed
Persisted truth (PERSIST-001) Pass No new table or artifact source of truth is introduced
Behavioral state (STATE-001) Pass No new status or reason-code family is added
UI semantics (UI-SEM-001) Pass No new presentation layer or UI framework is introduced
Filament-native UI (UI-FIL-001) Pass Existing Filament pages and themes remain authoritative; only path resolution changes
Filament v5 / Livewire v4 compliance Pass The relocation keeps Filament v5 on Livewire v4 with no API downgrades or legacy mix-in
Provider registration location Pass Panel providers remain registered in bootstrap/providers.php, which simply moves with the app to apps/platform/bootstrap/providers.php
Global search hard rule Pass The move does not remove Edit or View pages from any globally searchable resource and does not enable search for any previously disabled resource
Asset strategy Pass Existing Vite-compiled assets and Filament themes remain app-local; deployment continues to run php artisan filament:assets from apps/platform
Testing plan Pass Validation covers public entry, admin or tenant or system panels, queue worker startup, build chain, and focused Pest smoke through Sail

Phase 0 Research

Research outcomes are captured in /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/182-platform-relocation/research.md.

Key decisions:

  • Move the whole Laravel project into apps/platform so internal relative paths in artisan, public/index.php, bootstrap/app.php, PHPUnit bootstrap, Vite inputs, Tailwind @source, Filament theme imports, and Drizzle stay stable.
  • Keep apps/platform as the one canonical working directory for platform development commands.
  • Keep docker-compose.yml at repo root and bridge app-local Sail to it via SAIL_FILES, which the current Sail script already supports.
  • Mount only ./apps/platform into /var/www/html for the web and queue services so runtime commands execute against the relocated app, not the whole repo.
  • Keep application config truth in apps/platform/.env, allowing only compose-specific variables outside the app when strictly necessary.
  • Move app-local tooling files such as composer.json, package.json, vite.config.js, phpunit*.xml, and drizzle.config.ts with the app while leaving repo-wide docs, tasks, MCP config, and agent config at root.
  • Treat Dokploy build context, production working directory, volume mapping, and unpublished operator wrappers as explicit rollout questions rather than implicit assumptions.

Phase 1 Design

Design artifacts are created under /Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/182-platform-relocation/:

  • data-model.md: logical relocation artifacts, ownership boundaries, and validation rules
  • contracts/local-command-model.md: canonical developer command and delegation contract
  • contracts/runtime-smoke.openapi.yaml: preserved HTTP surface contract for relocation smoke validation
  • quickstart.md: implementation-time smoke and rollback validation workflow

Design decisions:

  • No schema migration is required; this is a repo topology and runtime-path slice only.
  • Because the whole app moves together, Laravel bootstrap files, Filament providers, Vite config, PHPUnit config, Tailwind theme files, and Drizzle config largely stay intact relative to the app root.
  • The main runtime changes occur at repo-root orchestration boundaries: docker-compose.yml, env bridging for Sail, root-level wrappers or docs, editor tasks, MCP config, and agent guidance.
  • Root command references are broad and include README.md, Agents.md, GEMINI.md, .github/copilot-instructions.md, .vscode/mcp.json, opencode.json, and many tasks.json entries; these must be normalized to the single official command model or deliberate root delegators.
  • Existing Filament panels remain panel-local Vite theme consumers; php artisan filament:assets stays part of deploy execution from apps/platform.

Project Structure

Documentation (this feature)

specs/182-platform-relocation/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│   ├── local-command-model.md
│   └── runtime-smoke.openapi.yaml
├── checklists/
│   └── requirements.md
└── tasks.md

Source Code (repository root)

repo-root/
├── apps/
│   └── platform/
│       ├── app/
│       ├── bootstrap/
│       ├── config/
│       ├── database/
│       ├── public/
│       ├── resources/
│       ├── routes/
│       ├── storage/
│       ├── tests/
│       ├── artisan
│       ├── composer.json
│       ├── package.json
│       ├── vite.config.js
│       ├── phpunit.xml
│       ├── phpunit.pgsql.xml
│       ├── drizzle.config.ts
│       └── .env.example
├── docs/
├── specs/
├── scripts/
├── .specify/
├── .github/
├── .vscode/
├── docker-compose.yml
├── README.md
├── Agents.md
├── GEMINI.md
├── boost.json
└── opencode.json

Structure Decision: Repo-root orchestration plus one Laravel monolith under apps/platform. The whole app moves together so app-local pathing stays coherent. No additional app, shared package, or workspace tooling layer is introduced.

Implementation Strategy

Phase A — Establish The File Placement Contract And App Root

Goal: Move the full Laravel app as one unit and eliminate the hybrid root-plus-app state.

Step Area Change
A.1 App root Create apps/platform and move all Laravel-owned directories and files there: app, bootstrap, config, database, public, resources, routes, storage, tests, artisan, composer.json, package.json, vite.config.js, phpunit*.xml, drizzle.config.ts, .env.example
A.2 Root cleanup Remove repo-root authority for moved Laravel structures and keep only repo-level docs, scripts, tooling, and orchestration files
A.3 Placement matrix Publish the file-placement contract in maintained docs and document reviewed exceptions such as .gitignore, .dockerignore, and any retained root helper or wrapper

Phase B — Rewire Docker, Sail, And Runtime Boundaries

Goal: Keep the repo root as orchestration while letting the relocated app run as if it were still a normal Laravel project root.

Step Area Change
B.1 Compose Update root docker-compose.yml so web and queue services mount ./apps/platform into /var/www/html and execute commands against the relocated app
B.2 Sail bridge Use Sail's existing SAIL_FILES support so apps/platform/vendor/bin/sail can target the repo-root compose file without making root the official working directory
B.3 Env model Keep app config in apps/platform/.env; isolate any compose-only variables outside the canonical app config truth
B.4 Queue and runtime Validate queue commands, storage paths, Vite port wiring, and node_modules volume behavior under the relocated mount

Phase C — Align Build, Test, And Tooling Surfaces

Goal: Make every maintained command surface either app-local or an explicit delegator.

Step Area Change
C.1 Build and test config Preserve or minimally adjust moved vite.config.js, Tailwind theme sources, phpunit.xml, phpunit.pgsql.xml, Composer scripts, and Drizzle config so they work from apps/platform
C.2 MCP and agent config Update root .vscode/mcp.json, opencode.json, agent instructions, and related tooling to call apps/platform commands or wrappers explicitly
C.3 VS Code tasks Normalize tasks.json to the new app path and use the relocation to repair the current root-coupled task surface rather than layering more absolute-path tasks
C.4 Documentation Update README.md, Agents.md, GEMINI.md, and other root guidance to the single official command model

Phase D — Define Rollback And Branch Safety

Goal: Make the move recoverable and merge-aware.

Step Area Change
D.1 Rollback Document git rollback, env relocation recovery, dependency rebuild, build artifact cleanup, Docker cleanup, and worker recovery
D.2 Branch guidance Document open-branch merge expectations and the communication required before landing the topology move
D.3 External unknowns Capture Dokploy and production working-directory questions separately from the code move so rollout is blocked on evidence, not on guesswork

Phase E — Execute Smoke Validation

Goal: Prove the relocated topology works end-to-end before rollout.

Step Area Change
E.1 Local boot Verify install, Sail startup, CLI bootstrap, and the public health or entry endpoints
E.2 Build and panels Verify Vite build, manifest resolution, Filament admin, choose-workspace navigation, a representative tenant route with preserved 404 versus 403 semantics, and system panels and theme loading
E.3 Queue and tests Verify queue worker startup, a representative queued flow, and a focused Sail-driven Pest smoke pack
E.4 Tooling Verify at least one VS Code task or MCP or agent-assisted command path works under the relocated model and capture timing evidence for setup and rollback success criteria

Key Design Decisions

D-001 — Move The Whole Laravel App, Not A Split Root/App Hybrid

The relocation moves app-owned files together so Laravel, Vite, Tailwind, PHPUnit, Filament theme imports, and Drizzle continue to resolve relative paths from one coherent app root.

D-002 — apps/platform Remains The Official Working Directory Even With Root Compose

The root keeps docker-compose.yml, but app-local Sail bridges to it using SAIL_FILES, which the current Sail script already supports. This preserves the spec's app-first command model without forcing compose back into the app directory.

D-003 — Root Helpers Are Compatibility Only, Never A Second Primary Workflow

Any retained root wrappers or task aliases must do nothing except delegate into apps/platform. The plan forbids a dual-workflow state where root and app commands are equally official.

D-004 — Application Env Truth Lives In apps/platform

apps/platform/.env remains the canonical Laravel environment file. Compose-specific values may exist outside the app only when they are orchestration concerns rather than application config truth.

D-005 — This Slice Stops At Topology And Runtime Safety

No website app, no shared package layer, no workspace tooling, and no CI/CD redesign enter the implementation plan. The only acceptable complexity is what is needed to relocate the existing platform app safely.

Risk Assessment

Risk Impact Likelihood Mitigation
Sail cannot find the repo-root compose file when run from apps/platform High Medium Bridge with SAIL_FILES, document it in .env.example, and validate the exact command flow in smoke tests
Compose mounts the wrong directory, so web boots or queue workers run against stale root paths High Medium Mount only ./apps/platform, verify worker command resolution, and include queue smoke in validation
Repo docs and agent configs continue to recommend root-relative vendor/bin/sail and artisan commands High High Update all maintained docs and configs in the same slice and verify at least one MCP or task flow
tasks.json and other editor automation contain stale absolute root paths or malformed task definitions Medium High Treat tooling normalization as part of the implementation, not a follow-up
External deploy assumptions differ from local Sail behavior High Medium Keep Dokploy questions explicit and block rollout on verified answers rather than implicit parity
Open branches encounter large merge churn after the topology move lands Medium High Include explicit branch and rollback guidance in the rollout notes

Test Strategy

  • Validate the relocated stack with Sail from apps/platform, not by ad-hoc root shell commands.
  • Run CLI smoke through the relocated app: version or about, migrate:status, and a queue-related artisan command.
  • Run build smoke through the relocated app: npm run build and, where needed, npm run dev validation for HMR and manifest resolution.
  • Run preserved HTTP surface smoke for the public entry, admin shell, workspace chooser, one representative tenant-scoped route, one deny-as-not-found tenant access path, and the system shell using the contract in contracts/runtime-smoke.openapi.yaml.
  • Validate Filament v5 and Livewire v4 compliance by loading existing admin, tenant, and system panels after relocation; no new page or relation-manager tests are required because no operator surface behavior changes are introduced.
  • Run a focused Sail-driven Pest smoke pack from the relocated app. Existing tests remain the primary regression guard; the relocation does not require new domain behavior tests unless a runtime-specific regression appears.
  • Validate queue integrity by starting the queue container and exercising one representative queued flow or command path under the relocated topology.
  • Validate at least one tooling flow such as .vscode/mcp.json or a repaired VS Code task so repo automation does not drift behind the app move.
  • Capture elapsed setup and rollback timings according to the quickstart measurement protocol so the time-based success criteria are reviewable instead of anecdotal.
  • Preserve deployment asset behavior by keeping php artisan filament:assets in the deployment flow executed from apps/platform.

Complexity Tracking

No constitution violations or exception-driven complexity are expected. The plan deliberately rejects shared packages, workspace tooling, and multi-app orchestration to keep the relocation proportional.

Proportionality Review

  • Current operator problem: The repo root currently conflates application runtime and repo orchestration, which increases path ambiguity today and blocks a clean multi-app topology tomorrow.
  • Existing structure is insufficient because: Every maintained command, task, and container assumes the Laravel app is the repo root. That prevents root from acting as an independent repo layer.
  • Narrowest correct implementation: Move the whole existing app into apps/platform, rewire root orchestration, document one command model, and validate with smoke plus rollback.
  • Ownership cost created: A broad but mechanical path migration across compose, docs, tasks, and tool configs, plus rollback and branch communication discipline.
  • Alternative intentionally rejected: A larger monorepo introduction with website apps, shared packages, workspace tooling, or CI/CD redesign. It solves a different problem and creates unnecessary ownership cost now.
  • Release truth: Current-release infrastructure truth that prepares future surfaces without shipping them in this slice.