openapi: 3.1.0
info:
  title: Findings Operator Inbox Surface Contract
  version: 1.0.0
  description: >-
    Internal reference contract for the canonical My Findings inbox and the
    workspace overview Assigned to me signal. The application continues to
    return rendered HTML through Filament and Livewire. The vendor media types
    below document the structured page models that must be derivable before
    rendering. This is not a public API commitment.
paths:
  /admin/findings/my-work:
    get:
      summary: Canonical personal findings inbox
      description: >-
        Returns the rendered admin-plane inbox for the current user's visible
        assigned open findings. Personal assignment scope is fixed. Tenant
        prefiltering may be derived from the active admin tenant context.
      responses:
        '302':
          description: Redirects into the existing workspace or tenant chooser flow when membership exists but workspace context is not yet established
        '200':
          description: Rendered My Findings inbox page
          content:
            text/html:
              schema:
                type: string
            application/vnd.tenantpilot.my-findings-inbox+json:
              schema:
                $ref: '#/components/schemas/MyFindingsInboxPage'
        '404':
          description: Workspace scope is not visible because membership is missing or out of scope
  /admin:
    get:
      summary: Workspace overview with assigned-to-me signal
      description: >-
        Returns the rendered workspace overview. The vendor media type documents
        the embedded personal findings signal used to decide whether assigned
        findings work exists before opening the inbox.
      responses:
        '302':
          description: Redirects into the existing workspace or tenant chooser flow when membership exists but workspace context is not yet established
        '200':
          description: Rendered workspace overview page
          content:
            text/html:
              schema:
                type: string
            application/vnd.tenantpilot.workspace-overview-my-findings+json:
              schema:
                $ref: '#/components/schemas/WorkspaceOverviewMyFindingsSignalSurface'
        '404':
          description: Workspace scope is not visible because membership is missing or out of scope
  /admin/t/{tenant}/findings/{finding}:
    get:
      summary: Tenant finding detail with inbox continuity support
      description: >-
        Returns the rendered tenant finding detail page. The logical contract
        below documents only the continuity inputs required when the page is
        opened from the My Findings inbox.
      parameters:
        - name: tenant
          in: path
          required: true
          schema:
            type: integer
        - name: finding
          in: path
          required: true
          schema:
            type: integer
        - name: nav
          in: query
          required: false
          style: deepObject
          explode: true
          schema:
            $ref: '#/components/schemas/CanonicalNavigationContext'
      responses:
        '200':
          description: Rendered tenant finding detail page
          content:
            text/html:
              schema:
                type: string
            application/vnd.tenantpilot.finding-detail-continuation+json:
              schema:
                $ref: '#/components/schemas/FindingDetailContinuation'
        '403':
          description: Viewer is in scope but lacks the existing findings capability for the tenant detail destination
        '404':
          description: Tenant or finding is not visible because workspace or tenant entitlement is missing
components:
  schemas:
    MyFindingsInboxPage:
      type: object
      required:
        - header
        - appliedScope
        - availableFilters
        - summaryCounts
        - rows
        - emptyState
      properties:
        header:
          $ref: '#/components/schemas/InboxHeader'
        appliedScope:
          $ref: '#/components/schemas/InboxAppliedScope'
        availableFilters:
          description: Includes the fixed assignee-scope filter plus tenant, overdue, reopened, and high-severity filters. Tenant filter options are derived only from visible capability-eligible tenants.
          type: array
          items:
            $ref: '#/components/schemas/InboxFilterDefinition'
        summaryCounts:
          $ref: '#/components/schemas/MyFindingsSummaryCounts'
        rows:
          description: Rows are ordered overdue first, reopened non-overdue second, then remaining findings. Within each bucket, rows with due dates sort by dueAt ascending, rows without due dates sort last, and remaining ties sort by findingId descending.
          type: array
          items:
            $ref: '#/components/schemas/AssignedFindingInboxRow'
        emptyState:
          oneOf:
            - $ref: '#/components/schemas/InboxEmptyState'
            - type: 'null'
    InboxHeader:
      type: object
      required:
        - title
        - description
      properties:
        title:
          type: string
          enum:
            - My Findings
        description:
          type: string
        clearTenantFilterAction:
          oneOf:
            - $ref: '#/components/schemas/ActionLink'
            - type: 'null'
    InboxAppliedScope:
      type: object
      required:
        - workspaceScoped
        - assigneeScope
        - tenantPrefilterSource
      properties:
        workspaceScoped:
          type: boolean
        assigneeScope:
          type: string
          enum:
            - current_user_only
        tenantPrefilterSource:
          type: string
          enum:
            - active_tenant_context
            - explicit_filter
            - none
        tenantLabel:
          type:
            - string
            - 'null'
    InboxFilterDefinition:
      type: object
      required:
        - key
        - label
        - fixed
      properties:
        key:
          type: string
          enum:
            - assignee_scope
            - tenant
            - overdue
            - reopened
            - high_severity
        label:
          type: string
        fixed:
          type: boolean
        options:
          type: array
          items:
            $ref: '#/components/schemas/FilterOption'
    FilterOption:
      type: object
      required:
        - value
        - label
      properties:
        value:
          type: string
        label:
          type: string
    MyFindingsSummaryCounts:
      type: object
      description: Counts derived from the currently visible inbox queue after the fixed assignee scope and any active tenant, overdue, reopened, or high-severity filters are applied.
      required:
        - openAssigned
        - overdueAssigned
      properties:
        openAssigned:
          type: integer
          minimum: 0
        overdueAssigned:
          type: integer
          minimum: 0
    AssignedFindingInboxRow:
      type: object
      required:
        - findingId
        - tenantId
        - tenantLabel
        - summary
        - severity
        - status
        - dueState
        - detailUrl
      properties:
        findingId:
          type: integer
        tenantId:
          type: integer
        tenantLabel:
          type: string
        summary:
          type: string
        severity:
          $ref: '#/components/schemas/Badge'
        status:
          $ref: '#/components/schemas/Badge'
        dueAt:
          type:
            - string
            - 'null'
          format: date-time
        dueState:
          $ref: '#/components/schemas/DueState'
        reopened:
          type: boolean
        ownerLabel:
          type:
            - string
            - 'null'
        detailUrl:
          type: string
        navigationContext:
          oneOf:
            - $ref: '#/components/schemas/CanonicalNavigationContext'
            - type: 'null'
    DueState:
      type: object
      required:
        - label
        - tone
      properties:
        label:
          type: string
        tone:
          type: string
          enum:
            - calm
            - warning
            - danger
    InboxEmptyState:
      type: object
      required:
        - title
        - body
        - action
      properties:
        title:
          type: string
        body:
          type: string
        reason:
          type: string
          enum:
            - no_visible_assigned_work
            - active_tenant_prefilter_excludes_rows
        action:
          description: For `active_tenant_prefilter_excludes_rows`, clears the tenant prefilter and returns the queue to all visible tenants. For `no_visible_assigned_work`, opens the active tenant's findings list when tenant context exists; otherwise opens `/admin/choose-tenant` so the operator can establish tenant context before entering tenant findings.
          $ref: '#/components/schemas/ActionLink'
    WorkspaceOverviewMyFindingsSignalSurface:
      type: object
      required:
        - workspaceId
        - myFindingsSignal
      properties:
        workspaceId:
          type: integer
        myFindingsSignal:
          $ref: '#/components/schemas/MyFindingsSignal'
    MyFindingsSignal:
      type: object
      required:
        - openAssignedCount
        - overdueAssignedCount
        - isCalm
        - headline
        - cta
      properties:
        openAssignedCount:
          type: integer
          minimum: 0
        overdueAssignedCount:
          type: integer
          minimum: 0
        isCalm:
          type: boolean
        headline:
          type: string
          enum:
            - Assigned to me
        description:
          type:
            - string
            - 'null'
        cta:
          $ref: '#/components/schemas/OpenMyFindingsActionLink'
    FindingDetailContinuation:
      type: object
      description: Continuity payload for tenant finding detail when it is opened from the My Findings inbox. The backLink is present whenever canonical inbox navigation context is provided and may be null only for direct entry without inbox continuity context.
      required:
        - findingId
        - tenantId
      properties:
        findingId:
          type: integer
        tenantId:
          type: integer
        backLink:
          description: Present when the detail page is reached from the My Findings inbox with canonical navigation context; null only for direct navigation that did not originate from the inbox.
          oneOf:
            - $ref: '#/components/schemas/BackToMyFindingsActionLink'
            - type: 'null'
    CanonicalNavigationContext:
      type: object
      required:
        - source_surface
        - canonical_route_name
      properties:
        source_surface:
          type: string
        canonical_route_name:
          type: string
        tenant_id:
          type:
            - integer
            - 'null'
        back_label:
          type:
            - string
            - 'null'
        back_url:
          type:
            - string
            - 'null'
    ActionLink:
      type: object
      required:
        - label
        - url
      properties:
        label:
          type: string
        url:
          type: string
    OpenMyFindingsActionLink:
      allOf:
        - $ref: '#/components/schemas/ActionLink'
        - type: object
          properties:
            label:
              type: string
              enum:
                - Open my findings
    BackToMyFindingsActionLink:
      allOf:
        - $ref: '#/components/schemas/ActionLink'
        - type: object
          properties:
            label:
              type: string
              enum:
                - Back to my findings
    Badge:
      type: object
      required:
        - label
      properties:
        label:
          type: string
        color:
          type:
            - string
            - 'null'