openapi: 3.1.0
info:
  title: Findings Intake & Team Queue Surface Contract
  version: 1.1.0
  description: >-
    Internal reference contract for the canonical Findings intake queue,
    the narrow Claim finding shortcut, and intake continuity into tenant
    finding detail and My Findings. The application continues to return
    rendered HTML through Filament and Livewire. The vendor media types
    below document the structured page and action models that must be
    derivable before rendering. This is not a public API commitment.
paths:
  /admin/findings/intake:
    get:
      summary: Canonical shared findings intake queue
      description: >-
        Returns the rendered admin-plane intake queue for visible unassigned
        findings in the current workspace. The page always keeps the fixed
        intake scope and may apply an active-tenant prefilter. Queue views are
        limited to Unassigned and Needs triage.
      responses:
        '302':
          description: Redirects into the existing workspace chooser flow when workspace context is not yet established
        '200':
          description: Rendered Findings intake page
          content:
            text/html:
              schema:
                type: string
            application/vnd.tenantpilot.findings-intake+json:
              schema:
                $ref: '#/components/schemas/FindingsIntakePage'
        '403':
          description: Workspace membership exists but no currently viewable findings scope exists for intake disclosure anywhere in the workspace
        '404':
          description: Workspace scope is not visible because membership is missing or out of scope
  /admin/findings/intake/{finding}/claim:
    post:
      summary: Claim a visible intake finding into personal execution
      description: >-
        Logical contract for the Livewire-backed Claim finding row action after
        the operator has reviewed the lightweight preview/confirmation content.
        The server must re-check entitlement, assign capability, and current
        assignee under lock before mutating the record.
      parameters:
        - name: finding
          in: path
          required: true
          schema:
            type: integer
      responses:
        '200':
          description: Claim succeeded and the finding left intake
          content:
            application/vnd.tenantpilot.findings-intake-claim+json:
              schema:
                $ref: '#/components/schemas/ClaimFindingResult'
        '403':
          description: Viewer is in scope but lacks the existing findings assign capability
        '404':
          description: Workspace or tenant scope is not visible for the referenced finding
        '409':
          description: The row is no longer claimable because another operator claimed it first or it otherwise left intake scope before mutation
  /admin/t/{tenant}/findings/{finding}:
    get:
      summary: Tenant finding detail with intake 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 Findings intake.
      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-from-intake+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:
    FindingsIntakePage:
      type: object
      required:
        - header
        - appliedScope
        - queueViews
        - summaryCounts
        - rows
        - emptyState
      properties:
        header:
          $ref: '#/components/schemas/IntakeHeader'
        appliedScope:
          $ref: '#/components/schemas/IntakeAppliedScope'
        queueViews:
          type: array
          items:
            $ref: '#/components/schemas/QueueViewDefinition'
        summaryCounts:
          $ref: '#/components/schemas/IntakeSummaryCounts'
        rows:
          description: >-
            Rows are ordered overdue first, reopened second, new third, then
            remaining unassigned backlog. 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/IntakeFindingRow'
        emptyState:
          oneOf:
            - $ref: '#/components/schemas/IntakeEmptyState'
            - type: 'null'
    IntakeHeader:
      type: object
      required:
        - title
        - description
      properties:
        title:
          type: string
          enum:
            - Findings intake
        description:
          type: string
        clearTenantFilterAction:
          oneOf:
            - $ref: '#/components/schemas/ActionLink'
            - type: 'null'
    IntakeAppliedScope:
      type: object
      required:
        - workspaceScoped
        - fixedScope
        - queueView
        - tenantPrefilterSource
      properties:
        workspaceScoped:
          type: boolean
        fixedScope:
          type: string
          enum:
            - visible_unassigned_open_findings_only
        queueView:
          type: string
          enum:
            - unassigned
            - needs_triage
        tenantPrefilterSource:
          type: string
          enum:
            - active_tenant_context
            - explicit_filter
            - none
        tenantLabel:
          type:
            - string
            - 'null'
    QueueViewDefinition:
      type: object
      required:
        - key
        - label
        - fixed
        - active
      properties:
        key:
          type: string
          enum:
            - unassigned
            - needs_triage
        label:
          type: string
        fixed:
          type: boolean
        active:
          type: boolean
        badgeCount:
          type:
            - integer
            - 'null'
          minimum: 0
    IntakeSummaryCounts:
      type: object
      description: Counts derived only from visible intake rows.
      required:
        - visibleUnassigned
        - visibleNeedsTriage
        - visibleOverdue
      properties:
        visibleUnassigned:
          type: integer
          minimum: 0
        visibleNeedsTriage:
          type: integer
          minimum: 0
        visibleOverdue:
          type: integer
          minimum: 0
    IntakeFindingRow:
      type: object
      required:
        - findingId
        - tenantId
        - tenantLabel
        - summary
        - severity
        - status
        - intakeReason
        - 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'
        ownerLabel:
          type:
            - string
            - 'null'
        intakeReason:
          type: string
          enum:
            - Unassigned
            - Needs triage
        claimAction:
          oneOf:
            - $ref: '#/components/schemas/ClaimFindingAffordance'
            - type: 'null'
        detailUrl:
          type: string
        navigationContext:
          oneOf:
            - $ref: '#/components/schemas/CanonicalNavigationContext'
            - type: 'null'
    ClaimFindingAffordance:
      type: object
      required:
        - actionId
        - label
        - enabled
        - requiresConfirmation
      properties:
        actionId:
          type: string
          enum:
            - claim
        label:
          type: string
          enum:
            - Claim finding
        enabled:
          type: boolean
        requiresConfirmation:
          type: boolean
          enum:
            - true
        confirmationTitle:
          type:
            - string
            - 'null'
        confirmationBody:
          type:
            - string
            - 'null'
        disabledReason:
          type:
            - string
            - 'null'
    DueState:
      type: object
      required:
        - label
        - tone
      properties:
        label:
          type: string
        tone:
          type: string
          enum:
            - calm
            - warning
            - danger
    IntakeEmptyState:
      type: object
      required:
        - title
        - body
        - action
      properties:
        title:
          type: string
        body:
          type: string
        reason:
          type: string
          enum:
            - no_visible_intake_work
            - active_tenant_prefilter_excludes_rows
        action:
          oneOf:
            - $ref: '#/components/schemas/OpenMyFindingsActionLink'
            - $ref: '#/components/schemas/ClearTenantFilterActionLink'
    ClaimFindingResult:
      type: object
      required:
        - findingId
        - tenantId
        - assigneeUserId
        - auditActionId
        - queueOutcome
        - nextPrimaryAction
      properties:
        findingId:
          type: integer
        tenantId:
          type: integer
        assigneeUserId:
          type: integer
        auditActionId:
          type: string
          enum:
            - finding.assigned
        queueOutcome:
          type: string
          enum:
            - removed_from_intake
        nextPrimaryAction:
          $ref: '#/components/schemas/OpenMyFindingsActionLink'
        nextInspectAction:
          oneOf:
            - $ref: '#/components/schemas/OpenFindingActionLink'
            - type: 'null'
    FindingDetailContinuation:
      type: object
      description: >-
        Continuity payload for tenant finding detail when it is opened from the
        Findings intake queue. The backLink is present whenever canonical intake
        navigation context is provided and may be null only for direct entry
        without intake continuity context.
      required:
        - findingId
        - tenantId
      properties:
        findingId:
          type: integer
        tenantId:
          type: integer
        backLink:
          oneOf:
            - $ref: '#/components/schemas/BackToFindingsIntakeActionLink'
            - 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
    OpenFindingActionLink:
      allOf:
        - $ref: '#/components/schemas/ActionLink'
        - type: object
          properties:
            label:
              type: string
              enum:
                - Open finding
    ClearTenantFilterActionLink:
      allOf:
        - $ref: '#/components/schemas/ActionLink'
        - type: object
          properties:
            label:
              type: string
              enum:
                - Clear tenant filter
    BackToFindingsIntakeActionLink:
      allOf:
        - $ref: '#/components/schemas/ActionLink'
        - type: object
          properties:
            label:
              type: string
              enum:
                - Back to findings intake
    Badge:
      type: object
      required:
        - label
      properties:
        label:
          type: string
        color:
          type:
            - string
            - 'null'