## Summary This PR implements Spec 206 end to end and establishes the first checked-in test suite governance foundation for the platform app. Key changes: - add manifest-backed test lanes for fast-feedback, confidence, browser, heavy-governance, profiling, and junit - add budget and report helpers plus app-local artifact generation under `apps/platform/storage/logs/test-lanes` - add repo-root Sail-friendly lane/report wrappers - switch the default contributor test path to the fast-feedback lane - introduce explicit fixture profiles and cheaper defaults for shared tenant/provider test setup - add minimal/heavy factory states for tenant and provider connection setup - migrate the first high-usage and provider-sensitive tests to explicit fixture profiles - document budgets, taxonomy rules, DB reset guidance, and the full Spec 206 plan/contracts/tasks set ## Validation Executed during implementation: - focused Spec 206 guard/support/factory validation pack: 31 passed - provider-sensitive regression pack: 29 passed - first high-usage caller migration pack: 120 passed - lane routing and wrapper validation succeeded - pint completed successfully Measured lane baselines captured in docs: - fast-feedback: 176.74s - confidence: 394.38s - heavy-governance: 83.66s - browser: 128.87s - junit: 380.14s - profiling: 2701.51s - full-suite baseline anchor: 2624.60s ## Notes - Livewire v4 / Filament v5 runtime behavior is unchanged by this PR. - No new runtime routes, product UI flows, or database migrations are introduced. - Panel provider registration remains unchanged in `bootstrap/providers.php`. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #239 |
||
|---|---|---|
| .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
Test Suite Governance
Canonical Lane Commands
- Preferred repo-root wrappers:
./scripts/platform-test-lane fast-feedback./scripts/platform-test-lane confidence./scripts/platform-test-lane heavy-governance./scripts/platform-test-lane browser./scripts/platform-test-lane profiling./scripts/platform-test-lane junit
- Regenerate the latest report artifacts without re-running the lane:
./scripts/platform-test-report fast-feedback./scripts/platform-test-report confidence./scripts/platform-test-report heavy-governance./scripts/platform-test-report browser./scripts/platform-test-report profiling./scripts/platform-test-report junit
- App-local equivalents remain available through Sail Composer scripts:
cd apps/platform && ./vendor/bin/sail composer run testcd apps/platform && ./vendor/bin/sail composer run test:confidencecd apps/platform && ./vendor/bin/sail composer run test:heavycd apps/platform && ./vendor/bin/sail composer run test:browsercd apps/platform && ./vendor/bin/sail composer run test:profilecd apps/platform && ./vendor/bin/sail composer run test:junit
- The root wrapper is the safer default for long lanes because it pins Composer to
--timeout=0.
Recorded Baselines
| Scope | Wall clock | Budget | Notes |
|---|---|---|---|
| Full suite baseline | 2624.60s |
reference only | Current broad-suite measurement used as the budget anchor |
fast-feedback |
176.74s |
200s |
More than 50% below the current full-suite baseline |
confidence |
394.38s |
450s |
Broader non-browser pre-merge lane |
heavy-governance |
83.66s |
120s |
Seed heavy family lane for architecture, deprecation, ops UX, and action-surface scans |
browser |
128.87s |
150s |
Dedicated browser smoke and workflow lane |
junit |
380.14s |
450s |
Parallel machine-readable report lane for the confidence scope |
profiling |
2701.51s |
3000s |
Serial slow-test drift lane with profile output |
Artifacts are written under apps/platform/storage/logs/test-lanes and kept out of git except for the checked-in skeleton .gitignore.
Honest Taxonomy Rules
Unit: isolated logic, helpers, and low-cost domain behavior.Feature: HTTP, Livewire, Filament, jobs, and non-browser integration slices.Browser: only end-to-end browser smoke and workflow coverage undertests/Browser.heavy-governance: intentionally expensive architecture, deprecation, ops UX, and wide contract scans. The first seeded batch istests/Architecture,tests/Deprecation,tests/Feature/078,tests/Feature/090,tests/Feature/144,tests/Feature/OpsUx,tests/Feature/Filament/Alerts/AlertsKpiHeaderTest.php,tests/Feature/Guards/ActionSurfaceContractTest.php,tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php, andtests/Feature/ProviderConnections/CredentialLeakGuardTest.php.
Fixture Cost Guidance
createUserWithTenant()now defaults to the explicit cheapminimalprofile.- Use
createMinimalUserWithTenant()in high-usage callers that only need tenant membership and workspace/session wiring. - Use
fixtureProfile: 'provider-enabled'when a test needs a default Microsoft provider connection. - Use
fixtureProfile: 'credential-enabled'when provider identity resolution, admin-consent URLs, or Graph option construction needs dedicated credentials. - Use
fixtureProfile: 'ui-context'when the helper should also seed current tenant UI context and clear capability caches. - Use
fixtureProfile: 'heavy'only when the full side-effect bundle is intentionally required.
DB Reset and Seed Rules
- Default lanes use SQLite
:memory:withRefreshDatabaseas the reset strategy. - The isolated PostgreSQL coverage remains the
Pgsqlsuite and is reserved for schema or foreign-key assertions. - Keep seeds out of default lanes. Opt into seeded fixtures only inside the test that needs business-truth seed data.
- Schema-baseline or dump-based acceleration remains a follow-up investigation, not a default requirement for the current lane model.
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