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

255 lines
20 KiB
Markdown

# 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)
```text
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)
```text
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.