## 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
24 KiB
Feature Specification: Finding Ownership Semantics Clarification
Feature Branch: 219-finding-ownership-semantics
Created: 2026-04-20
Status: Draft
Input: User description: "Finding Ownership Semantics Clarification"
Spec Candidate Check (mandatory — SPEC-GATE-001)
- Problem: Open findings already store both an owner and an assignee, but the product does not yet state clearly which field represents accountability and which field represents active execution.
- Today's failure: Operators can see or change responsibility on a finding without being sure whether they transferred accountability, execution work, or only exception ownership, which makes triage slower and audit language less trustworthy.
- User-visible improvement: Findings surfaces make accountable owner, active assignee, and exception owner visibly distinct, so operators can route work faster and read ownership changes honestly.
- Smallest enterprise-capable version: Clarify the existing responsibility contract on current findings list/detail/action surfaces, add explicit derived responsibility states, and align audit and filter language without creating new roles, queues, or persistence.
- Explicit non-goals: No team queues, no escalation engine, no automatic reassignment hygiene, no new capability split, no new ownership framework, and no mandatory backfill before rollout.
- Permanent complexity imported: One explicit product contract for finding owner versus assignee, one derived responsibility-state vocabulary, and focused acceptance tests for mixed ownership contexts.
- Why now: The next findings execution slices depend on honest ownership semantics, and the ambiguity already affects triage, reassignment, and exception routing in today’s tenant workflow.
- Why not local: A one-surface copy fix would leave contradictory meaning across the findings list, detail view, assignment flows, personal work cues, and exception-request language.
- Approval class: Core Enterprise
- Red flags triggered: None after scope cut. This spec clarifies existing fields instead of introducing a new semantics axis.
- Score: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 2 | Produktnähe: 2 | Wiederverwendung: 1 | Gesamt: 11/12
- Decision: approve
Spec Scope Fields (mandatory)
- Scope: tenant
- Primary Routes:
/admin/t/{tenant}/findings,/admin/t/{tenant}/findings/{finding} - Data Ownership: Tenant-owned findings remain the source of truth; this spec also covers exception ownership only when it is shown or edited from a finding-context surface.
- RBAC: Tenant membership is required for visibility. Tenant findings view permission gates read access. Tenant findings assign permission gates owner and assignee changes. Non-members or cross-tenant requests remain deny-as-not-found. Members without mutation permission receive an explicit authorization failure.
UI / Surface Guardrail Impact (mandatory when operator-facing surfaces are changed; otherwise write N/A)
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / N/A Note |
|---|---|---|---|---|---|---|
| Tenant findings list and grouped bulk actions | yes | Native Filament + existing UI enforcement helpers | Same tenant work-queue family as other tenant resources | table, bulk-action modal, notification copy | no | Clarifies an existing resource instead of adding a new page |
| Finding detail and single-record action modals | yes | Native Filament infolist + action modals | Same finding resource family | detail, action modal, helper copy | no | Keeps one decision flow inside the current resource |
Decision-First Surface Role (mandatory when operator-facing surfaces are changed)
| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction |
|---|---|---|---|---|---|---|---|
| Tenant findings list and grouped bulk actions | Primary Decision Surface | A tenant operator reviews the open backlog and decides who owns the outcome and who should act next | Severity, lifecycle status, due state, owner, assignee, and derived responsibility state | Full evidence, audit trail, run metadata, and exception details | Primary because it is the queue where responsibility is routed at scale | Follows the operator’s “what needs action now?” workflow instead of raw data lineage | Removes the need to open each record just to tell who is accountable versus merely assigned |
| Finding detail and single-record action modals | Secondary Context Surface | A tenant operator verifies one finding before reassigning work or requesting an exception | Finding summary, current owner, current assignee, exception owner when applicable, and next workflow action | Raw evidence, historical context, and additional governance details | Secondary because it resolves one case after the list has already identified the work item | Preserves the single-record decision loop without creating a separate ownership page | Avoids cross-page reconstruction when ownership and exception context must be compared together |
UI/UX Surface Classification (mandatory when operator-facing surfaces are changed)
| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Tenant findings list and grouped bulk actions | List / Table / Bulk | Workflow queue / list-first resource | Open a finding or update responsibility | Finding | required | Structured More action group and grouped bulk actions |
Existing dangerous actions remain inside grouped actions with confirmation where already required | /admin/t/{tenant}/findings | /admin/t/{tenant}/findings/{finding} | Tenant context, filters, severity, due state, workflow state | Findings / Finding | Accountable owner, active assignee, and whether the finding is assigned, owned-but-unassigned, or orphaned | none |
| Finding detail and single-record action modals | Record / Detail / Actions | View-first operational detail | Confirm or update responsibility for one finding | Finding | N/A - detail surface | Structured header actions on the existing record page | Existing dangerous actions remain grouped and confirmed | /admin/t/{tenant}/findings | /admin/t/{tenant}/findings/{finding} | Tenant breadcrumb, lifecycle state, due state, governance context | Findings / Finding | Finding owner, finding assignee, and exception owner stay visibly distinct in the same context | none |
Operator Surface Contract (mandatory when operator-facing surfaces are changed)
| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|---|
| Tenant findings list and grouped bulk actions | Tenant operator or tenant manager | Route responsibility on open findings and separate accountable ownership from active work | Workflow queue | Who owns this outcome, who is doing the work, and what needs reassignment now? | Severity, lifecycle state, due state, owner, assignee, derived responsibility state, and personal-work cues | Raw evidence, run identifiers, historical audit details | lifecycle, severity, governance validity, responsibility state | TenantPilot only | Open finding, Assign, Triage, Start progress | Resolve, Close, Request exception |
| Finding detail and single-record action modals | Tenant operator or tenant manager | Verify and change responsibility for one finding without losing exception context | Detail/action surface | Is this finding owned by the right person, assigned to the right executor, and clearly separate from any exception owner? | Finding summary, owner, assignee, exception owner when present, due state, lifecycle state | Raw evidence payloads, extended audit history, related run metadata | lifecycle, governance validity, responsibility state | TenantPilot only | Assign, Start progress, Resolve | Close, Request exception |
Proportionality Review (mandatory when structural complexity is introduced)
- New source of truth?: no
- New persisted entity/table/artifact?: no
- New abstraction?: no
- New enum/state/reason family?: no persisted family; only derived responsibility-state rules on existing fields
- New cross-domain UI framework/taxonomy?: no
- Current operator problem: Operators cannot reliably distinguish accountability from active execution, which creates avoidable misrouting and weak audit interpretation.
- Existing structure is insufficient because: Two existing user fields and an exception-owner concept can already appear in the same workflow, but current copy does not establish a stable product contract across list, detail, and action surfaces.
- Narrowest correct implementation: Reuse the existing finding owner and assignee fields, define their meaning, and apply that meaning consistently to labels, filters, derived states, and audit copy.
- Ownership cost: Focused UI copy review, acceptance tests for role combinations, and ongoing discipline to keep future findings work aligned with the same contract.
- Alternative intentionally rejected: A broader ownership framework with separate role types, queues, or team routing was rejected because it adds durable complexity before the product has proven the simpler owner-versus-assignee contract.
- Release truth: Current-release truth. This spec clarifies the meaning of responsibility on live findings now and becomes the contract for later execution surfaces.
Testing / Lane / Runtime Impact (mandatory for runtime behavior changes)
- Test purpose / classification: Feature
- Validation lane(s): fast-feedback, confidence
- Test governance outcome: document-in-feature
- Why this classification and these lanes are sufficient: The change is visible through tenant findings UI behavior, responsibility-state rendering, and audit-facing wording. Focused feature coverage proves the contract without introducing browser or heavy-governance breadth.
- New or expanded test families: Expand tenant findings resource coverage to include responsibility rendering, personal-work cues, and mixed-context exception-owner wording. Add focused assignment-audit coverage for owner-only, assignee-only, and combined changes.
- Fixture / helper cost impact: Low. Existing tenant, membership, user, and finding factories are sufficient; tests only need explicit responsibility combinations and tenant-scoped memberships.
- Heavy-family visibility / justification: none
- Special surface test profile: standard-native-filament
- Standard-native relief or required special coverage: Ordinary feature coverage is sufficient, with explicit assertions for owner versus assignee visibility and authorization behavior.
- Reviewer handoff: Reviewers should confirm that one positive and one negative authorization case exist, that list and detail surfaces use the same vocabulary, and that audit-facing feedback distinguishes owner changes from assignee changes.
- Budget / baseline / trend impact: none
- Escalation needed: none
- Active feature PR close-out entry: Guardrail
- Planned validation commands:
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
User Scenarios & Testing (mandatory)
User Story 1 - Route accountable ownership clearly (Priority: P1)
As a tenant operator, I want to tell immediately who owns a finding and who is actively assigned to it, so I can route remediation work without guessing which responsibility changed.
Why this priority: This is the smallest slice that fixes the core trust gap. Without it, every later execution surface inherits ambiguous meaning.
Independent Test: Can be tested by loading the tenant findings list and detail pages with findings in owner-only, owner-plus-assignee, and no-owner states, and verifying that the first decision is possible without opening unrelated screens.
Acceptance Scenarios:
- Given an open finding with an owner and no assignee, When an operator views the findings list, Then the record is shown as owned but unassigned rather than fully assigned.
- Given an open finding with both an owner and an assignee, When an operator opens the finding detail, Then the accountable owner and active assignee are shown as separate roles.
- Given an open finding with no owner, When an operator views the finding from the list or detail page, Then the record is surfaced with the derived state
orphaned accountability.
User Story 2 - Reassign work without losing accountability (Priority: P2)
As a tenant manager, I want to change the assignee, the owner, or both in one responsibility update, so I can transfer execution work without silently transferring accountability.
Why this priority: Reassignment is the first mutation where the ambiguity causes operational mistakes and misleading audit history.
Independent Test: Can be tested by performing responsibility updates on open findings and asserting separate outcomes for owner-only, assignee-only, combined, clear-owner, and clear-assignee changes.
Acceptance Scenarios:
- Given an open finding with an existing owner and assignee, When an authorized operator changes only the assignee, Then the owner remains unchanged and the resulting feedback states that only the assignee changed.
- Given an open finding with an existing owner and assignee, When an authorized operator changes only the owner, Then the assignee remains unchanged and the resulting feedback states that only the owner changed.
- Given an open finding with an existing owner and assignee, When an authorized operator changes both roles in one responsibility update, Then the resulting feedback states that both the owner and assignee changed.
- Given an open finding with an existing owner and assignee, When an authorized operator clears only the assignee, Then the owner remains unchanged and the finding returns to
owned but unassigned. - Given an open finding with an existing owner and assignee, When an authorized operator clears only the owner, Then the assignee remains unchanged and the finding returns to
orphaned accountability.
User Story 3 - Keep exception ownership separate (Priority: P3)
As a governance reviewer, I want exception ownership to remain visibly separate from finding ownership when both appear in one flow, so risk-acceptance work does not overwrite the meaning of the finding owner.
Why this priority: This is a narrower but important boundary that prevents the accepted-risk workflow from reintroducing the same ambiguity under a second owner label.
Independent Test: Can be tested by opening a finding that has, or is about to create, an exception record and verifying that finding owner and exception owner remain distinct in the same operator context.
Acceptance Scenarios:
- Given a finding detail view that shows both finding responsibility and exception context, When the operator reviews ownership information, Then exception ownership is labeled separately from finding owner.
- Given an operator starts an exception request from a finding, When the request asks for exception ownership, Then the form language makes clear that the selected person owns the exception artifact, not the finding itself.
Edge Cases
- A finding may have the same user as both owner and assignee; the UI must show both roles as satisfied without implying duplication or error.
- A finding with an assignee but no owner is still an orphaned accountability case, not a healthy assigned state.
- Historical or imported findings may have both responsibility fields empty; they must render as orphaned without blocking rollout on a data backfill.
- When personal-work cues are shown, assignee-based work and owner-based accountability must not collapse into one ambiguous shortcut.
Requirements (mandatory)
Constitution alignment (required): This feature changes tenant-scoped finding workflow semantics only. It introduces no Microsoft Graph calls, no scheduled or long-running work, and no new OperationRun. Responsibility mutations remain audited tenant-local writes.
Constitution alignment (RBAC-UX): The affected authorization plane is tenant-context only. Cross-tenant and non-member access remain deny-as-not-found. Members without the findings assign permission continue to be blocked from responsibility mutations. The feature reuses the canonical capability registry and does not introduce raw role checks or a new capability split.
Constitution alignment (UI-FIL-001 / Filament Action Surfaces / UX-001): The feature reuses existing native Filament tables, infolist entries, filters, selects, modal actions, grouped actions, and notifications. No local badge taxonomy or custom action chrome is added. The action-surface contract remains satisfied: the finding is still the one and only primary inspect/open model, row click remains the canonical inspect affordance on the list, no redundant view action is introduced, and existing dangerous actions remain grouped and confirmed where already required.
Constitution alignment (UI-NAMING-001 / UI-SEM-001 / TEST-TRUTH-001): Direct field labels alone are insufficient because finding owner, finding assignee, and exception owner can appear in the same operator flow. This feature resolves that ambiguity by tightening the product vocabulary on the existing domain truth instead of adding a new semantic layer or presenter family. Tests prove visible business consequences rather than thin indirection.
Functional Requirements
- FR-001: The system MUST treat finding owner as the accountable user responsible for ensuring that a finding reaches a governed terminal outcome.
- FR-002: The system MUST treat finding assignee as the user currently expected to perform or coordinate active remediation work, and any operator-facing use of
assignedlanguage MUST refer to this role only. - FR-003: Authorized users MUST be able to set, change, or clear finding owner and finding assignee independently on open findings while continuing to restrict selectable people to members of the active tenant.
- FR-004: The system MUST derive responsibility state from the existing owner and assignee fields without adding a persisted lifecycle field. At minimum it MUST distinguish
owned but unassigned,assigned, andorphaned accountability. - FR-005: Findings list, finding detail, and responsibility-update flows MUST label owner and assignee distinctly, and any mixed context that also references exception ownership MUST label that role as exception owner.
- FR-006: When personal-work shortcuts or filters are shown on the tenant findings list, the system MUST expose separate, explicitly named cues for assignee-based work and owner-based accountability.
- FR-007: Responsibility-update feedback and audit-facing wording MUST state whether a mutation changed the owner, the assignee, or both.
- FR-008: Historical or imported findings with null or partial responsibility data MUST render using the derived responsibility contract without requiring a blocking data migration before rollout.
- FR-009: Exception ownership MUST remain a separate governance concept. Creating, viewing, or editing an exception from a finding context MUST NOT silently replace the meaning of the finding owner.
State naming convention: Operator-facing copy uses orphaned accountability as the visible label. Internal derived-state and contract slugs use orphaned_accountability.
UI Action Matrix (mandatory when Filament is changed)
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Tenant findings list | /admin/t/{tenant}/findings |
None added by this feature | Full-row open into finding detail | Primary open affordance plus More action group with Triage, Start progress, Assign, Resolve, Close, and Request exception |
Grouped actions including Triage selected, Assign selected, Resolve selected, and existing close/exception-safe bulk actions if already present |
Existing findings empty state remains; no new CTA introduced | N/A | N/A | Yes for responsibility mutations and existing workflow mutations | Action Surface Contract satisfied. No empty groups or redundant view action introduced. |
| Finding detail | /admin/t/{tenant}/findings/{finding} |
Existing record-level workflow actions only | Entered from the list row click; no separate inspect action added | Structured record actions retain Assign, Start progress, Resolve, Close, and Request exception |
N/A | N/A | Existing view header actions only | N/A | Yes for responsibility mutations and existing workflow mutations | UI-FIL-001 satisfied through existing infolist and action modal primitives. No exemption needed. |
Key Entities (include if feature involves data)
- Finding responsibility: The visible responsibility contract attached to an open finding, consisting of accountable ownership, active execution assignment, and a derived responsibility state.
- Finding owner: The accountable person who owns the outcome of the finding and is expected to ensure it reaches a governed end state.
- Finding assignee: The person currently expected to perform or coordinate the remediation work on the finding.
- Exception owner: The accountable person for an exception artifact created from a finding; separate from finding owner whenever both appear in one operator context.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: In acceptance review, an authorized operator can identify owner, assignee, and responsibility state for any scripted open-finding example from the list or detail surface within 10 seconds.
- SC-002: 100% of covered responsibility combinations in automated acceptance tests, including same-person, owner-only, owner-plus-assignee, assignee-only, and both-empty cases, render the correct visible labels and derived state.
- SC-003: 100% of covered responsibility-update tests distinguish owner-only, assignee-only, clear-owner, clear-assignee, and combined changes in operator feedback and audit-facing wording.
- SC-004: When personal-work shortcuts are present, an operator can isolate assignee-based work and owner-based accountability from the findings list in one interaction each.
Assumptions
- Existing finding owner and assignee fields remain the single source of truth for responsibility in this slice.
- Open findings may legitimately begin without an assignee while still needing an accountable owner.
- Membership hygiene for users who later lose tenant access is a separate follow-up concern and not solved by this clarification slice.
Non-Goals
- Introduce team, queue, or workgroup ownership.
- Add automatic escalation, reassignment, or inactivity timers.
- Split authorization into separate owner-edit and assignee-edit capabilities.
- Require a mandatory historical backfill before the clarified semantics can ship.
Dependencies
- Spec 111, Findings Workflow + SLA, remains the lifecycle and assignment baseline that this spec clarifies.
- Spec 154, Finding Risk Acceptance, remains the exception governance baseline whose owner semantics must stay separate from finding owner.