TenantAtlas/specs/219-finding-ownership-semantics/research.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

52 lines
5.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research: Finding Ownership Semantics Clarification
**Date**: 2026-04-20
**Branch**: `219-finding-ownership-semantics`
## Decision 1: Reuse the existing finding owner and assignee fields as the only responsibility truth
- **Decision**: Keep `findings.owner_user_id` and `findings.assignee_user_id` as the sole persisted responsibility fields and derive any responsibility-state wording from them.
- **Rationale**: The current model, workflow service, migrations, and tests already support independent owner and assignee assignment. The spec problem is semantic ambiguity, not missing persistence.
- **Alternatives considered**:
- Add a new persisted responsibility status column. Rejected because the state is directly derivable from existing nullable fields.
- Introduce a separate responsibility aggregate/service object. Rejected because one resource and one model can express the needed truth without adding a new layer.
## Decision 2: Treat assignee-without-owner as an accountability gap, not a healthy assigned state
- **Decision**: Use a derived responsibility contract where owner presence determines accountability. `owner != null && assignee == null` is `owned but unassigned`; `owner != null && assignee != null` is `assigned`; any `owner == null` case is an accountability gap, even if an assignee exists.
- **Rationale**: The spec defines owner as accountable for the outcome and assignee as actively executing work. Existing resolver logic already treats missing owner or missing assignee as follow-up needing attention.
- **Alternatives considered**:
- Treat any assignee as `assigned` even without an owner. Rejected because it hides the exact accountability gap the feature exists to surface.
- Introduce a fourth persisted state family. Rejected because the business consequence is presentation and next-action guidance, not new workflow routing.
## Decision 3: Keep exception owner explicitly separate from finding owner
- **Decision**: Preserve `finding_exceptions.owner_user_id` as a distinct governance concept and label it only as `Exception owner` when shown in a finding context.
- **Rationale**: The current resource already surfaces exception owner separately, and the spec explicitly calls out the risk of accepted-risk flows reintroducing ambiguity under a second `Owner` label.
- **Alternatives considered**:
- Collapse exception owner into finding owner language. Rejected because it would blur responsibility for the finding with responsibility for the exception artifact.
- Rename finding owner around exception flows only. Rejected because local relabeling would increase rather than reduce semantic drift.
## Decision 4: Keep the implementation local to the existing findings resource and current workflow services
- **Decision**: Make the semantics change in `FindingResource`, with only small supporting adjustments in `Finding`, `FindingWorkflowService`, `FindingExceptionService`, and `FindingRiskGovernanceResolver` if wording or derived next-action copy needs alignment.
- **Rationale**: The current UI already displays owner, assignee, and exception owner. The gap is where those semantics are explained, prioritized, and tested.
- **Alternatives considered**:
- Add a new presenter or explanation layer for responsibility semantics. Rejected because a local derived helper is sufficient and aligned with the constitutions anti-layering bias.
- Add a dedicated responsibility page. Rejected because the findings list/detail surfaces are already the operator decision context.
## Decision 5: Use focused Pest feature + Livewire coverage, not browser or heavy-governance tests
- **Decision**: Cover the feature with focused findings resource and workflow feature tests, including explicit tenant-context setup and Filament routing discipline.
- **Rationale**: Existing tests already prove tenant-member validation and row-action behavior. The missing coverage is list/detail semantics and owner-only versus assignee-only feedback.
- **Alternatives considered**:
- Browser tests. Rejected because the behavior is visible and provable through current Filament Livewire test patterns.
- Unit-only tests. Rejected because they would miss the operator-facing semantics that motivated the spec.
## Decision 6: Preserve current tenant-panel and canonical-admin context rules during testing
- **Decision**: Keep tenant context explicit in tests and use explicit panel selection for tenant-route assertions when necessary.
- **Rationale**: Repository memory shows two relevant pitfalls: tenant-scoped Filament list pages can lose context on later Livewire interactions, and `Resource::getUrl(..., tenant: $tenant)` defaults to the admin panel unless `panel: 'tenant'` is passed.
- **Alternatives considered**:
- Rely on implicit panel resolution in tests. Rejected because it can produce false redirects to `/admin/choose-tenant` instead of the intended authorization result.
- Broaden shared fixtures to always set tenant context. Rejected because the constitution prefers opt-in context rather than expensive defaults.