TenantAtlas/specs/151-findings-workflow-backstop/contracts/findings-workflow.openapi.yaml
2026-03-18 13:56:09 +01:00

357 lines
11 KiB
YAML

openapi: 3.1.0
info:
title: Findings Workflow Semantic Contract
version: 0.1.0
description: |
Semantic contract for tenant-context findings workflow operations and audit review.
These endpoints describe the canonical server-side behavior required by the feature,
even if concrete implementation remains behind Filament and Livewire action handlers.
Human-driven and system-driven lifecycle mutations share the same server-side
transition gateway and audit contract.
servers:
- url: http://localhost
paths:
/admin/t/{tenant}/findings/{finding}:
get:
summary: View a tenant-scoped finding
operationId: viewFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
responses:
'200':
description: Finding returned within the entitled tenant scope
content:
application/json:
schema:
$ref: '#/components/schemas/Finding'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
/admin/t/{tenant}/findings/{finding}/workflow/triage:
post:
summary: Triage a finding
operationId: triageFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
/admin/t/{tenant}/findings/{finding}/workflow/start-progress:
post:
summary: Move a finding to in progress
operationId: startFindingProgress
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
/admin/t/{tenant}/findings/{finding}/workflow/assign:
post:
summary: Assign finding ownership or assignee
operationId: assignFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
requestBody:
required: true
content:
application/json:
schema:
type: object
properties:
assignee_user_id:
type:
- integer
- 'null'
owner_user_id:
type:
- integer
- 'null'
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
'422':
$ref: '#/components/responses/ValidationError'
/admin/t/{tenant}/findings/{finding}/workflow/resolve:
post:
summary: Resolve a finding
operationId: resolveFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [reason]
properties:
reason:
type: string
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
'422':
$ref: '#/components/responses/ValidationError'
/admin/t/{tenant}/findings/{finding}/workflow/close:
post:
summary: Close a finding
operationId: closeFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [reason]
properties:
reason:
type: string
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
'422':
$ref: '#/components/responses/ValidationError'
/admin/t/{tenant}/findings/{finding}/workflow/risk-accept:
post:
summary: Mark a finding as risk accepted
operationId: riskAcceptFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [reason]
properties:
reason:
type: string
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
'422':
$ref: '#/components/responses/ValidationError'
/admin/t/{tenant}/findings/{finding}/workflow/reopen:
post:
summary: Reopen a terminal finding
operationId: reopenFinding
parameters:
- $ref: '#/components/parameters/TenantId'
- $ref: '#/components/parameters/FindingId'
responses:
'200':
$ref: '#/components/responses/FindingMutationSucceeded'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
/admin/t/{tenant}/findings/workflow/bulk:
post:
summary: Execute a bulk workflow mutation over multiple findings
operationId: bulkMutateFindings
parameters:
- $ref: '#/components/parameters/TenantId'
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [operation, finding_ids]
properties:
operation:
type: string
enum: [triage, assign, resolve, close, risk_accept, reopen]
finding_ids:
type: array
items:
type: integer
payload:
type: object
additionalProperties: true
responses:
'200':
description: Bulk mutation completed with per-record audit history
content:
application/json:
schema:
type: object
required: [processed, succeeded, failed]
properties:
processed:
type: integer
succeeded:
type: integer
failed:
type: integer
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
'409':
$ref: '#/components/responses/InvalidTransition'
/admin/audit-log:
get:
summary: Review finding-related audit history on the canonical audit surface
operationId: listFindingAuditEvents
parameters:
- name: resource_type
in: query
schema:
type: string
enum: [finding]
- name: tenant_id
in: query
schema:
type: integer
responses:
'200':
description: Audit events visible within current workspace and tenant entitlement
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/FindingAuditEvent'
'403':
$ref: '#/components/responses/Forbidden'
'404':
$ref: '#/components/responses/NotFound'
components:
parameters:
TenantId:
name: tenant
in: path
required: true
schema:
type: integer
FindingId:
name: finding
in: path
required: true
schema:
type: integer
responses:
FindingMutationSucceeded:
description: Workflow mutation completed and durable audit history was written
content:
application/json:
schema:
type: object
required: [finding, audit_event]
properties:
finding:
$ref: '#/components/schemas/Finding'
audit_event:
$ref: '#/components/schemas/FindingAuditEvent'
Forbidden:
description: Viewer is in scope but lacks required capability
NotFound:
description: Viewer is not entitled to the requested workspace or tenant scope, or the record is outside that scope, including workspace-mismatched findings in canonical tenant context
InvalidTransition:
description: Requested lifecycle mutation is not valid from the current state
ValidationError:
description: Request payload is syntactically valid but missing required business fields
schemas:
Finding:
type: object
required: [id, tenant_id, workspace_id, status, severity]
properties:
id:
type: integer
tenant_id:
type: integer
workspace_id:
type: integer
status:
type: string
enum: [new, acknowledged, triaged, in_progress, reopened, resolved, closed, risk_accepted]
severity:
type: string
enum: [low, medium, high, critical]
due_at:
type:
- string
- 'null'
format: date-time
resolved_reason:
type:
- string
- 'null'
closed_reason:
type:
- string
- 'null'
FindingAuditEvent:
type: object
required: [action, resource_type, resource_id, tenant_id, workspace_id, metadata]
properties:
action:
type: string
example: finding.resolved
resource_type:
type: string
enum: [finding]
resource_id:
type: string
tenant_id:
type:
- integer
- 'null'
workspace_id:
type: integer
metadata:
$ref: './finding-audit-event.schema.yaml#/components/schemas/FindingAuditMetadata'
actor_type:
type: string
enum: [human, system, scheduled, integration, platform]
description: Distinguishes system-origin workflow mutations from human-initiated actions on the canonical audit surface.