TenantAtlas/specs/219-finding-ownership-semantics/research.md
Ahmed Darrazi 1741b22203 docs: amend constitution to v2.7.0 (LEAN-001 pre-production lean doctrine)
- Add LEAN-001 to constitution after BIAS-001: forbids legacy aliases,
  migration shims, dual-write logic, and compatibility fixtures in a
  pre-production codebase
- Add compatibility posture default block to spec template
- Add pre-production compatibility check to agent instructions
- Unify backup_set operation type to canonical backup_set.update
- Remove all legacy backup_set.add_policies/remove_policies references
- Add finding ownership semantics (responsibility/accountability labels)
- Clean up roadmap.md and spec-candidates.md
2026-04-20 19:53:04 +02:00

5.0 KiB
Raw Blame History

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.