TenantAtlas/specs/219-finding-ownership-semantics/data-model.md
ahmido c86b399b43
Some checks failed
Main Confidence / confidence (push) Failing after 53s
feat(219): Finding ownership semantics + LEAN-001 constitution + backup_set unification (#256)
## Summary

This PR delivers three related improvements:

### 1. Finding Ownership Semantics (Spec 219)
- Add responsibility/accountability labels to findings and finding exceptions
- `owner_user_id` = accountable party (governance owner)
- `assignee_user_id` = responsible party (technical implementer)
- Expose Assign/Reassign actions in FindingResource with audit logging
- Add ownership columns and filters to finding list
- Propagate owner from finding to exception on creation
- Tests: ownership semantics, assignment audit, workflow actions

### 2. Constitution v2.7.0 — LEAN-001 Pre-Production Lean Doctrine
- New principle forbidding legacy aliases, migration shims, dual-write logic, and compatibility fixtures in a pre-production codebase
- AI-agent 4-question verification gate before adding any compatibility path
- Review rule: compatibility shims without answering the gate questions = merge blocker
- Exit condition: LEAN-001 expires at first production deployment
- Spec template: added default "Compatibility posture" block
- Agent instructions: added "Pre-production compatibility check" section

### 3. Backup Set Operation Type Unification
- Unified `backup_set.add_policies` and `backup_set.remove_policies` into single canonical `backup_set.update`
- Removed all legacy aliases, constants, and test fixtures
- Added lifecycle coverage for `backup_set.update` in config
- Updated all 14+ test files referencing legacy types

### Spec Artifacts
- `specs/219-finding-ownership-semantics/` — full spec, plan, tasks, research, data model, contracts, checklist

### Tests
- All affected tests pass (OperationCatalog, backup set, finding workflow, ownership semantics)

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #256
2026-04-20 17:54:33 +00:00

5.6 KiB

Data Model: Finding Ownership Semantics Clarification

Date: 2026-04-20
Branch: 219-finding-ownership-semantics

Overview

This feature introduces no new persisted entities. It clarifies responsibility semantics over existing finding and finding-exception records and adds one derived responsibility-state projection for operator-facing surfaces.

Entity: Finding

Represents: A tenant-owned operational governance finding that moves through the findings workflow and may carry both accountable ownership and active remediation assignment.

Key Fields

Field Type Required Notes
id bigint yes Primary key
workspace_id bigint yes Derived tenant ownership boundary
tenant_id bigint yes Tenant isolation boundary
status string yes Existing findings lifecycle state
severity string yes Existing severity dimension
owner_user_id bigint nullable no Accountable person for the finding outcome
assignee_user_id bigint nullable no Active remediation executor / coordinator
due_at datetime nullable no Existing SLA/follow-up deadline
resolved_reason string nullable no Existing closure context
closed_reason string nullable no Existing closure/governance context

Relationships

Relationship Target Cardinality Purpose
tenant() Tenant belongsTo Tenant ownership and authorization
ownerUser() User belongsTo Accountable owner
assigneeUser() User belongsTo Active remediation assignee
findingException() FindingException hasOne Optional exception artifact for accepted-risk governance

Validation Rules

  • owner_user_id MAY be null.
  • assignee_user_id MAY be null.
  • If present, either user ID MUST reference a current member of the active tenant.
  • Responsibility changes are allowed only on open findings, matching the current FindingWorkflowService::assign() rule.

Entity: FindingException

Represents: A tenant-owned exception artifact attached to a finding when governance coverage is requested or granted.

Key Fields

Field Type Required Notes
id bigint yes Primary key
finding_id bigint yes Owning finding
tenant_id bigint yes Tenant isolation boundary
owner_user_id bigint nullable no Accountable owner of the exception artifact, not of the finding itself
status string yes Existing exception lifecycle state
current_validity_state string nullable no Existing governance-validity dimension
request_reason text yes Existing request context

Relationships

Relationship Target Cardinality Purpose
finding() Finding belongsTo Parent finding context
owner() User belongsTo Exception artifact owner

Validation Rules

  • Exception-owner selection continues to use current tenant-member validation.
  • Exception ownership MUST remain semantically distinct from finding ownership on all mixed-context surfaces.

Derived Projection: ResponsibilityState

Represents: An operator-facing derived state computed from owner_user_id and assignee_user_id without new persistence.

Naming convention:

  • Operator-facing UI label: orphaned accountability
  • Internal derived-state and contract slug: orphaned_accountability

Derived Values

Derived State Rule Operator Meaning
orphaned_accountability owner_user_id == null No accountable owner is set. This remains true even if an assignee exists.
owned_unassigned owner_user_id != null && assignee_user_id == null Someone owns the outcome, but active remediation work is not assigned.
assigned owner_user_id != null && assignee_user_id != null Accountability and active remediation assignment are both set.

Rendering Notes

  • If owner and assignee are the same user, the state remains assigned; the UI should show both roles satisfied without implying a data problem.
  • If both are null, the finding still uses the slug orphaned_accountability and the visible label orphaned accountability.
  • If assignee is present but owner is null, the finding remains orphaned_accountability; the UI may also show that remediation is assigned without accountable ownership.

Mutation Contract: ResponsibilityUpdate

Represents: The input/output contract of the existing assignment action.

Input Shape

Field Type Required Notes
owner_user_id bigint nullable no Set, change, or clear finding owner
assignee_user_id bigint nullable no Set, change, or clear finding assignee

Behavioral Rules

  • The existing FindingWorkflowService::assign() method remains the mutation boundary.
  • The service MUST continue to write both fields explicitly to the finding.
  • Operator feedback and audit-facing wording should classify the result as owner_only, assignee_only, clear_owner, clear_assignee, or owner_and_assignee when both fields change in one update.

State and Lifecycle Impact

This feature does not add a new lifecycle family. It overlays responsibility semantics on top of existing findings lifecycle states.

Existing Lifecycle State Responsibility Impact
new, triaged, in_progress, reopened, acknowledged Responsibility state is actionable and visible by default
resolved, closed Responsibility remains historical context only
risk_accepted Responsibility remains visible, but exception-owner context may also appear and must remain separate