TenantAtlas/specs/225-assignment-hygiene/data-model.md
Ahmed Darrazi bc07e05659
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m27s
feat: add findings hygiene report and control catalog layering
2026-04-22 14:19:23 +02:00

176 lines
5.7 KiB
Markdown

# Data Model: Assignment Hygiene & Stale Work Detection
## Overview
This feature introduces no new persisted business entity. Existing finding truth, audit logs, tenant memberships, and user lifecycle truth remain canonical. The new work is one bounded derived hygiene layer over those existing records.
## Existing Persistent Entities
### Finding
**Purpose**: Canonical tenant-scoped findings workflow truth for ownership, lifecycle, and timing.
**Key fields used by this feature**:
- `id`
- `workspace_id`
- `tenant_id`
- `status`
- `owner_user_id`
- `assignee_user_id`
- `due_at`
- `in_progress_at`
- `reopened_at`
- `resolved_at`
- `closed_at`
- `last_seen_at`
- `times_seen`
**Relationships**:
- belongs to one workspace
- belongs to one tenant
- may reference one current owner user
- may reference one current assignee user
**Rules relevant to hygiene**:
- Only non-terminal findings participate in hygiene evaluation.
- Owner-only findings are not hygiene issues by themselves.
- Unassigned intake findings are not hygiene issues by themselves.
- `last_seen_at` remains observation truth and must not reset the stale operator-work window.
### AuditLog
**Purpose**: Existing audit truth for workflow actions and responsibility changes.
**Key fields used by this feature**:
- `id`
- `workspace_id`
- `tenant_id`
- `auditable_type`
- `auditable_id`
- `action`
- `created_at`
**Rules relevant to hygiene**:
- Existing finding workflow audit actions provide one source of workflow-activity timestamps.
- No new audit action id is required for this feature because the report is read-only.
### User
**Purpose**: Canonical operator identity record.
**Key fields used by this feature**:
- `id`
- `deleted_at`
**Rules relevant to hygiene**:
- Soft-deleted users are treated as unavailable for current assignment execution.
- The feature does not add a separate availability state.
### Tenant Membership / Tenant Entitlement Truth
**Purpose**: Current authorization truth for whether an assignee can still work inside the tenant.
**Key inputs used by this feature**:
- membership existence for the tenant
- current tenant visibility
- current findings-view capability in the referenced tenant
**Rules relevant to hygiene**:
- Broken assignment is derived from current workability, not historical assignment legitimacy.
- Hidden tenants remain excluded from rows, counts, and filters.
## Derived Models
### FindingHygieneIssue
**Purpose**: Canonical derived issue envelope used by both the report and the overview signal.
**Fields**:
- `finding_id`
- `workspace_id`
- `tenant_id`
- `tenant_name`
- `finding_summary`
- `status`
- `owner_user_id`
- `owner_name`
- `assignee_user_id`
- `assignee_name`
- `reasons`: list of hygiene reasons
- `last_workflow_activity_at`
- `due_at`
- `is_overdue`
**Validation rules**:
- The finding must be non-terminal.
- `finding_summary`, `owner_name`, and `assignee_name` are display-ready values resolved from the current finding and loaded user relationships; they remain derived, not persisted.
- `reasons` contains one or more of the allowed hygiene reasons.
- `last_workflow_activity_at` is derived from workflow anchors only and must not come from observation freshness.
- One finding produces one issue row even when multiple reasons apply.
### HygieneReason
**Purpose**: Bounded derived reason vocabulary for this feature.
**Allowed values**:
- `broken_assignment`
- `stale_in_progress`
**Rules**:
- Reasons are derived labels, not persisted finding status.
- Reasons exist because they change operator routing and filter behavior on the hygiene surface.
### FindingsHygieneOverviewSignal
**Purpose**: Workspace overview summary projection for fast discovery.
**Fields**:
- `headline`
- `description`
- `unique_issue_count`
- `broken_assignment_count`
- `stale_in_progress_count`
- `is_calm`
- `cta_label`
- `cta_url`
**Rules**:
- Counts are based on unique visible finding issues, not duplicated reason rows.
- `description` is a short operator-facing summary derived from broken-assignment and stale-in-progress counts; no separate severity ordering is introduced.
- When `unique_issue_count = 0`, the signal remains visible in a calm state with zero issues, calm descriptive copy, and the canonical CTA into the report.
- The signal uses the same source query and tenant visibility truth as the canonical report.
## Classification Matrix
| Reason | Trigger | Required finding state | Required visibility truth | Exclusions |
|--------|---------|------------------------|---------------------------|------------|
| `broken_assignment` | Assignee can no longer act | non-terminal, `assignee_user_id` present | current user may see the finding and the assigned user is either soft-deleted or no longer tenant-entitled | owner-only, unassigned, hidden tenant |
| `stale_in_progress` | No meaningful workflow movement for 7 days | `status = in_progress` and non-terminal | current user may see the finding | merely overdue, recently reassigned, recently reopened, or recently moved into progress |
## Workflow Activity Anchor Rules
- `in_progress_at` is the baseline activity anchor for work that actually started.
- `reopened_at` can reset the stale window when the finding re-enters active lifecycle.
- Existing workflow audit rows for `finding.assigned`, `finding.in_progress`, and `finding.reopened` can supersede the baseline anchor when they are newer.
- `last_seen_at` and `times_seen` remain observation truth only and must not reset the stale-work window.
## Persistence Boundaries
- No new table, enum-backed persistence, or report snapshot is introduced.
- The hygiene report and overview signal are recalculated from current truth at read time.
- Repair continues through the existing finding detail mutation path and its current audit behavior.