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

199 lines
16 KiB
Markdown

# Implementation Plan: Finding Ownership Semantics Clarification
**Branch**: `219-finding-ownership-semantics` | **Date**: 2026-04-20 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `/specs/219-finding-ownership-semantics/spec.md`
## Summary
Clarify the meaning of finding owner versus finding assignee across the existing tenant findings list, detail surface, responsibility-update flows, and exception-request context without adding new persistence, capabilities, or workflow services. The implementation will reuse the existing `owner_user_id` and `assignee_user_id` fields, add a derived responsibility-state presentation layer on top of current data, tighten operator-facing copy and audit/feedback wording, preserve tenant-safe Filament behavior, and extend focused Pest + Livewire coverage for list/detail semantics, responsibility updates, and exception-owner boundary cases.
## Technical Context
**Language/Version**: PHP 8.4.15 / Laravel 12
**Primary Dependencies**: Filament v5, Livewire v4.0+, Pest v4, Tailwind CSS v4
**Storage**: PostgreSQL via Sail; existing `findings.owner_user_id`, `findings.assignee_user_id`, and `finding_exceptions.owner_user_id` fields; no schema changes planned
**Testing**: Pest v4 feature and Livewire component tests via `./vendor/bin/sail artisan test --compact`
**Validation Lanes**: fast-feedback, confidence
**Target Platform**: Laravel monolith in Sail/Docker locally; Dokploy-hosted Linux deployment for staging/production
**Project Type**: Laravel monolith / Filament admin application
**Performance Goals**: No new remote calls, no new queued work, no material list/detail query growth beyond current eager loading of owner, assignee, and exception-owner relations
**Constraints**: Keep responsibility state derived rather than persisted; preserve existing tenant membership validation; preserve deny-as-not-found tenant isolation; do not split capabilities or add new abstractions; keep destructive-action confirmations unchanged
**Scale/Scope**: 1 primary Filament resource, 1 model helper or equivalent derived-state mapping, 1 workflow/audit wording touchpoint, 1 exception-owner wording boundary, and 2 focused new/expanded feature test families
## UI / Surface Guardrail Plan
- **Guardrail scope**: changed surfaces
- **Native vs custom classification summary**: native
- **Shared-family relevance**: existing tenant findings resource and exception-context surfaces only
- **State layers in scope**: page, detail, URL-query
- **Handling modes by drift class or surface**: review-mandatory
- **Repository-signal treatment**: review-mandatory
- **Special surface test profiles**: standard-native-filament
- **Required tests or manual smoke**: functional-core, state-contract
- **Exception path and spread control**: none; the feature reuses existing resource/table/infolist/action primitives and keeps exception-owner semantics local to the finding context
- **Active feature PR close-out entry**: Guardrail
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first: PASS. The feature only clarifies operator semantics on tenant-owned findings and exception artifacts; it does not change observed-state or snapshot truth.
- Read/write separation: PASS. Responsibility updates remain TenantPilot-only writes on existing finding records; no Microsoft tenant mutation is introduced. Existing destructive-like actions keep their current confirmation rules.
- Graph contract path: PASS / N/A. No Graph calls or contract-registry changes are involved.
- Deterministic capabilities: PASS. Existing canonical findings capabilities remain the source of truth; no new capability split is introduced.
- RBAC-UX: PASS. Tenant-context membership remains the isolation boundary. Non-members remain 404 and in-scope members missing `TENANT_FINDINGS_ASSIGN` remain 403 for responsibility mutations.
- Workspace isolation: PASS. The feature remains inside tenant-context findings flows and preserves existing workspace-context stabilization patterns.
- Global search: PASS. `FindingResource` already has a `view` page, so any existing global-search participation remains compliant. This feature does not add or remove global search.
- Tenant isolation: PASS. All reads and writes remain tenant-scoped and continue to restrict owner/assignee selections to current tenant members.
- Run observability / Ops-UX: PASS / N/A. No new long-running, queued, or remote work is introduced and no `OperationRun` behavior changes.
- Automation / data minimization: PASS / N/A. No new background automation or payload persistence is introduced.
- Test governance (TEST-GOV-001): PASS. The narrowest proving surface is feature-level Filament + workflow coverage with low fixture cost and no heavy-family expansion.
- Proportionality / no premature abstraction / persisted truth / behavioral state: PASS. Responsibility state remains derived from existing fields and does not create a persisted enum, new abstraction, or new table.
- UI semantics / few layers: PASS. The plan uses direct domain-to-UI mapping on `FindingResource` and an optional local helper on `Finding` rather than a new presenter or taxonomy layer.
- Badge semantics (BADGE-001): PASS with restraint. If a new responsibility badge or label is added, it stays local to findings semantics unless multiple consumers later prove centralization is necessary.
- Filament-native UI / action surface contract / UX-001: PASS. Existing native Filament tables, infolists, filters, selects, grouped actions, and modals remain the implementation path; the finding remains the sole primary inspect/open model and row click remains the canonical inspect affordance.
**Post-Phase-1 re-check**: PASS. The design keeps responsibility semantics derived, tenant-safe, and local to the existing findings resource and tests.
## Test Governance Check
- **Test purpose / classification by changed surface**: Feature
- **Affected validation lanes**: fast-feedback, confidence
- **Test governance outcome**: document-in-feature
- **Why this lane mix is the narrowest sufficient proof**: The business truth is visible in Filament list/detail rendering, responsibility action behavior, and tenant-scoped audit feedback. Unit-only testing would miss the operator-facing semantics, while browser/heavy-governance coverage would add unnecessary cost.
- **Narrowest proving command(s)**:
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/Resources/FindingResourceOwnershipSemanticsTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings/FindingAssignmentAuditSemanticsTest.php`
- **Fixture / helper / factory / seed / context cost risks**: Low. Existing tenant/user/finding factories and tenant membership helpers are sufficient. The only explicit context risk is tenant-panel routing and admin canonical tenant state.
- **Expensive defaults or shared helper growth introduced?**: no; tests should keep tenant context explicit rather than broadening shared fixtures.
- **Heavy-family additions, promotions, or visibility changes**: none
- **Surface-class relief / special coverage rule**: standard-native relief; explicit tenant-panel routing is required for authorization assertions.
- **Closing validation and reviewer handoff**: Re-run the two focused test files plus formatter on dirty files. Reviewers should verify owner versus assignee wording on list/detail surfaces, exception-owner separation, and 404/403 semantics for out-of-scope versus in-scope unauthorized users.
- **Budget / baseline / trend follow-up**: none
- **Review-stop questions**: lane fit, hidden fixture cost, accidental presenter growth, tenant-context drift in tests
- **Escalation path**: none
- **Active feature PR close-out entry**: Guardrail
- **Why no dedicated follow-up spec is needed**: This work stays inside the existing findings responsibility contract. Only future queue/team-routing or capability-split work would justify a separate spec.
## Project Structure
### Documentation (this feature)
```text
specs/219-finding-ownership-semantics/
├── plan.md
├── spec.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── finding-responsibility.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md
```
### Source Code (repository root)
```text
apps/platform/
├── app/
│ ├── Filament/
│ │ └── Resources/
│ │ └── FindingResource.php # MODIFY: list/detail labels, derived responsibility state, filters, action help text, exception-owner wording
│ ├── Models/
│ │ └── Finding.php # MODIFY: local derived responsibility-state helper if needed
│ └── Services/
│ └── Findings/
│ ├── FindingWorkflowService.php # MODIFY: mutation feedback / audit wording for owner-only, assignee-only, clear, and combined changes
│ ├── FindingExceptionService.php # MODIFY: request-exception wording if the exception-owner boundary needs alignment
│ └── FindingRiskGovernanceResolver.php # MODIFY: next-action copy to reflect orphaned accountability semantics
└── tests/
└── Feature/
├── Filament/
│ └── Resources/
│ └── FindingResourceOwnershipSemanticsTest.php # NEW: list/detail rendering, filters, exception-owner distinction, tenant-safe semantics
└── Findings/
├── FindingAssignmentAuditSemanticsTest.php # NEW: owner-only, assignee-only, combined update feedback/audit semantics
├── FindingWorkflowRowActionsTest.php # MODIFY: assignment form/help-text semantics and member validation coverage
└── FindingWorkflowServiceTest.php # MODIFY: audit metadata and responsibility-mutation expectations
```
**Structure Decision**: Keep all work inside the existing Laravel/Filament monolith. The implementation is a targeted semantics pass over the current findings resource and workflow tests; no new folders, packages, or service families are required.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| — | — | — |
## Proportionality Review
- **Current operator problem**: Operators cannot tell whether a finding change transferred accountability, active remediation work, or only exception ownership.
- **Existing structure is insufficient because**: The existing fields and actions exist, but the current UI copy and derived next-step language do not establish a stable contract across list, detail, and exception flows.
- **Narrowest correct implementation**: Reuse the existing `owner_user_id` and `assignee_user_id` fields, derive responsibility state from them, and tighten wording on existing Filament surfaces and audit feedback.
- **Ownership cost created**: Low ongoing UI/test maintenance to keep future findings work aligned with the clarified contract.
- **Alternative intentionally rejected**: A new ownership framework, queue model, or capability split was rejected because the current product has not yet exhausted the simpler owner-versus-assignee model.
- **Release truth**: Current-release truth
## Phase 0 — Research (output: `research.md`)
See: [research.md](./research.md)
Research goals:
- Confirm the existing source of truth for owner, assignee, and exception owner.
- Confirm the smallest derived responsibility-state model that fits the current schema.
- Confirm the existing findings tests and Filament routing pitfalls to avoid false negatives.
- Confirm which operator-facing wording changes belong in resource copy versus workflow service feedback.
## Phase 1 — Design & Contracts (outputs: `data-model.md`, `contracts/`, `quickstart.md`)
See:
- [data-model.md](./data-model.md)
- [contracts/finding-responsibility.openapi.yaml](./contracts/finding-responsibility.openapi.yaml)
- [quickstart.md](./quickstart.md)
Design focus:
- Keep responsibility truth on existing finding and finding-exception records.
- Model responsibility state as a derived projection over owner and assignee presence rather than a persisted enum.
- Preserve exception owner as a separate governance concept when shown from a finding context.
- Keep tenant membership validation and existing `FindingWorkflowService::assign()` semantics as the mutation boundary.
## Phase 2 — Implementation Outline (tasks created in `/speckit.tasks`)
### Surface semantics pass
- Update the findings list column hierarchy so owner and assignee meaning is explicit at first scan.
- Add a derived responsibility-state label or equivalent summary on list/detail surfaces.
- Keep exception owner visibly separate from finding owner wherever both appear.
### Responsibility mutation clarity
- Add owner/assignee help text to assignment flows.
- Differentiate owner-only, assignee-only, clear-owner, clear-assignee, and combined responsibility changes in operator feedback and audit-facing wording.
- Keep current tenant-member validation and open-finding restrictions unchanged.
### Personal-work and next-action alignment
- Add or refine personal-work filters so assignee-based work and owner-based accountability are explicitly separate.
- Update next-action copy for owner-missing states so assignee-only findings are treated as accountability gaps.
### Regression protection
- Add focused list/detail rendering tests for owner-only, assignee-only, both-set, same-user, and both-null states.
- Add focused responsibility-update tests for owner-only, assignee-only, clear-owner, clear-assignee, and combined changes.
- Preserve tenant-context and authorization regression coverage using explicit Filament panel routing where needed.
### Verification
- Run the two focused Pest files and any directly modified sibling findings tests.
- Run Pint on dirty files through Sail.
## Constitution Check (Post-Design)
Re-check result: PASS. The design stays inside the existing findings domain, preserves tenant isolation and capability enforcement, avoids new persisted truth or semantic framework growth, and keeps responsibility state derived from current fields.
## Filament v5 Agent Output Contract
1. **Livewire v4.0+ compliance**: Yes. The feature only adjusts existing Filament v5 resources/pages/actions that already run on Livewire v4.0+.
2. **Provider registration location**: No new panel or service providers are needed. Existing Filament providers remain registered in `apps/platform/bootstrap/providers.php`.
3. **Global search**: `FindingResource` already has a `view` page via `getPages()`, so any existing global-search participation remains compliant. This feature does not enable new global search.
4. **Destructive actions and authorization**: No new destructive actions are introduced. Existing destructive-like findings actions remain server-authorized and keep `->requiresConfirmation()` where already required. Responsibility updates continue to enforce tenant membership and the canonical findings capability registry.
5. **Asset strategy**: No new frontend assets or published views. The feature uses existing Filament tables, infolists, filters, and action modals, so deployment asset handling stays unchanged and no new `filament:assets` step is added.
6. **Testing plan**: Cover the change with focused Pest feature tests for findings resource responsibility semantics, assignment/audit wording, and existing workflow regression surfaces. No browser or heavy-governance expansion is planned.