# Implementation Plan: Finding Ownership Semantics Clarification **Branch**: `001-finding-ownership-semantics` | **Date**: 2026-04-20 | **Spec**: [spec.md](./spec.md) **Input**: Feature specification from `/specs/001-finding-ownership-semantics/spec.md` **Note**: The setup script reported a numeric-prefix collision with `001-rbac-onboarding`, but it still resolved the active branch and plan path correctly to this feature directory. Planning continues against the current branch path. ## 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 - **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/001-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 vs assignee-only 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, 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, 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.