Some checks failed
Main Confidence / confidence (push) Failing after 44s
## Summary - add the new admin findings intake queue at `/admin/findings/intake` with fixed `Unassigned` and `Needs triage` views, tenant-safe filtering, claim flow, and continuity into tenant finding detail and `My Findings` - add Spec 222 artifacts (`spec`, `plan`, `tasks`, `research`, `data model`, `quickstart`, contract, checklist) and register the new admin page - fix follow-up regressions uncovered during full-suite validation around findings action-surface declarations, findings list default columns, provider-blocked run messaging, operation catalog aliases, and workspace overview query volume ## Validation - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingsIntakeQueueTest.php tests/Feature/Authorization/FindingsIntakeAuthorizationTest.php tests/Feature/Findings/FindingsClaimHandoffTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact` ## Notes - Filament remains on v5 with Livewire v4-compatible patterns - provider registration remains unchanged in `apps/platform/bootstrap/providers.php` - no new assets or schema changes are introduced Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #260
470 lines
13 KiB
YAML
470 lines
13 KiB
YAML
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' |