- 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
16 KiB
Implementation Plan: Finding Ownership Semantics Clarification
Branch: 001-finding-ownership-semantics | Date: 2026-04-20 | Spec: 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_ASSIGNremain 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.
FindingResourcealready has aviewpage, 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
OperationRunbehavior 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
FindingResourceand an optional local helper onFindingrather 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.phpcd 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)
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)
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_idandassignee_user_idfields, 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 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:
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
- Livewire v4.0+ compliance: Yes. The feature only adjusts existing Filament v5 resources/pages/actions that already run on Livewire v4.0+.
- Provider registration location: No new panel or service providers are needed. Existing Filament providers remain registered in
apps/platform/bootstrap/providers.php. - Global search:
FindingResourcealready has aviewpage viagetPages(), so any existing global-search participation remains compliant. This feature does not enable new global search. - 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. - 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:assetsstep is added. - 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.