Some checks failed
Main Confidence / confidence (push) Failing after 48s
## Summary - implement the finding outcome taxonomy end-to-end with canonical resolve, close, reopen, and verification semantics - align finding UI, filters, audit metadata, review summaries, and export/read-model consumers to the shared outcome semantics - add focused Pest coverage and complete the spec artifacts for feature 231 ## Details - manual resolve is limited to the canonical `remediated` outcome - close and reopen flows now use bounded canonical reasons - trusted system clear and reopen distinguish verified-clear from verification-failed and recurrence paths - duplicate lifecycle backfill now closes findings canonically as `duplicate` - accepted-risk recording now uses the canonical `accepted_risk` reason - finding detail and list surfaces now expose terminal outcome and verification summaries - review, snapshot, and review-pack consumers now propagate the same outcome buckets ## Filament / Platform Contract - Livewire v4.0+ compatibility remains intact - provider registration is unchanged and remains in `bootstrap/providers.php` - no new globally searchable resource was introduced; `FindingResource` still has a View page and `TenantReviewResource` remains globally searchable false - lifecycle mutations still run through confirmed Filament actions with capability enforcement - no new asset family was added; the existing `filament:assets` deploy step is unchanged ## Verification - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingWorkflowServiceTest.php tests/Feature/Findings/FindingRecurrenceTest.php tests/Feature/Findings/FindingsListFiltersTest.php tests/Feature/Filament/FindingResolvedReferencePresentationTest.php tests/Feature/Findings/FindingOutcomeSummaryReportingTest.php tests/Feature/Findings/FindingRiskGovernanceProjectionTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings tests/Feature/Filament/FindingResolvedReferencePresentationTest.php tests/Feature/Models/FindingResolvedTest.php tests/Unit/Findings/FindingWorkflowServiceTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantReview/TenantReviewExplanationSurfaceTest.php tests/Feature/TenantReview/TenantReviewRegisterTest.php tests/Feature/ReviewPack/TenantReviewDerivedReviewPackTest.php` - browser smoke: `/admin/findings/my-work` -> finding detail resolve flow -> queue regression check passed ## Notes - this commit also includes the existing `.github/agents/copilot-instructions.md` workspace change that was already present in the worktree when all changes were committed Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #267
397 lines
10 KiB
YAML
397 lines
10 KiB
YAML
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. |