## Summary
- centralize tenant operability into a lane-aware, actor-aware policy boundary
- align selector eligibility, administrative discoverability, remembered context, tenant-bound routes, and canonical run viewers
- add focused Pest coverage plus Spec 148 artifacts and final polish task completion
## Validation
- `vendor/bin/sail artisan test --compact tests/Unit/Tenants/TenantOperabilityServiceTest.php tests/Unit/Tenants/TenantOperabilityOutcomeTest.php tests/Feature/Workspaces/ChooseTenantPageTest.php tests/Feature/Workspaces/SelectTenantControllerTest.php tests/Feature/TenantRBAC/ArchivedTenantRouteAccessTest.php tests/Feature/TenantRBAC/TenantRouteDenyAsNotFoundTest.php tests/Feature/Operations/TenantlessOperationRunViewerTest.php tests/Feature/OpsUx/OperateHubShellTest.php tests/Feature/Rbac/TenantLifecycleActionVisibilityTest.php tests/Feature/TenantRBAC/TenantSwitcherScopeTest.php tests/Feature/Rbac/TenantResourceAuthorizationTest.php tests/Feature/Filament/ManagedTenantsLandingLifecycleTest.php tests/Feature/Filament/TenantGlobalSearchLifecycleScopeTest.php tests/Feature/Onboarding/OnboardingDraftLifecycleTest.php tests/Feature/Onboarding/OnboardingDraftAuthorizationTest.php`
- `vendor/bin/sail bin pint --dirty --format agent`
- manual browser smoke checks on `/admin/choose-tenant`, `/admin/tenants`, `/admin/onboarding`, `/admin/onboarding/{draft}`, and `/admin/operations/{run}`
## Filament / platform notes
- Livewire v4 compliance preserved
- panel provider registration unchanged in `bootstrap/providers.php`
- Tenant resource global search remains backed by existing view/edit pages and is now separated from active-only selector eligibility
- destructive actions remain action closures with confirmation and authorization enforcement
- no asset pipeline changes and no new `filament:assets` deployment requirement
Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #177
242 lines
7.4 KiB
YAML
242 lines
7.4 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Tenant Operability Policy Internal Admin Contract
|
|
version: 0.1.0
|
|
summary: Internal planning contract for centralized tenant operability resolution
|
|
description: |
|
|
This is an internal design artifact for Spec 148. It documents the intended
|
|
route and support-layer semantics for selector eligibility, remembered-context
|
|
validation, tenant-bound viewability, canonical linked-record viewability,
|
|
and lifecycle-safe action eligibility. It does not require a new public HTTP API.
|
|
servers:
|
|
- url: /internal/admin
|
|
tags:
|
|
- name: Operability Policy
|
|
- name: Context Management
|
|
- name: Route Semantics
|
|
paths:
|
|
/tenants/{tenant}/operability:
|
|
get:
|
|
tags: [Operability Policy]
|
|
summary: Resolve one tenant-semantic decision for a specific lane and actor context
|
|
operationId: resolveTenantOperability
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantId'
|
|
- name: lane
|
|
in: query
|
|
required: true
|
|
schema:
|
|
$ref: '#/components/schemas/TenantInteractionLane'
|
|
- name: question
|
|
in: query
|
|
required: true
|
|
schema:
|
|
$ref: '#/components/schemas/TenantOperabilityQuestion'
|
|
responses:
|
|
'200':
|
|
description: Operability decision resolved
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TenantOperabilityOutcome'
|
|
'403':
|
|
description: Actor is in scope but lacks the required capability for the requested action semantics
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
'404':
|
|
description: Tenant is outside workspace or tenant entitlement scope
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ErrorResponse'
|
|
/admin/select-tenant:
|
|
post:
|
|
tags: [Context Management]
|
|
summary: Persist remembered tenant context only when selector-lane operability allows it
|
|
operationId: selectTenantContext
|
|
requestBody:
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- tenant_id
|
|
properties:
|
|
tenant_id:
|
|
type: integer
|
|
responses:
|
|
'302':
|
|
description: Remembered tenant context accepted and redirect issued to the tenant-lane destination
|
|
'404':
|
|
description: Tenant missing, outside workspace, not entitled, or not eligible for standard active selection
|
|
/admin/clear-tenant-context:
|
|
post:
|
|
tags: [Context Management]
|
|
summary: Clear remembered tenant context for the current workspace
|
|
operationId: clearTenantContext
|
|
responses:
|
|
'302':
|
|
description: Remembered tenant context cleared and workspace-safe redirect issued
|
|
/admin/tenants/{tenant}:
|
|
get:
|
|
tags: [Route Semantics]
|
|
summary: Resolve tenant-bound route legitimacy from route tenant plus operability and authorization
|
|
operationId: viewTenantBoundPage
|
|
parameters:
|
|
- $ref: '#/components/parameters/TenantRouteKey'
|
|
responses:
|
|
'200':
|
|
description: Tenant-bound page rendered for an authorized actor
|
|
'403':
|
|
description: Actor is in scope but lacks a required capability for the page or follow-up action
|
|
'404':
|
|
description: Tenant missing or actor not entitled to tenant scope
|
|
/admin/operations/{run}:
|
|
get:
|
|
tags: [Route Semantics]
|
|
summary: Resolve canonical workspace record viewer without requiring selected tenant equality
|
|
operationId: viewCanonicalOperationRun
|
|
parameters:
|
|
- name: run
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
responses:
|
|
'200':
|
|
description: Authorized canonical run viewer rendered
|
|
'403':
|
|
description: Actor is in scope but lacks capability for the run type or follow-up action
|
|
'404':
|
|
description: Run missing, workspace missing, or tenant entitlement missing for the referenced tenant
|
|
components:
|
|
parameters:
|
|
TenantId:
|
|
name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
description: Canonical tenant primary key used for internal policy resolution.
|
|
TenantRouteKey:
|
|
name: tenant
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: string
|
|
description: External tenant route key used by tenant-bound admin routes.
|
|
schemas:
|
|
TenantInteractionLane:
|
|
type: string
|
|
enum:
|
|
- standard_active_operating
|
|
- onboarding_workflow
|
|
- administrative_management
|
|
- canonical_workspace_record
|
|
TenantOperabilityQuestion:
|
|
type: string
|
|
enum:
|
|
- selector_eligibility
|
|
- remembered_context_validity
|
|
- tenant_bound_viewability
|
|
- canonical_linked_record_viewability
|
|
- archive_eligibility
|
|
- restore_eligibility
|
|
- resume_onboarding_eligibility
|
|
- onboarding_completion_eligibility
|
|
- verification_readiness_eligibility
|
|
- administrative_discoverability
|
|
TenantOperabilityReasonCode:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
enum:
|
|
- workspace_mismatch
|
|
- tenant_not_entitled
|
|
- missing_capability
|
|
- wrong_lane
|
|
- selector_ineligible_lifecycle
|
|
- tenant_not_archived
|
|
- tenant_already_archived
|
|
- onboarding_not_resumable
|
|
- remembered_context_stale
|
|
- canonical_view_followup_only
|
|
- null
|
|
TenantOperabilityOutcome:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- tenantId
|
|
- lifecycle
|
|
- lane
|
|
- question
|
|
- allowed
|
|
- discoverable
|
|
properties:
|
|
tenantId:
|
|
type: integer
|
|
lifecycle:
|
|
type: string
|
|
enum:
|
|
- draft
|
|
- onboarding
|
|
- active
|
|
- archived
|
|
lane:
|
|
$ref: '#/components/schemas/TenantInteractionLane'
|
|
question:
|
|
$ref: '#/components/schemas/TenantOperabilityQuestion'
|
|
allowed:
|
|
type: boolean
|
|
discoverable:
|
|
type: boolean
|
|
requiredCapability:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
reasonCode:
|
|
$ref: '#/components/schemas/TenantOperabilityReasonCode'
|
|
informationalMessageKey:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
metadata:
|
|
type: object
|
|
additionalProperties: true
|
|
RememberedTenantContextState:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- workspaceId
|
|
- status
|
|
properties:
|
|
workspaceId:
|
|
type: integer
|
|
tenantId:
|
|
type:
|
|
- integer
|
|
- 'null'
|
|
status:
|
|
type: string
|
|
enum:
|
|
- no_selected_tenant
|
|
- remembered_active
|
|
- route_authoritative_tenant
|
|
- stale_context_cleared
|
|
invalidationReason:
|
|
$ref: '#/components/schemas/TenantOperabilityReasonCode'
|
|
ErrorResponse:
|
|
type: object
|
|
additionalProperties: false
|
|
required:
|
|
- code
|
|
- message
|
|
properties:
|
|
code:
|
|
type: string
|
|
message:
|
|
type: string |