|
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m4s
Implemented the consolidated operator guidance panel for the environment dashboard. Updated EnvironmentDashboardSummaryBuilder to prioritize and select guidance based on the operator guidance contract. Added comprehensive unit, feature, and browser tests to verify the guidance selection logic and UI rendering. |
||
|---|---|---|
| .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 | ||
| get_gitea_tools.py | ||
| opencode.json | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| spec351-after-publish-detail.png | ||
| spec351-after-publish-workspace.png | ||
| spec351-publish-modal-before-submit.png | ||
| spec351-ready-fixture-detail.png | ||
| spec351-ready-fixture-workspace.png | ||
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
Website-track guardrails for independent evolution live in
docs/strategy/website-working-contract.md.
Official Root Commands
- Install workspace-managed JavaScript dependencies:
corepack pnpm install - Start the platform stack and Laravel panel Vite watcher:
corepack pnpm dev:platform - Start the website dev server:
corepack pnpm dev:website - Start platform Vite + website together:
corepack pnpm dev - Build the website:
corepack pnpm build:website - Build platform frontend assets inside Sail:
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 - Apply incremental migrations on an already cut-over local database:
cd apps/platform && ./vendor/bin/sail artisan migrate - Initialize or reset the local database:
cd apps/platform && ./vendor/bin/sail artisan migrate:fresh --seed - Run frontend watch/build inside Sail:
corepack pnpm dev:platform,cd apps/platform && ./vendor/bin/sail pnpm dev, orcd 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
- Trend-aware report refresh options:
--history-file=/absolute/path/to/<lane>-latest.trend-history.jsonseeds one prior comparable window explicitly.--history-bundle=/absolute/path/to/bundle-or-ziphydrates the newest matchingtrend-history.jsonfrom a staged artifact bundle.--fetch-latest-historyasks the wrapper to download the most recent comparable bundle from Gitea whenTENANTATLAS_GITEA_TOKENorGITEA_TOKENis available.--skip-latest-historykeeps the run intentionally cold-start so the summary reportsunstableinstead of guessing at trend state.
- 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.
Trend Summary Reading
healthy: enough comparable samples exist, the lane is comfortably under budget, and recent variance stays inside the documented noise floor.budget-near: the lane is still within budget, but headroom has entered the lane's near-budget band and needs attention before it becomes a repeated blocker.trending-worse: multiple comparable samples are worsening above the lane variance floor even though the lane is not yet clearly over budget.regressed: the lane is over budget or repeatedly worsening enough that ordinary noise is no longer a credible explanation.unstable: the report intentionally refuses a stronger label because history is too short, the comparison fingerprint changed, or the recent window is noisy.- Recalibration is separate from health. Reports can emit candidate, approved, or rejected baseline or budget decisions, but repository truth never moves automatically.
- Hotspot evidence may be unavailable on a given cycle. When that happens the summary must say so explicitly, and
profilingorjunitremain the preferred support-lane follow-up paths.
Workflow Expectation
- Every runtime-changing or test-affecting spec, plan, and task set MUST record actual test-purpose classification, target validation lane(s), fixture-cost risks, any heavy-governance or browser expansion, any heavy-family visibility change, and any budget/baseline/trend follow-up.
- Test classification follows the real proving purpose of the change, not the filename or folder.
- Minimal fixtures and minimal infrastructure are the default; database, Livewire, Filament, provider, workspace, membership, or session-heavy setup must stay explicit and opt-in.
- Review treats wrong lane fit, hidden default cost, accidental heavy-family growth, or undocumented runtime drift as merge issues, not later cleanup.
- Routine lane recalibration belongs inside the affecting feature spec or PR; open a dedicated follow-up spec only when recurring pain or structural lane changes justify it.
Spec 288 Cutover Proof
- Spec
288is an enforcement-only package. It owns the named no-legacy guards, the two named browser smokes, and contributor-facing quality-gate wording. It does not own runtime cutover repair, Package Execution work, or a full-suite stabilization pass. - The pinned no-legacy heavy-governance proof is:
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Feature/Guards/Spec288NoLegacyRouteAndHelperGuardTest.php tests/Feature/Guards/Spec288ProviderCoreAndRoleAuthorityGuardTest.php tests/Feature/Guards/AdminWorkspaceRoutesGuardTest.php tests/Feature/Guards/ProviderBoundaryPlatformCoreGuardTest.php tests/Feature/ProviderConnections/LegacyRedirectTest.php tests/Feature/ManagedEnvironment/LegacyTenantCoreGuardTest.php tests/Feature/Spec080WorkspaceManagedTenantAdminMigrationTest.php tests/Feature/Rbac/ProviderConnectionWorkspaceFirstPolicyTest.php tests/Feature/Filament/ManagedEnvironmentAccessScopeManagementTest.php tests/Feature/Guards/BrowserLaneIsolationTest.php tests/Feature/Guards/CiLaneFailureClassificationContractTest.php tests/Feature/Guards/CiHeavyBrowserWorkflowContractTest.php tests/Unit/Auth/NoRoleStringChecksTest.php) - The pinned browser proof is:
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php tests/Browser/Spec285WorkspaceRbacEnvironmentAccessSmokeTest.php) - Keep the source-scan inventory narrow and explicit. The Spec
288guard family is limited to the named route/helper and provider/role guard files; do not widen it with broadtests/Feature/Guardspath allowlists or repo-wide tenant-panel helper bans. - When
heavy-governanceorbrowserreports broader baseline fallout outside those named proofs, classify it in the lane output and follow up separately. Under Spec288, that broader fallout remains classification-only and does not by itself expand repair ownership.
Authoring And Review Guardrails
- Start with the smallest honest surface:
Unitfor isolated logic,Featurefor HTTP, Livewire, Filament, jobs, or non-browser integration,heavy-governancefor intentionally expensive governance scans, andBrowseronly for end-to-end workflow coverage. - Specs and plans must state the affected lanes or a deliberate
N/A, the family impact, the setup-cost impact, and the narrowest reviewer command. - If database, Livewire, Filament, provider setup, workspace or membership context, session state, capability context, or browser coverage is required, say why a narrower proof is insufficient.
- Keep shared helpers, factories, seeds, fixtures, and defaults cheap by default. Full-context setup should stay behind explicit opt-ins instead of becoming the default path.
- Extend an existing heavy or browser family only when the behavior truly matches it. New heavy families, new browser scope, revived expensive defaults, or material lane-cost shifts require explicit escalation.
- Low-impact docs-only or template-only work may answer the governance prompts with
N/Aornone; do not invent runtime impact where none exists. - Review should end with one explicit outcome:
keep,split,document-in-feature,follow-up-spec, orreject-or-split. - Use
document-in-featurefor contained drift or cost that belongs in the active feature. Usefollow-up-specfor recurring pain or structural lane-model changes. Usereject-or-splitwhen hidden cost is still unresolved.
CI Trigger Matrix
- Pull requests (
opened,reopened,synchronize) run only./scripts/platform-test-lane fast-feedbackthrough.gitea/workflows/test-pr-fast-feedback.ymland block on test, wrapper, artifact, and mature Fast Feedback budget failures. - Pushes to
devrun only./scripts/platform-test-lane confidencethrough.gitea/workflows/test-main-confidence.yml; test and artifact failures block, while budget drift remains warning-first. - Heavy Governance runs live in
.gitea/workflows/test-heavy-governance.ymland stay isolated to manual plus scheduled triggers. The schedule remains gated behindTENANTATLAS_ENABLE_HEAVY_GOVERNANCE_SCHEDULE=1until the first successful manual validation. - Browser runs live in
.gitea/workflows/test-browser.ymland stay isolated to manual plus scheduled triggers. The schedule remains gated behindTENANTATLAS_ENABLE_BROWSER_SCHEDULE=1until the first successful manual validation. - Fast Feedback uses a documented CI variance allowance of
15sbefore budget overrun becomes blocking. Confidence uses30s, Browser uses20s, and Heavy Governance keeps warning-first or trend-only handling while its CI baseline stabilizes.
CI Artifact Bundles
- Lane-local artifacts are still generated in
apps/platform/storage/logs/test-lanesas*-latest.*files. - CI workflows stage deterministic upload bundles through
./scripts/platform-test-artifactsinto.gitea-artifacts/<workflow-profile>before upload. - Every governed CI lane now publishes
summary.md,budget.json,report.json,junit.xml, andtrend-history.json.profilingmay additionally publishprofile.txt. - The report refresh step hydrates the most recent comparable
trend-history.jsonbefore regenerating the current summary when CI credentials allow it, then republishes the refreshed bounded history for the next run. - Artifact publication failures are first-class blocking failures for pull request and
devworkflows.
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
createStandardUserWithTenant()orfixtureProfile: 'standard'when a test needs a default Microsoft provider connection without credentials, cache resets, or UI context. - Use
createFullUserWithTenant()orfixtureProfile: 'full'when a test intentionally needs provider, credential, cache-reset, and UI-context side effects together. - Use
OperationRun::factory()->minimal()for system-style runs andOperationRun::factory()->withUser($user)only when the initiator identity is materially part of the assertion. - Use
BackupSet::factory()->full()only when the test really needs backup items; the default backup-set factory path now stays item-free. provider-enabled,credential-enabled,ui-context, andheavyremain available only as temporary transition aliases while the first migration packs are landing.
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. - Spec 279 managed-environment core cutover is destructive for old local databases. If a local database still contains legacy
tenants,tenant_user,tenant_memberships, oruser_tenant_preferencestables, reset it withcd apps/platform && ./vendor/bin/sail artisan migrate:fresh --seedinstead of trying to preserve that schema. - 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