TenantAtlas/specs/225-assignment-hygiene/contracts/assignment-hygiene.logical.openapi.yaml
ahmido 12fb5ebb30
Some checks failed
Main Confidence / confidence (push) Failing after 1m20s
feat: add findings hygiene report and control catalog layering (#264)
## Summary
- add the workspace-scoped findings hygiene report, overview signal, and supporting classification service for broken assignments and stale in-progress work
- add Spec 225 artifacts and focused findings hygiene test coverage alongside the new Filament page and workspace overview wiring
- align product roadmap and spec candidates around the layered canonical control catalog, CIS library, and readiness model
- extend SpecKit constitution and templates with the XCUT-001 shared-pattern reuse guidance

## Notes
- validation commands and implementation close-out notes are documented in `specs/225-assignment-hygiene/plan.md` and `specs/225-assignment-hygiene/quickstart.md`
- this PR targets `dev` from `225-assignment-hygiene`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #264
2026-04-22 12:26:18 +00:00

276 lines
8.0 KiB
YAML

openapi: 3.1.0
info:
title: Assignment Hygiene Surface Contract
version: 1.0.0
summary: Logical internal contract for Spec 225 report rendering, overview discoverability, and finding drilldown.
description: |
This contract documents the structured payloads and UI-facing surfaces that Spec 225 must satisfy.
It is intentionally logical rather than public-API only: the feature reuses existing Filament pages,
workspace overview builders, and finding detail routes instead of introducing a new public controller namespace.
servers:
- url: https://logical.internal
description: Non-routable placeholder used to describe internal repository contracts.
paths:
/internal/findings/hygiene-issues:
get:
summary: Evaluate derived findings hygiene issues for the current visible workspace scope.
description: |
Logical internal contract implemented by the narrow findings hygiene service.
It derives `broken_assignment` and `stale_in_progress` from current finding, audit,
tenant-entitlement, and user lifecycle truth without persisting a second workflow state.
operationId: listFindingHygieneIssues
x-not-public-http: true
parameters:
- name: workspaceId
in: query
required: true
schema:
type: integer
- name: tenantId
in: query
required: false
schema:
type:
- integer
- 'null'
- name: reason
in: query
required: false
schema:
$ref: '#/components/schemas/HygieneReasonFilter'
responses:
'200':
description: Derived findings hygiene issues and visible-scope summary counts.
content:
application/vnd.tenantpilot.findings-hygiene-report+json:
schema:
$ref: '#/components/schemas/FindingsHygieneReportSurface'
/admin/findings/hygiene:
get:
summary: Canonical workspace-context findings hygiene report.
operationId: viewFindingsHygieneReport
parameters:
- name: tenant
in: query
required: false
schema:
type:
- string
- 'null'
- name: reason
in: query
required: false
schema:
$ref: '#/components/schemas/HygieneReasonFilter'
responses:
'200':
description: Existing Filament page renders the read-first findings hygiene report.
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.findings-hygiene-report+json:
schema:
$ref: '#/components/schemas/FindingsHygieneReportSurface'
'404':
description: Non-member or out-of-scope workspace remains deny-as-not-found; hidden tenants inside an entitled workspace are suppressed from rows, counts, filters, and hints rather than causing a route-level `404`.
/admin:
get:
summary: Workspace overview includes one findings hygiene signal.
operationId: viewWorkspaceOverviewWithFindingsHygieneSignal
responses:
'200':
description: Existing workspace overview renders the hygiene summary signal in attention or calm state for the current visible scope.
content:
text/html:
schema:
type: string
application/vnd.tenantpilot.findings-hygiene-overview-signal+json:
schema:
$ref: '#/components/schemas/FindingsHygieneOverviewSignal'
/admin/t/{tenant}/findings/{finding}:
get:
summary: Report drilldown opens the existing tenant finding detail route.
operationId: openFindingFromHygieneReport
parameters:
- name: tenant
in: path
required: true
schema:
type: integer
- name: finding
in: path
required: true
schema:
type: integer
responses:
'200':
description: Existing finding detail renders for an entitled tenant operator.
content:
text/html:
schema:
type: string
'403':
description: In-scope member lacks the current capability to inspect the finding.
'404':
description: Recipient no longer has tenant or record visibility.
components:
schemas:
HygieneReason:
type: string
enum:
- broken_assignment
- stale_in_progress
HygieneReasonFilter:
type: string
enum:
- all
- broken_assignment
- stale_in_progress
FindingsHygieneIssue:
type: object
required:
- findingId
- tenantId
- tenantName
- findingSummary
- status
- lastWorkflowActivityAt
- reasons
- detailUrl
properties:
findingId:
type: integer
workspaceId:
type: integer
tenantId:
type: integer
tenantName:
type: string
findingSummary:
type: string
status:
type: string
ownerUserId:
type:
- integer
- 'null'
ownerName:
type:
- string
- 'null'
assigneeUserId:
type:
- integer
- 'null'
assigneeName:
type:
- string
- 'null'
dueAt:
type:
- string
- 'null'
format: date-time
overdue:
type: boolean
lastWorkflowActivityAt:
type:
- string
- 'null'
format: date-time
reasons:
type: array
minItems: 1
items:
$ref: '#/components/schemas/HygieneReason'
detailUrl:
type: string
FindingsHygieneSummary:
type: object
required:
- uniqueIssueCount
- brokenAssignmentCount
- staleInProgressCount
- isCalm
properties:
uniqueIssueCount:
type: integer
minimum: 0
brokenAssignmentCount:
type: integer
minimum: 0
staleInProgressCount:
type: integer
minimum: 0
isCalm:
type: boolean
FindingsHygieneReportSurface:
type: object
required:
- appliedReasonFilter
- appliedTenantFilter
- issues
- summary
properties:
appliedReasonFilter:
$ref: '#/components/schemas/HygieneReasonFilter'
appliedTenantFilter:
type:
- string
- 'null'
issues:
type: array
items:
$ref: '#/components/schemas/FindingsHygieneIssue'
summary:
$ref: '#/components/schemas/FindingsHygieneSummary'
emptyState:
type:
- object
- 'null'
properties:
title:
type: string
description:
type: string
ctaLabel:
type:
- string
- 'null'
ctaUrl:
type:
- string
- 'null'
FindingsHygieneOverviewSignal:
type: object
required:
- headline
- description
- uniqueIssueCount
- brokenAssignmentCount
- staleInProgressCount
- isCalm
- ctaLabel
- ctaUrl
properties:
headline:
type: string
description:
type: string
description: Short operator-facing summary derived from broken-assignment and stale-in-progress counts.
uniqueIssueCount:
type: integer
minimum: 0
brokenAssignmentCount:
type: integer
minimum: 0
staleInProgressCount:
type: integer
minimum: 0
isCalm:
type: boolean
ctaLabel:
type: string
ctaUrl:
type: string