## Summary - add the first multi-app workspace foundation with a new standalone Astro website under `apps/website` - introduce repo-root pnpm workspace orchestration and migrate the platform Node workflow from npm assumptions to pnpm - update root docs, editor or agent guidance, and workspace-focused smoke tests for the new platform plus website command model - add Spec 183 artifacts for spec, plan, research, contracts, quickstart, checklist, and tasks ## Verification - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/WorkspaceFoundation` - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `corepack pnpm build:website` - integrated-browser smoke: verified `http://localhost/up`, `http://localhost/admin/login`, and `http://localhost:4321/` including website anchor navigation and combined root dev flow ## Notes - branch: `183-website-workspace-foundation` - commit: `6d41618d` - root command model now covers `dev:platform`, `dev:website`, `dev`, `build:platform`, and `build:website` - website port override documentation is included in the command contract, quickstart, and README Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #214 |
||
|---|---|---|
| .agents/skills | ||
| .ai/guidelines | ||
| .codex | ||
| .gemini | ||
| .gitea | ||
| .github | ||
| .specify | ||
| apps | ||
| docs | ||
| scripts | ||
| spechistory | ||
| specs | ||
| .dockerignore | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .npmignore | ||
| .prettierignore | ||
| Agents.md | ||
| boost.json | ||
| docker-compose.yml | ||
| GEMINI.md | ||
| opencode.json | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
TenantPilot Workspace
TenantPilot is an Intune management platform built around a stable Laravel application in
apps/platform and, starting with Spec 183, a standalone public Astro website in
apps/website. The repository root is now the official JavaScript workspace entry point and
orchestrates app-local commands without becoming a runtime itself.
Multi-App Topology
apps/platform: the Laravel 12 + Filament v5 + Livewire v4 product runtimeapps/website: the Astro v6 public website runtime- repo root: workspace manifests, documentation, scripts, editor tooling, and
docker-compose.yml ./scripts/platform-sail: platform-only compatibility helper for tooling that cannot setcwd
Official Root Commands
- Install workspace-managed JavaScript dependencies:
corepack pnpm install - Start the platform stack:
corepack pnpm dev:platform - Start the website dev server:
corepack pnpm dev:website - Start platform + website together:
corepack pnpm dev - Build the website:
corepack pnpm build:website - Build platform frontend assets:
corepack pnpm build:platform
App-Local Commands
Platform
- Install PHP dependencies:
cd apps/platform && composer install - Start Sail:
cd apps/platform && ./vendor/bin/sail up -d - Generate the app key:
cd apps/platform && ./vendor/bin/sail artisan key:generate - Run migrations and seeders:
cd apps/platform && ./vendor/bin/sail artisan migrate --seed - Run frontend watch/build inside Sail:
cd apps/platform && ./vendor/bin/sail pnpm devorcd apps/platform && ./vendor/bin/sail pnpm build - Run tests:
cd apps/platform && ./vendor/bin/sail artisan test --compact
Website
- Start the dev server:
cd apps/website && pnpm dev - Build the static site:
cd apps/website && pnpm build
Port Overrides
- Platform HTTP and Vite ports: set
APP_PORTand orVITE_PORTbeforecorepack pnpm dev:platformorcd apps/platform && ./vendor/bin/sail up -d - Website dev server port: set
WEBSITE_PORTbeforecorepack pnpm dev:websiteor pass--port <port>tocd apps/website && pnpm dev - Parallel local development keeps both apps isolated, even when one or both ports are overridden
Platform Setup Notes
- Filament admin:
/admin(seed usertest@example.com, set password via factory orartisan tinker). - Microsoft Graph (Intune) env vars:
GRAPH_TENANT_IDGRAPH_CLIENT_IDGRAPH_CLIENT_SECRETGRAPH_SCOPE(defaulthttps://graph.microsoft.com/.default)- Without these, the
NullGraphClientruns in dry mode (no Graph calls). - Required API Permissions: See docs/PERMISSIONS.md for complete list
- Missing permissions? Scope tags will show as "Unknown (ID: X)" - add
DeviceManagementRBAC.Read.All
- Missing permissions? Scope tags will show as "Unknown (ID: X)" - add
- Deployment (Dokploy, staging → production):
- Containerized deploy; ensure Postgres + Redis are provisioned (see
docker-compose.ymlfor local baseline). - Run application commands from
apps/platform, includingphp artisan filament:assets. - Run migrations on staging first, validate backup/restore flows, then promote to production.
- Ensure queue workers are running for jobs (e.g., policy sync) after deploy.
- Keep secrets/env in Dokploy, never in code.
- Containerized deploy; ensure Postgres + Redis are provisioned (see
Platform relocation rollout notes
- Open branches that still touch legacy root app paths should merge
devfirst, then remap file moves fromapp/,bootstrap/,config/,database/,lang/,public/,resources/,routes/,storage/, andtests/intoapps/platform/.... - Keep using merge-based catch-up on shared feature branches; do not rebase long-lived shared branches just to absorb the relocation.
- VS Code tasks expose the official root workspace commands, while MCP launchers remain platform-only and delegate through
./scripts/platform-sail.
Bulk operations (Feature 005)
- Bulk actions are available in Filament resource tables (Policies, Policy Versions, Backup Sets, Restore Runs).
- Destructive operations require type-to-confirm at higher thresholds (e.g.
DELETE). - Long-running bulk ops are queued; the bottom-right progress widget polls for active runs.
Troubleshooting
- Progress stuck on “Queued…” usually means the queue worker is not running (or not processing the queue you expect).
- Prefer using the Sail/Docker worker (see
docker-compose.yml) rather than starting an additional localphp artisan queue:work. - Check worker status/logs:
cd apps/platform && ./vendor/bin/sail psandcd apps/platform && ./vendor/bin/sail logs -f queue.
- Prefer using the Sail/Docker worker (see
- Exit code 137 for
queue:worktypically means the process was killed (often OOM). Increase Docker memory/limits or run the worker inside the container. - Moved app but old commands still fail usually means the command is still being run from repo root. Switch to
cd apps/platform && ...or use./scripts/platform-sail ...only for tooling that cannot setcwd.
Rollback checklist
- Revert the relocation commit or merge on your feature branch instead of hard-resetting shared history.
- Preserve any local app env overrides before switching commits:
cp apps/platform/.env /tmp/tenantatlas.platform.env.backupif needed. - Stop local containers and clean generated artifacts:
cd apps/platform && ./vendor/bin/sail down -v, then removeapps/platform/vendor,apps/platform/node_modules,apps/platform/public/build, andapps/platform/public/hotif they need a clean rebuild. - After rollback, restore the matching env file for the restored topology and rerun the documented setup flow for that commit.
- Notify owners of open feature branches that the topology changed so they can remap outstanding work before the next merge from
dev.
Deployment unknowns
- Dokploy build context for a repo-root compose file plus an app-root Laravel runtime still needs staging confirmation.
- Production web, queue, and scheduler working directories must be verified explicitly after the move; do not assume repo root and app root behave interchangeably.
- Any Dokploy volume mounts or storage persistence paths that previously targeted repo-root
storage/must be reviewed againstapps/platform/storage/.
Configuration
TENANTPILOT_BULK_CHUNK_SIZE(default10): job refresh/progress chunk size.TENANTPILOT_BULK_POLL_INTERVAL_SECONDS(default3): Livewire polling interval for the progress widget (clamped to 1–10s).
Intune RBAC Onboarding Wizard
- Entry point: Tenant detail in Filament (
Setup Intune RBACin the ⋯ ActionGroup). Visible only for active tenants withapp_client_id. - Flow (synchronous, delegated):
- Configure Role (default Policy/Profile Manager), Scope (global or scope group), Group mode (create default
TenantPilot-Intune-RBACor pick existing security-enabled group). Review planned changes. - Delegated admin login (short-lived token, not stored in DB/cache).
- Execute: resolve service principal, ensure/validate security group, ensure membership, ensure/create/patch Intune role assignment; persists IDs on tenant for idempotency; no queue.
- Post-verify: forces fresh token, runs canary reads (deviceConfigurations/deviceCompliancePolicies; CA canary only if feature enabled), updates health and warnings (scope-limited, CA disabled, manual assignment required).
- Configure Role (default Policy/Profile Manager), Scope (global or scope group), Group mode (create default
- Safety/notes: least-privilege default, idempotent reruns, “already exists” treated as success. If service principal missing, run Admin consent first. Scope-limited setups may yield partial inventory/restore; warnings are surfaced in UI and health panel.
Graph Contract Registry & Drift Guard
- Registry:
config/graph_contracts.phpdefines per-type contracts (resource paths, allowed$select/$expand, @odata.type family, create/update methods, id field, hydration). - Client behavior:
- Sanitizes
$select/$expandto allowed fields; logs warnings on trim. - Derived @odata.type values within the family are accepted for preview/restore routing.
- Capability fallback: on 400s related to select/expand, retries without those clauses and surfaces warnings.
- Sanitizes
- Drift check:
cd apps/platform && php artisan graph:contract:check [--tenant=]runs lightweight probes against contract endpoints to detect capability/shape issues; useful in staging/CI (prod optional). - If Graph returns capability errors, TenantPilot downgrades safely, records warnings/audit entries, and avoids breaking preview/restore flows.
Policy Settings Display
- Policy detail pages render normalized settings instead of raw JSON:
- OMA-URI/custom policies → path/value table
- Settings Catalog → flattened key/value entries
- Standard objects → labeled key/value view with metadata filtered
- Version detail pages show both pretty-printed JSON and normalized settings.
- Warnings surface malformed snapshots or @odata.type mismatches before restore.
Policy JSON Viewer (Feature 002)
- Location: Policy View pages (
/admin/policies/{record}) - Capability: Pretty-printed JSON snapshot viewer with copy-to-clipboard
- Settings Catalog Enhancement: Dual-view tabs (Settings table + JSON viewer) for Settings Catalog policies
- Features:
- Copy JSON to clipboard with success message
- Large payload detection (>500 KB) with warning badge and auto-collapse
- Dark mode support integrated with Filament design system
- Browser native search (Cmd+F / Ctrl+F) for finding specific keys or values
- Scrollable container with max height to prevent page overflow
- Usage: See
specs/002-filament-json/quickstart.mdfor detailed examples and configuration - Performance: Optimized for payloads up to 1 MB; auto-collapse improves initial render for large snapshots