# Implementation Plan: Provider Connections (Tenantless UI + Tenant Transparency) **Branch**: `089-provider-connections-tenantless-ui` | **Date**: 2026-02-12 | **Spec**: `specs/089-provider-connections-tenantless-ui/spec.md` **Input**: Feature specification from `specs/089-provider-connections-tenantless-ui/spec.md` **Note**: This template is filled in by the `/speckit.plan` command. See `.specify/scripts/` for helper scripts. ## Summary Move Provider Connections to a canonical tenantless admin route (`/admin/provider-connections`) while enforcing workspace/tenant isolation (404 for non-members) and capability-first authorization (403 for members lacking capability). The list/details stay tenant-transparent (tenant column + deep links) and respect an active tenant context for default filtering, with a `tenant_id` querystring override. ## Technical Context **Language/Version**: PHP 8.4.15 (Laravel 12) **Primary Dependencies**: Filament v5, Livewire v4, Laravel Sail, PostgreSQL **Storage**: PostgreSQL (Sail locally; production via Dokploy) **Testing**: Pest v4 (+ PHPUnit runner), Livewire component testing for Filament pages/actions **Target Platform**: Web app (Laravel), admin panel under `/admin` **Project Type**: Web application (single Laravel monolith) **Performance Goals**: JOIN-based membership scoping (no large `whereIn` lists), eager-load tenant on list/detail, paginate safely for MSP-scale datasets **Constraints**: Workspace non-members are 404; tenant non-members are 404; capability denial is 403 after membership is established; no plaintext secrets rendered; global search disabled for Provider Connections **Scale/Scope**: MSP-style workspaces with many tenants and many provider connections ## 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; non-member tenant 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` - 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 - 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 **Gate evaluation (pre-Phase 0)** - Inventory-first: Not impacted (UI/routing + entitlement/scoping only). - Read/write separation: Manage actions are state-changing and will be confirmed + audited; run/health uses OperationRun. - Graph contract path: Any health check job must use existing Graph abstractions; no external calls at render time. - Deterministic capabilities: Uses existing capability registry (`PROVIDER_VIEW`, `PROVIDER_MANAGE`, `PROVIDER_RUN`). - Workspace/Tenant isolation: Enforced via existing membership middleware + policy deny-as-not-found patterns. - Global search: Explicitly disabled for Provider Connections. - Action surface contract: List/detail surfaces must keep inspection affordance and proper action grouping. ## Project Structure ### Documentation (this feature) ```text specs/089-provider-connections-tenantless-ui/ ├── plan.md # This file (/speckit.plan command output) ├── 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/ │ └── Resources/ │ └── ProviderConnectionResource.php │ └── ProviderConnectionResource/ │ └── Pages/ │ ├── ListProviderConnections.php │ ├── CreateProviderConnection.php │ └── EditProviderConnection.php ├── Policies/ │ └── ProviderConnectionPolicy.php ├── Support/ │ ├── Auth/Capabilities.php │ ├── Rbac/UiEnforcement.php │ └── Workspaces/WorkspaceContext.php └── Http/ └── Middleware/ ├── EnsureWorkspaceMember.php └── EnsureWorkspaceSelected.php routes/ └── web.php tests/ ├── Feature/ └── Unit/ ├── Filament/ └── Policies/ ``` **Structure Decision**: Implement as a Laravel/Filament change (resource slug, navigation placement, scoping, and legacy redirects) plus targeted Pest tests. No new standalone services or packages are required. ## Complexity Tracking > **Fill ONLY if Constitution Check has violations that must be justified** | Violation | Why Needed | Simpler Alternative Rejected Because | |-----------|------------|-------------------------------------| | [e.g., 4th project] | [current need] | [why 3 projects insufficient] | | [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] | No constitution violations are required for this feature.