openapi: 3.1.0
info:
  title: Finding Outcome Taxonomy Logical Contract
  version: 0.1.0
  description: |
    Logical contract for the findings outcome taxonomy feature. This is not a new
    public HTTP API commitment. It documents the request and response shapes that
    existing Filament and service workflows must converge on.

servers:
  - url: https://tenantpilot.local
    description: Logical base URL only

tags:
  - name: Findings
  - name: FindingsInternal

paths:
  /tenants/{tenantId}/findings:
    get:
      tags: [Findings]
      summary: List tenant findings with terminal-outcome filters
      operationId: listTenantFindings
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - name: status
          in: query
          schema:
            $ref: '#/components/schemas/FindingStatus'
        - name: terminal_outcome
          in: query
          schema:
            $ref: '#/components/schemas/TerminalOutcomeKey'
        - name: verification_state
          in: query
          schema:
            $ref: '#/components/schemas/VerificationState'
      responses:
        '200':
          description: Tenant findings list
          content:
            application/json:
              schema:
                type: object
                required: [data]
                properties:
                  data:
                    type: array
                    items:
                      $ref: '#/components/schemas/FindingSummary'

  /tenants/{tenantId}/findings/{findingId}:
    get:
      tags: [Findings]
      summary: Get one finding with current terminal-outcome semantics
      operationId: getTenantFinding
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/FindingId'
      responses:
        '200':
          description: Finding detail
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingDetail'

  /tenants/{tenantId}/findings/{findingId}/resolve:
    post:
      tags: [Findings]
      summary: Resolve a finding with a bounded operator reason
      operationId: resolveTenantFinding
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/FindingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ResolveFindingRequest'
      responses:
        '200':
          description: Finding resolved pending verification
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingDetail'

  /tenants/{tenantId}/findings/{findingId}/close:
    post:
      tags: [Findings]
      summary: Close a finding with a bounded administrative reason
      operationId: closeTenantFinding
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/FindingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CloseFindingRequest'
      responses:
        '200':
          description: Finding closed with a non-remediation outcome
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingDetail'

  /tenants/{tenantId}/findings/{findingId}/reopen:
    post:
      tags: [Findings]
      summary: Reopen a terminal finding with a bounded reopen reason
      operationId: reopenTenantFinding
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/FindingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ReopenFindingRequest'
      responses:
        '200':
          description: Finding reopened
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingDetail'

  /internal/tenants/{tenantId}/findings/{findingId}/system-clear:
    post:
      tags: [FindingsInternal]
      summary: Apply a trusted system-clear reason
      operationId: systemClearFinding
      x-internal: true
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/FindingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SystemClearFindingRequest'
      responses:
        '200':
          description: Finding moved into a verified-cleared outcome
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingDetail'

  /internal/tenants/{tenantId}/findings/{findingId}/system-reopen:
    post:
      tags: [FindingsInternal]
      summary: Reopen a terminal finding due to trusted recurrence or verification failure
      operationId: systemReopenFinding
      x-internal: true
      parameters:
        - $ref: '#/components/parameters/TenantId'
        - $ref: '#/components/parameters/FindingId'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/SystemReopenFindingRequest'
      responses:
        '200':
          description: Finding reopened by trusted automation
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/FindingDetail'

components:
  parameters:
    TenantId:
      name: tenantId
      in: path
      required: true
      schema:
        type: integer
    FindingId:
      name: findingId
      in: path
      required: true
      schema:
        type: integer

  schemas:
    FindingStatus:
      type: string
      enum:
        - new
        - acknowledged
        - triaged
        - in_progress
        - reopened
        - resolved
        - closed
        - risk_accepted

    VerificationState:
      type: string
      enum:
        - pending_verification
        - verified_cleared
        - not_applicable

    ResolveReasonKey:
      type: string
      enum:
        - remediated
        - no_longer_drifting
        - permission_granted
        - permission_removed_from_registry
        - role_assignment_removed
        - ga_count_within_threshold

    CloseReasonKey:
      type: string
      enum:
        - false_positive
        - duplicate
        - no_longer_applicable
        - accepted_risk

    ReopenReasonKey:
      type: string
      enum:
        - recurred_after_resolution
        - verification_failed
        - manual_reassessment

    TerminalOutcomeKey:
      type: string
      enum:
        - resolved_pending_verification
        - verified_cleared
        - closed_false_positive
        - closed_duplicate
        - closed_no_longer_applicable
        - risk_accepted

    TerminalOutcome:
      type: object
      required:
        - key
        - label
        - verification_state
        - report_bucket
      properties:
        key:
          $ref: '#/components/schemas/TerminalOutcomeKey'
        label:
          type: string
        verification_state:
          $ref: '#/components/schemas/VerificationState'
        report_bucket:
          type: string
          enum:
            - remediation_pending_verification
            - remediation_verified
            - administrative_closure
            - accepted_risk
        governance_state:
          type: string
          nullable: true
          description: Present only when the outcome depends on risk-governance validity.

    ResolveFindingRequest:
      type: object
      required: [reason]
      properties:
        reason:
          type: string
          enum: [remediated]
        note:
          type: string
          maxLength: 255
          nullable: true

    CloseFindingRequest:
      type: object
      required: [reason]
      properties:
        reason:
          type: string
          enum:
            - false_positive
            - duplicate
            - no_longer_applicable
        note:
          type: string
          maxLength: 255
          nullable: true

    ReopenFindingRequest:
      type: object
      required: [reason]
      properties:
        reason:
          $ref: '#/components/schemas/ReopenReasonKey'
        note:
          type: string
          maxLength: 255
          nullable: true

    SystemClearFindingRequest:
      type: object
      required: [reason, observed_at]
      properties:
        reason:
          type: string
          enum:
            - no_longer_drifting
            - permission_granted
            - permission_removed_from_registry
            - role_assignment_removed
            - ga_count_within_threshold
        observed_at:
          type: string
          format: date-time
        operation_run_id:
          type: integer
          nullable: true

    SystemReopenFindingRequest:
      type: object
      required: [reason, observed_at]
      properties:
        reason:
          type: string
          enum:
            - recurred_after_resolution
            - verification_failed
        observed_at:
          type: string
          format: date-time
        operation_run_id:
          type: integer
          nullable: true

    FindingSummary:
      type: object
      required:
        - id
        - tenant_id
        - status
        - severity
        - terminal_outcome
      properties:
        id:
          type: integer
        tenant_id:
          type: integer
        status:
          $ref: '#/components/schemas/FindingStatus'
        severity:
          type: string
        resolved_reason:
          oneOf:
            - $ref: '#/components/schemas/ResolveReasonKey'
            - type: 'null'
        closed_reason:
          oneOf:
            - $ref: '#/components/schemas/CloseReasonKey'
            - type: 'null'
        terminal_outcome:
          $ref: '#/components/schemas/TerminalOutcome'

    FindingDetail:
      allOf:
        - $ref: '#/components/schemas/FindingSummary'
        - type: object
          properties:
            resolved_at:
              type: string
              format: date-time
              nullable: true
            closed_at:
              type: string
              format: date-time
              nullable: true
            reopened_at:
              type: string
              format: date-time
              nullable: true
            audit_context:
              type: object
              additionalProperties: true
              description: Logical placeholder for the readable audit/history payload.