Some checks failed
Main Confidence / confidence (push) Failing after 55s
## Summary - add the canonical admin-plane `My Findings` inbox at `/admin/findings/my-work` - add the workspace overview `Assigned to me` signal and inbox-to-detail continuity - add focused Pest coverage plus the full Spec 221 artifact bundle ## Validation - `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/MyWorkInboxTest.php tests/Feature/Authorization/MyWorkInboxAuthorizationTest.php tests/Feature/Dashboard/MyFindingsSignalTest.php` - `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceOverviewNavigationTest.php` - integrated-browser smoke completed against the browser-facing `tenantatlas` runtime, including seeded positive-path and negative-path checks plus fixture cleanup ## Filament v5 Guardrails - Livewire v4.0+ compliant - panel provider registration remains in `apps/platform/bootstrap/providers.php` - global search behavior is unchanged; `FindingResource` already has a View page and the new inbox is a custom page, not a searchable resource - no destructive actions were introduced on the inbox or overview signal - no new assets were added; the existing deploy step for `cd apps/platform && php artisan filament:assets` remains unchanged - coverage includes the new inbox page, authorization boundaries, the workspace overview signal, and the overview CTA regression Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #258
399 lines
12 KiB
YAML
399 lines
12 KiB
YAML
openapi: 3.1.0
|
|
info:
|
|
title: Findings Operator Inbox Surface Contract
|
|
version: 1.0.0
|
|
description: >-
|
|
Internal reference contract for the canonical My Findings inbox and the
|
|
workspace overview Assigned to me signal. The application continues to
|
|
return rendered HTML through Filament and Livewire. The vendor media types
|
|
below document the structured page models that must be derivable before
|
|
rendering. This is not a public API commitment.
|
|
paths:
|
|
/admin/findings/my-work:
|
|
get:
|
|
summary: Canonical personal findings inbox
|
|
description: >-
|
|
Returns the rendered admin-plane inbox for the current user's visible
|
|
assigned open findings. Personal assignment scope is fixed. Tenant
|
|
prefiltering may be derived from the active admin tenant context.
|
|
responses:
|
|
'302':
|
|
description: Redirects into the existing workspace or tenant chooser flow when membership exists but workspace context is not yet established
|
|
'200':
|
|
description: Rendered My Findings inbox page
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.my-findings-inbox+json:
|
|
schema:
|
|
$ref: '#/components/schemas/MyFindingsInboxPage'
|
|
'404':
|
|
description: Workspace scope is not visible because membership is missing or out of scope
|
|
/admin:
|
|
get:
|
|
summary: Workspace overview with assigned-to-me signal
|
|
description: >-
|
|
Returns the rendered workspace overview. The vendor media type documents
|
|
the embedded personal findings signal used to decide whether assigned
|
|
findings work exists before opening the inbox.
|
|
responses:
|
|
'302':
|
|
description: Redirects into the existing workspace or tenant chooser flow when membership exists but workspace context is not yet established
|
|
'200':
|
|
description: Rendered workspace overview page
|
|
content:
|
|
text/html:
|
|
schema:
|
|
type: string
|
|
application/vnd.tenantpilot.workspace-overview-my-findings+json:
|
|
schema:
|
|
$ref: '#/components/schemas/WorkspaceOverviewMyFindingsSignalSurface'
|
|
'404':
|
|
description: Workspace scope is not visible because membership is missing or out of scope
|
|
/admin/t/{tenant}/findings/{finding}:
|
|
get:
|
|
summary: Tenant finding detail with inbox 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 the My Findings inbox.
|
|
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-continuation+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:
|
|
MyFindingsInboxPage:
|
|
type: object
|
|
required:
|
|
- header
|
|
- appliedScope
|
|
- availableFilters
|
|
- summaryCounts
|
|
- rows
|
|
- emptyState
|
|
properties:
|
|
header:
|
|
$ref: '#/components/schemas/InboxHeader'
|
|
appliedScope:
|
|
$ref: '#/components/schemas/InboxAppliedScope'
|
|
availableFilters:
|
|
description: Includes the fixed assignee-scope filter plus tenant, overdue, reopened, and high-severity filters. Tenant filter options are derived only from visible capability-eligible tenants.
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/InboxFilterDefinition'
|
|
summaryCounts:
|
|
$ref: '#/components/schemas/MyFindingsSummaryCounts'
|
|
rows:
|
|
description: Rows are ordered overdue first, reopened non-overdue second, then remaining findings. 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/AssignedFindingInboxRow'
|
|
emptyState:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/InboxEmptyState'
|
|
- type: 'null'
|
|
InboxHeader:
|
|
type: object
|
|
required:
|
|
- title
|
|
- description
|
|
properties:
|
|
title:
|
|
type: string
|
|
enum:
|
|
- My Findings
|
|
description:
|
|
type: string
|
|
clearTenantFilterAction:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/ActionLink'
|
|
- type: 'null'
|
|
InboxAppliedScope:
|
|
type: object
|
|
required:
|
|
- workspaceScoped
|
|
- assigneeScope
|
|
- tenantPrefilterSource
|
|
properties:
|
|
workspaceScoped:
|
|
type: boolean
|
|
assigneeScope:
|
|
type: string
|
|
enum:
|
|
- current_user_only
|
|
tenantPrefilterSource:
|
|
type: string
|
|
enum:
|
|
- active_tenant_context
|
|
- explicit_filter
|
|
- none
|
|
tenantLabel:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
InboxFilterDefinition:
|
|
type: object
|
|
required:
|
|
- key
|
|
- label
|
|
- fixed
|
|
properties:
|
|
key:
|
|
type: string
|
|
enum:
|
|
- assignee_scope
|
|
- tenant
|
|
- overdue
|
|
- reopened
|
|
- high_severity
|
|
label:
|
|
type: string
|
|
fixed:
|
|
type: boolean
|
|
options:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/FilterOption'
|
|
FilterOption:
|
|
type: object
|
|
required:
|
|
- value
|
|
- label
|
|
properties:
|
|
value:
|
|
type: string
|
|
label:
|
|
type: string
|
|
MyFindingsSummaryCounts:
|
|
type: object
|
|
description: Counts derived from the currently visible inbox queue after the fixed assignee scope and any active tenant, overdue, reopened, or high-severity filters are applied.
|
|
required:
|
|
- openAssigned
|
|
- overdueAssigned
|
|
properties:
|
|
openAssigned:
|
|
type: integer
|
|
minimum: 0
|
|
overdueAssigned:
|
|
type: integer
|
|
minimum: 0
|
|
AssignedFindingInboxRow:
|
|
type: object
|
|
required:
|
|
- findingId
|
|
- tenantId
|
|
- tenantLabel
|
|
- summary
|
|
- severity
|
|
- status
|
|
- 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'
|
|
reopened:
|
|
type: boolean
|
|
ownerLabel:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
detailUrl:
|
|
type: string
|
|
navigationContext:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/CanonicalNavigationContext'
|
|
- type: 'null'
|
|
DueState:
|
|
type: object
|
|
required:
|
|
- label
|
|
- tone
|
|
properties:
|
|
label:
|
|
type: string
|
|
tone:
|
|
type: string
|
|
enum:
|
|
- calm
|
|
- warning
|
|
- danger
|
|
InboxEmptyState:
|
|
type: object
|
|
required:
|
|
- title
|
|
- body
|
|
- action
|
|
properties:
|
|
title:
|
|
type: string
|
|
body:
|
|
type: string
|
|
reason:
|
|
type: string
|
|
enum:
|
|
- no_visible_assigned_work
|
|
- active_tenant_prefilter_excludes_rows
|
|
action:
|
|
description: For `active_tenant_prefilter_excludes_rows`, clears the tenant prefilter and returns the queue to all visible tenants. For `no_visible_assigned_work`, opens the active tenant's findings list when tenant context exists; otherwise opens `/admin/choose-tenant` so the operator can establish tenant context before entering tenant findings.
|
|
$ref: '#/components/schemas/ActionLink'
|
|
WorkspaceOverviewMyFindingsSignalSurface:
|
|
type: object
|
|
required:
|
|
- workspaceId
|
|
- myFindingsSignal
|
|
properties:
|
|
workspaceId:
|
|
type: integer
|
|
myFindingsSignal:
|
|
$ref: '#/components/schemas/MyFindingsSignal'
|
|
MyFindingsSignal:
|
|
type: object
|
|
required:
|
|
- openAssignedCount
|
|
- overdueAssignedCount
|
|
- isCalm
|
|
- headline
|
|
- cta
|
|
properties:
|
|
openAssignedCount:
|
|
type: integer
|
|
minimum: 0
|
|
overdueAssignedCount:
|
|
type: integer
|
|
minimum: 0
|
|
isCalm:
|
|
type: boolean
|
|
headline:
|
|
type: string
|
|
enum:
|
|
- Assigned to me
|
|
description:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
cta:
|
|
$ref: '#/components/schemas/OpenMyFindingsActionLink'
|
|
FindingDetailContinuation:
|
|
type: object
|
|
description: Continuity payload for tenant finding detail when it is opened from the My Findings inbox. The backLink is present whenever canonical inbox navigation context is provided and may be null only for direct entry without inbox continuity context.
|
|
required:
|
|
- findingId
|
|
- tenantId
|
|
properties:
|
|
findingId:
|
|
type: integer
|
|
tenantId:
|
|
type: integer
|
|
backLink:
|
|
description: Present when the detail page is reached from the My Findings inbox with canonical navigation context; null only for direct navigation that did not originate from the inbox.
|
|
oneOf:
|
|
- $ref: '#/components/schemas/BackToMyFindingsActionLink'
|
|
- 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
|
|
BackToMyFindingsActionLink:
|
|
allOf:
|
|
- $ref: '#/components/schemas/ActionLink'
|
|
- type: object
|
|
properties:
|
|
label:
|
|
type: string
|
|
enum:
|
|
- Back to my findings
|
|
Badge:
|
|
type: object
|
|
required:
|
|
- label
|
|
properties:
|
|
label:
|
|
type: string
|
|
color:
|
|
type:
|
|
- string
|
|
- 'null' |