openapi: 3.0.3
info:
  title: TenantPilot Admin/System - Workspace Commercial Lifecycle Overlay (Conceptual)
  version: 0.1.0
  description: |
    Conceptual contract for the commercial lifecycle overlay that follows the
    existing workspace entitlement substrate from Spec 247.

    NOTE: These routes are implemented as existing Filament pages, resources,
    widgets, and Livewire-backed actions. Exact Livewire payload shapes are not
    part of this contract. This file captures logical route boundaries, the
    system/admin split, and the required 404 / 403 / business-state semantics.
servers:
  - url: /admin
  - url: /system
paths:
  /directory/workspaces/{workspace}:
    get:
      summary: View read-only workspace commercial lifecycle summary in the system plane
      description: |
        Renders the existing system directory workspace detail page with the
        effective lifecycle state, rationale, affected behavior summary, and the
        reused entitlement substrate summary.
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      responses:
        '200':
          description: System workspace detail rendered
          content:
            text/html:
              schema:
                type: string
          x-logical-view-model:
            $ref: '#/components/schemas/SystemWorkspaceCommercialLifecycleView'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /directory/workspaces/{workspace}/actions/change-commercial-state:
    post:
      summary: Change the workspace commercial lifecycle state from the system plane
      description: |
        Conceptual contract for the confirmation-protected state-change action
        on the existing system workspace detail page.

        Behavior:
        - Platform user with directory visibility but without the dedicated
          lifecycle-manage capability: 403
        - Wrong plane or non-platform actor: 404 semantics at the panel boundary
        - Authorized platform user: state and rationale are written through the
          existing workspace settings audit path
      parameters:
        - $ref: '#/components/parameters/WorkspaceId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ChangeCommercialLifecycleCommand'
      responses:
        '204':
          description: Commercial lifecycle state changed successfully
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '422':
          $ref: '#/components/responses/ValidationError'
  /onboarding/{onboardingDraft}:
    get:
      summary: View onboarding workflow with lifecycle-aware completion state
      description: |
        Renders the existing managed-tenant onboarding wizard. The completion
        step must include the commercial lifecycle outcome after the underlying
        entitlement substrate has been evaluated.
      parameters:
        - $ref: '#/components/parameters/OnboardingDraftId'
      responses:
        '200':
          description: Onboarding wizard rendered
          content:
            text/html:
              schema:
                type: string
          x-logical-view-model:
            $ref: '#/components/schemas/OnboardingCommercialLifecycleView'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /onboarding/{onboardingDraft}/actions/complete:
    post:
      summary: Complete onboarding when entitlement, lifecycle state, and existing readiness all allow
      parameters:
        - $ref: '#/components/parameters/OnboardingDraftId'
      responses:
        '204':
          description: Onboarding completed
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/BusinessStateBlocked'
  /review-packs/actions/generate:
    post:
      summary: Generate a review pack from the current tenant context
      description: |
        Conceptual contract for the tenant dashboard and review-pack list start
        action family.

        Behavior ordering:
        1. authorization
        2. underlying entitlement substrate decision
        3. lifecycle overlay decision
        4. existing dedupe / queued-start flow when allowed

        A lifecycle-blocked attempt is future-start-only in this slice: it
        creates no new `ReviewPack`, creates no new `OperationRun`, emits no
        queued or terminal review-pack notification, and does not affect any
        review-pack work that was already queued or running.
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReviewPackGenerationCommand'
      responses:
        '202':
          description: Generation accepted or deduped through the existing flow
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/BusinessStateBlocked'
  /tenant-reviews/{tenantReview}/actions/export-executive-pack:
    post:
      summary: Export an executive pack from an existing tenant review
      description: |
        Conceptual contract for the review register and tenant review detail
        export action family. The lifecycle overlay must block before any new
        `ReviewPack` or `OperationRun` is created, emit no queued or terminal
        review-pack notification for the blocked attempt, and leave any
        already-created queued or running review-pack work unchanged.
      parameters:
        - $ref: '#/components/parameters/TenantReviewId'
      responses:
        '202':
          description: Export accepted or deduped through the existing flow
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/BusinessStateBlocked'
  /review-packs/{reviewPack}/actions/regenerate:
    post:
      summary: Regenerate an existing review pack
      description: |
        Conceptual contract for the existing review-pack detail regenerate
        action. Existing confirmation and dedupe behavior remain in place when
        the lifecycle overlay allows the start. A lifecycle-blocked attempt is
        future-start-only: it creates no new `ReviewPack`, creates no new
        `OperationRun`, emits no queued or terminal review-pack notification,
        and leaves any already-created queued or running review-pack work
        unchanged.
      parameters:
        - $ref: '#/components/parameters/ReviewPackId'
      requestBody:
        required: false
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReviewPackGenerationCommand'
      responses:
        '202':
          description: Regeneration accepted or deduped through the existing flow
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
        '409':
          $ref: '#/components/responses/BusinessStateBlocked'
  /tenant-reviews/{tenantReview}:
    get:
      summary: View existing tenant review while the workspace may be suspended read-only
      parameters:
        - $ref: '#/components/parameters/TenantReviewId'
      responses:
        '200':
          description: Existing tenant review rendered when current RBAC allows it
          content:
            text/html:
              schema:
                type: string
          x-logical-view-model:
            $ref: '#/components/schemas/PreservedReadOnlyView'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /review-packs/{reviewPack}:
    get:
      summary: View existing review pack while the workspace may be suspended read-only
      parameters:
        - $ref: '#/components/parameters/ReviewPackId'
      responses:
        '200':
          description: Existing review pack rendered when current RBAC allows it
          content:
            text/html:
              schema:
                type: string
          x-logical-view-model:
            $ref: '#/components/schemas/PreservedReadOnlyView'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /review-packs/{reviewPack}/download:
    get:
      summary: Download an already-generated review pack while the workspace may be suspended read-only
      parameters:
        - $ref: '#/components/parameters/ReviewPackId'
      responses:
        '200':
          description: Existing generated pack download is still available when current RBAC allows it
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
  /evidence-snapshots/{evidenceSnapshot}:
    get:
      summary: View existing evidence snapshot while the workspace may be suspended read-only
      parameters:
        - $ref: '#/components/parameters/EvidenceSnapshotId'
      responses:
        '200':
          description: Existing evidence snapshot rendered when current RBAC allows it
          content:
            text/html:
              schema:
                type: string
          x-logical-view-model:
            $ref: '#/components/schemas/PreservedReadOnlyView'
        '403':
          $ref: '#/components/responses/Forbidden'
        '404':
          $ref: '#/components/responses/NotFound'
components:
  parameters:
    WorkspaceId:
      name: workspace
      in: path
      required: true
      schema:
        type: integer
    OnboardingDraftId:
      name: onboardingDraft
      in: path
      required: true
      schema:
        type: integer
    TenantReviewId:
      name: tenantReview
      in: path
      required: true
      schema:
        type: integer
    ReviewPackId:
      name: reviewPack
      in: path
      required: true
      schema:
        type: integer
    EvidenceSnapshotId:
      name: evidenceSnapshot
      in: path
      required: true
      schema:
        type: integer
  responses:
    Forbidden:
      description: Established-scope actor lacks the required capability
    NotFound:
      description: Wrong plane, non-member scope, or inaccessible record
    BusinessStateBlocked:
      description: Actor is otherwise authorized, but the workspace commercial state or underlying entitlement substrate blocks the requested action
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/CommercialLifecycleBlockResponse'
    ValidationError:
      description: Submitted commercial lifecycle state change failed validation
  schemas:
    ChangeCommercialLifecycleCommand:
      type: object
      required:
        - state
        - reason
      properties:
        state:
          $ref: '#/components/schemas/CommercialLifecycleState'
        reason:
          type: string
          description: Required for every explicit lifecycle state change, including an explicit return to active_paid.
          minLength: 1
          maxLength: 500
    CommercialLifecycleState:
      type: string
      enum:
        - trial
        - grace
        - active_paid
        - suspended_read_only
    ReviewPackGenerationCommand:
      type: object
      properties:
        include_pii:
          type: boolean
        include_operations:
          type: boolean
    SystemWorkspaceCommercialLifecycleView:
      type: object
      required:
        - workspace_id
        - lifecycle
        - affected_behaviors
      properties:
        workspace_id:
          type: integer
        lifecycle:
          $ref: '#/components/schemas/CommercialLifecycleDecision'
        affected_behaviors:
          type: array
          items:
            $ref: '#/components/schemas/CommercialLifecycleActionDecision'
        entitlement_substrate:
          type: object
          description: Existing Spec 247 workspace entitlement summary reused for context
        primary_action:
          $ref: '#/components/schemas/NextAction'
          nullable: true
    OnboardingCommercialLifecycleView:
      type: object
      required:
        - onboarding_draft_id
        - action_decision
      properties:
        onboarding_draft_id:
          type: integer
        action_decision:
          $ref: '#/components/schemas/CommercialLifecycleActionDecision'
        entitlement_substrate:
          type: object
          nullable: true
    CommercialLifecycleDecision:
      type: object
      required:
        - state
        - label
        - source
        - source_label
      properties:
        state:
          $ref: '#/components/schemas/CommercialLifecycleState'
        label:
          type: string
        source:
          type: string
          enum:
            - default_active_paid
            - workspace_setting
        source_label:
          type: string
          description: Rendered source label from the shared lifecycle source mapping used by system detail surfaces.
        rationale:
          type: string
          nullable: true
        last_changed_at:
          type: string
          format: date-time
          nullable: true
        last_changed_by:
          type: string
          nullable: true
    CommercialLifecycleActionDecision:
      type: object
      required:
        - action_key
        - outcome
        - lifecycle_state
      properties:
        action_key:
          type: string
          enum:
            - managed_tenant_activation
            - review_pack_start
            - review_history_read
            - evidence_read
            - generated_pack_read
        outcome:
          type: string
          enum:
            - allow
            - warn
            - block
            - allow_read_only
        reason_family:
          type: string
          nullable: true
          enum:
            - commercial_lifecycle
            - entitlement_substrate
        message:
          type: string
          nullable: true
        lifecycle_state:
          $ref: '#/components/schemas/CommercialLifecycleState'
        underlying_entitlement_key:
          type: string
          nullable: true
    CommercialLifecycleBlockResponse:
      type: object
      required:
        - reason_family
        - message
      properties:
        reason_family:
          type: string
          enum:
            - commercial_lifecycle
            - entitlement_substrate
        lifecycle_state:
          $ref: '#/components/schemas/CommercialLifecycleState'
          nullable: true
        message:
          type: string
    PreservedReadOnlyView:
      type: object
      required:
        - read_only_access_preserved
      properties:
        read_only_access_preserved:
          type: boolean
          enum: [true]
        lifecycle_state:
          $ref: '#/components/schemas/CommercialLifecycleState'
        message:
          type: string
          nullable: true
          description: Optional calm explanation that the workspace is suspended read-only while current history access remains available
    NextAction:
      type: object
      required:
        - label
      properties:
        label:
          type: string
        enabled:
          type: boolean
        reason:
          type: string
          nullable: true