TenantAtlas/specs/143-tenant-lifecycle-operability-context-semantics/plan.md
ahmido 641bb4afde feat: implement tenant lifecycle operability semantics (#172)
## Summary
- implement Spec 143 tenant lifecycle, operability, and tenant-context semantics across chooser, tenant management, onboarding, and canonical operation viewers
- add centralized tenant lifecycle and operability support types, audit action coverage, and lifecycle-aware badge and action handling
- add feature and unit coverage for tenant chooser eligibility, global search scoping, canonical operation access, onboarding authorization, and lifecycle presentation

## Testing
- vendor/bin/sail artisan test --compact
- vendor/bin/sail bin pint --dirty --format agent

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #172
2026-03-15 09:08:36 +00:00

199 lines
14 KiB
Markdown

# Implementation Plan: Tenant Lifecycle, Operability, and Context Semantics Foundation
**Branch**: `143-tenant-lifecycle-operability-context-semantics` | **Date**: 2026-03-14 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/143-tenant-lifecycle-operability-context-semantics/spec.md`
**Input**: Feature specification from `/specs/143-tenant-lifecycle-operability-context-semantics/spec.md`
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts.
## Summary
Define a single foundation for tenant lifecycle, tenant operability, and tenant context semantics across workspace-scoped admin surfaces, tenant-bound pages, onboarding flows, and canonical workspace record viewers. The implementation approach is to formalize lifecycle and operability decisions behind central domain services and presentation mappings, then apply those decisions in the first rollout slice to selector eligibility, tenant management actions, onboarding visibility, tenant-safe global search, lifecycle audit coverage, and canonical operation-run viewing without letting remembered tenant context determine route legitimacy.
## Technical Context
**Language/Version**: PHP 8.4.15
**Primary Dependencies**: Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4
**Storage**: PostgreSQL via Laravel Eloquent models and workspace/tenant scoped tables
**Testing**: Pest feature, unit, and browser tests run through Laravel Sail
**Target Platform**: Laravel web application served through Filament admin panels
**Project Type**: Web application with server-rendered Filament and Livewire surfaces
**Performance Goals**: Preserve DB-only rendering for Monitoring and canonical run viewers, eliminate false 404 outcomes caused by remembered tenant mismatch, and keep selector/context transitions request-bounded without external calls during page render
**Constraints**: Workspace isolation and tenant isolation are non-negotiable; admin-plane canonical viewers must preserve 404 vs 403 semantics; no new Microsoft Graph calls are introduced by this foundation; badge semantics must remain centralized; destructive actions continue to require explicit confirmation
**Scale/Scope**: Cross-cutting foundation across 8 primary admin routes, 5 core domain entities, multiple Filament pages/resources, and existing authorization, badge, and workspace-context helpers
Additional implementation facts:
- Livewire v4.0+ compliance is already required and preserved because this repo runs Filament v5 on Livewire v4.
- Filament panel providers are registered in `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/bootstrap/providers.php`.
- Relevant global-search posture in current scope:
- `TenantResource` is globally searchable only if kept through its existing resource view/edit surface; any follow-up global-search changes must preserve a View/Edit page.
- `OperationRunResource` has global search disabled already.
- The onboarding and workspace landing surfaces are pages, not searchable resources.
- This implementation must add explicit regression coverage to ensure lifecycle and operability rules do not leak ineligible tenants into global search results.
- Lifecycle mutations introduced or clarified by this feature must emit explicit workspace audit entries through the existing audit layer.
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first: clarify what is “last observed” vs snapshots/backups
- Read/write separation: any writes require preview + confirmation + audit + tests
- Graph contract path: Graph calls only via `GraphClientInterface` + `config/graph_contracts.php`
- Deterministic capabilities: capability derivation is testable (snapshot/golden tests)
- RBAC-UX: two planes (/admin vs /system) remain separated; cross-plane is 404; tenant-context routes (/admin/t/{tenant}/...) are tenant-scoped; canonical workspace-context routes under /admin remain tenant-safe; non-member tenant/workspace access is 404; member-but-missing-capability is 403; authorization checks use Gates/Policies + capability registries (no raw strings, no role-string checks)
- Workspace isolation: non-member workspace access is 404; tenant-plane routes require an established workspace context; workspace context switching is separate from Filament Tenancy
- RBAC-UX: destructive-like actions require `->requiresConfirmation()` and clear warning text
- RBAC-UX: global search is tenant-scoped; non-members get no hints; inaccessible results are treated as not found (404 semantics)
- Tenant isolation: all reads/writes tenant-scoped; cross-tenant views are explicit and access-checked
- Run observability: long-running/remote/queued work creates/reuses `OperationRun`; start surfaces enqueue-only; Monitoring is DB-only; DB-only <2s actions may skip runs but security-relevant ones still audit-log; auth handshake exception OPS-EX-AUTH-001 allows synchronous outbound HTTP on `/auth/*` without `OperationRun`
- Ops-UX 3-surface feedback: if `OperationRun` is used, feedback is exactly toast intent-only + progress surfaces + exactly-once terminal `OperationRunCompleted` (initiator-only); no queued/running DB notifications
- Ops-UX lifecycle: `OperationRun.status` / `OperationRun.outcome` transitions are service-owned (only via `OperationRunService`); context-only updates allowed outside
- Ops-UX summary counts: `summary_counts` keys come from `OperationSummaryKeys::all()` and values are flat numeric-only
- Ops-UX guards: CI has regression guards that fail with actionable output (file + snippet) when these patterns regress
- Ops-UX system runs: initiator-null runs emit no terminal DB notification; audit remains via Monitoring; tenant-wide alerting goes through Alerts (not OperationRun notifications)
- Automation: queued/scheduled ops use locks + idempotency; handle 429/503 with backoff+jitter
- Data minimization: Inventory stores metadata + whitelisted meta; logs contain no secrets/tokens
- Badge semantics (BADGE-001): status-like badges use `BadgeCatalog` / `BadgeRenderer`; no ad-hoc mappings; new values include tests
- UI naming (UI-NAMING-001): operator-facing labels use `Verb + Object`; scope (`Workspace`, `Tenant`) is never the primary action label; source/domain is secondary unless disambiguation is required; runs/toasts/audit prose use the same domain vocabulary; implementation-first terms do not appear in primary operator UI
- Filament UI Action Surface Contract: for any new/modified Filament Resource/RelationManager/Page, define Header/Row/Bulk/Empty-State actions, ensure every List/Table has a record inspection affordance (prefer `recordUrl()` clickable rows; do not render a lone View row action), keep max 2 visible row actions with the rest in More”, group bulk actions, require confirmations for destructive actions (typed confirmation for large/bulk where applicable), write audit logs for mutations, enforce RBAC via central helpers (non-member 404, member missing capability 403), and ensure CI blocks merges if the contract is violated or not explicitly exempted
- Filament UI UX-001 (Layout & IA): Create/Edit uses Main/Aside (3-col grid, Main=columnSpan(2), Aside=columnSpan(1)); all fields inside Sections/Cards (no naked inputs); View uses Infolists (not disabled edit forms); status badges use BADGE-001; empty states have specific title + explanation + 1 CTA; max 1 primary + 1 secondary header action; tables provide search/sort/filters for core dimensions; shared layout builders preferred for consistency
Gate status before Phase 0 research: PASS
- No new Graph contract work is required for this foundation because it does not add outbound Graph calls.
- The feature affects admin-plane authorization semantics and therefore must preserve canonical capability registry usage, deny-as-not-found boundaries, and central policy enforcement.
- The feature affects Filament tenant and operations surfaces, so this implementation slice must satisfy the Action Surface Contract and UX-001 for every modified surface; later follow-up specs may extend the same rules to additional screens.
- The feature affects canonical operation viewers but does not change `OperationRun` lifecycle ownership; any follow-up changes must continue using `OperationRunService` for status and outcome transitions.
- Badge centralization is already available through `BadgeCatalog` and must be extended rather than bypassed for tenant lifecycle semantics.
## Project Structure
### Documentation (this feature)
```text
specs/143-tenant-lifecycle-operability-context-semantics/
├── plan.md # This file (/speckit.plan command output)
├── checklists/
│ └── requirements.md # Spec quality checklist
├── research.md # Phase 0 output (/speckit.plan command)
├── data-model.md # Phase 1 output (/speckit.plan command)
├── quickstart.md # Phase 1 output (/speckit.plan command)
├── contracts/ # Phase 1 output (/speckit.plan command)
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
```
### Source Code (repository root)
```text
app/
├── Filament/
│ ├── Pages/
│ │ ├── ChooseTenant.php
│ │ ├── Operations/TenantlessOperationRunViewer.php
│ │ └── Workspaces/
│ │ ├── ManagedTenantOnboardingWizard.php
│ │ └── ManagedTenantsLanding.php
│ ├── Resources/
│ │ ├── OperationRunResource.php
│ │ └── TenantResource.php
│ └── Concerns/
├── Http/
│ ├── Controllers/
│ │ ├── SelectTenantController.php
│ │ ├── ClearTenantContextController.php
│ │ └── SwitchWorkspaceController.php
│ └── Middleware/
├── Models/
│ ├── Tenant.php
│ ├── TenantOnboardingSession.php
│ └── OperationRun.php
├── Policies/
│ ├── OperationRunPolicy.php
│ └── TenantOnboardingSessionPolicy.php
├── Services/
│ ├── Audit/
│ ├── OperationRunService.php
│ ├── Tenants/
│ └── Onboarding/
└── Support/
├── Audit/
├── Auth/
├── Badges/
├── OperateHub/
├── Tenants/
└── Workspaces/
resources/views/
routes/web.php
bootstrap/providers.php
tests/
├── Browser/
├── Feature/
│ ├── Audit/
│ ├── Auth/
│ ├── Filament/
│ ├── Monitoring/
│ ├── Onboarding/
│ ├── Operations/
│ ├── OpsUx/
│ ├── Rbac/
│ ├── Spec085/
│ └── TenantRBAC/
└── Unit/
├── Tenants/
└── Onboarding/
```
Additional targeted tests may also live in existing focused suites such as `tests/Feature/Guards/` when the regression is cross-cutting rather than surface-specific.
**Structure Decision**: Use the existing Laravel single-project structure. The implementation is cross-cutting but remains inside current `app/`, `routes/`, `resources/views/`, and `tests/` directories. No new top-level source folders are needed, but this feature may introduce focused `Tenants` support/service namespaces and matching `tests/Unit/Tenants` coverage within the existing tree.
## Complexity Tracking
> **Fill ONLY if Constitution Check has violations that must be justified**
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| None | N/A | N/A |
## Phase 0 Research
- Consolidate lifecycle semantics around the existing `Tenant` status values instead of inventing a parallel state source.
- Formalize selector eligibility and remembered-context behavior around workspace context plus operability rules.
- Decouple canonical operation-run viewer validity from active tenant mismatch while preserving existing `OperationRunPolicy` tenant-entitlement checks.
- Extend centralized badge semantics for tenant lifecycle presentation rather than adding ad hoc UI mappings.
- Preserve separation between tenant lifecycle and onboarding draft lifecycle by layering rules across `Tenant` and `TenantOnboardingSession` instead of merging the models.
Research output: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/143-tenant-lifecycle-operability-context-semantics/research.md`
## Phase 1 Design & Contracts
- Define the domain model and derived concepts for lifecycle, operability, context eligibility, canonical viewers, and remembered tenant preference.
- Record the affected route contracts for selector, tenant-bound, onboarding, and canonical record viewer semantics.
- Provide a quickstart validation flow that follow-up implementation specs can use for acceptance testing and regression targeting.
- Update agent context after design artifacts are generated.
Design outputs:
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/143-tenant-lifecycle-operability-context-semantics/data-model.md`
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/143-tenant-lifecycle-operability-context-semantics/contracts/admin-tenant-context-foundation.openapi.yaml`
- `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/143-tenant-lifecycle-operability-context-semantics/quickstart.md`
## Post-Design Constitution Check
Gate status after Phase 1 design: PASS
- Livewire v4.0+ compliance remains explicit and unchanged.
- Filament provider registration remains correctly anchored in `bootstrap/providers.php`.
- No affected design artifact introduces direct Graph calls, new raw capability strings, or non-central badge mappings.
- Canonical record viewer design preserves workspace membership + tenant entitlement checks and removes remembered-tenant mismatch as a source of false 404.
- Destructive lifecycle actions remain explicitly confirmation-gated in follow-up implementation work.
## Implementation Sequencing
1. Introduce central lifecycle and operability abstractions around `Tenant` status and page categories.
2. Refactor selector and remembered-context resolution to consume operability decisions instead of raw `status = active` checks scattered across pages and controllers.
3. Refactor canonical workspace record viewers, especially `TenantlessOperationRunViewer`, to authorize by record and entitlement rather than active tenant equality.
4. Centralize lifecycle presentation and action-label semantics on tenant management surfaces.
5. Add or update Pest feature, unit, and browser tests for canonical viewer access, selector eligibility, badge coverage, tenant-safe global search, lifecycle audit logging, and authorization semantics.