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