TenantAtlas/specs/148-central-tenant-operability-policy/contracts/tenant-operability-policy.openapi.yaml
ahmido 417df4f9aa feat: central tenant operability policy (#177)
## 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
2026-03-17 11:48:55 +00:00

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