## 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
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/platformso internal relative paths inartisan,public/index.php,bootstrap/app.php, PHPUnit bootstrap, Vite inputs, Tailwind@source, Filament theme imports, and Drizzle stay stable. - Keep
apps/platformas the one canonical working directory for platform development commands. - Keep
docker-compose.ymlat repo root and bridge app-local Sail to it viaSAIL_FILES, which the current Sail script already supports. - Mount only
./apps/platforminto/var/www/htmlfor 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, anddrizzle.config.tswith 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 rulescontracts/local-command-model.md: canonical developer command and delegation contractcontracts/runtime-smoke.openapi.yaml: preserved HTTP surface contract for relocation smoke validationquickstart.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 manytasks.jsonentries; 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:assetsstays part of deploy execution fromapps/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 buildand, where needed,npm run devvalidation 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.jsonor 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:assetsin the deployment flow executed fromapps/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.