9.6 KiB
Data Model: Finding Outcome Taxonomy & Verification Semantics
Overview
This feature does not add a new table or a new top-level persisted entity. It reuses the current findings row as the source of truth for current terminal meaning, keeps reopen rationale in audit metadata, and derives verification or reporting buckets through one findings-local semantics helper.
Entity: Finding
Persistence: existing findings table
Owner: tenant-owned record
Primary responsibility: current workflow status, current terminal-outcome key, timestamps, and tenant-scoped operational ownership
Relevant persisted fields
| Field | Type | Source | Notes |
|---|---|---|---|
id |
integer | existing | Primary key |
workspace_id |
integer | existing | Workspace isolation boundary |
tenant_id |
integer | existing | Tenant isolation boundary |
status |
string | existing | Primary lifecycle status; remains one of new, acknowledged, triaged, in_progress, reopened, resolved, closed, risk_accepted |
severity |
string | existing | Existing priority and SLA signal |
resolved_reason |
string nullable | existing | Becomes a bounded canonical resolve key instead of free-form prose |
closed_reason |
string nullable | existing | Becomes a bounded canonical close key and remains the stored reason for risk_accepted |
resolved_at |
datetime nullable | existing | Marks the current resolved terminal timestamp |
closed_at |
datetime nullable | existing | Marks the current closed or risk-accepted terminal timestamp |
reopened_at |
datetime nullable | existing | Marks the current reopen timestamp |
closed_by_user_id |
integer nullable | existing | Current actor for close and risk-accept paths |
owner_user_id |
integer nullable | existing | Accountability owner |
assignee_user_id |
integer nullable | existing | Active assignee |
evidence_jsonb |
jsonb | existing | Current supporting evidence; unchanged in this slice |
Relationships
| Relationship | Type | Notes |
|---|---|---|
tenant() |
belongsTo | Tenant scope owner |
ownerUser() |
belongsTo | Accountability owner |
assigneeUser() |
belongsTo | Active assignee |
closedByUser() |
belongsTo | Actor for close/risk accept |
findingException() |
hasOne | Existing risk-governance truth from Spec 154 |
Validation rules introduced by this feature
| Rule | Description |
|---|---|
status |
No new status is introduced; all existing transition rules stay in force |
resolved_reason |
Required for resolve and system-clear transitions; must be a canonical key from the resolve-reason family |
closed_reason |
Required for close and risk-accept transitions; must be a canonical key from the close-reason family or the risk-accept key |
reopen_reason |
Required for reopen transitions; remains audit metadata and must be a canonical key from the reopen-reason family |
Canonical reason families
Resolve reason keys
| Key | Meaning | Derived verification state |
|---|---|---|
remediated |
Operator declares that remediation work was completed | pending_verification |
no_longer_drifting |
Trusted compare no longer reproduces prior drift | verified_cleared |
permission_granted |
Trusted permission evidence no longer reproduces the finding condition | verified_cleared |
permission_removed_from_registry |
Trusted permission evidence confirms the triggering permission was removed | verified_cleared |
role_assignment_removed |
Trusted role evidence confirms the triggering assignment was removed | verified_cleared |
ga_count_within_threshold |
Trusted role evidence confirms the triggering count is now safe | verified_cleared |
Close reason keys
| Key | Meaning | Outcome family |
|---|---|---|
false_positive |
The finding should not have been actionable | administrative_closure |
duplicate |
The finding duplicates another case | administrative_closure |
no_longer_applicable |
The finding no longer applies to the tenant context | administrative_closure |
accepted_risk |
Existing accepted-risk path only; still governed by exception validity | accepted_risk |
Reopen reason keys
| Key | Meaning | Persistence |
|---|---|---|
recurred_after_resolution |
A previously addressed condition reappeared | audit metadata only |
verification_failed |
Trusted evidence contradicted the earlier resolved outcome | audit metadata only |
manual_reassessment |
An operator reopened the finding after review | audit metadata only |
Derived facets from the current row
| Derived facet | Source | Meaning |
|---|---|---|
verification_state |
status + resolved_reason |
pending_verification, verified_cleared, or not_applicable |
terminal_outcome_key |
status + canonical reason key |
Stable UI/reporting key such as resolved_pending_verification, verified_cleared, closed_false_positive, closed_duplicate, closed_no_longer_applicable, or risk_accepted |
report_bucket |
terminal_outcome_key + governance validity |
Report-friendly aggregation bucket |
outcome_label |
terminal_outcome_key |
Canonical operator wording |
State transitions
| From | Action | Stored result | Derived outcome |
|---|---|---|---|
new, triaged, in_progress, reopened, acknowledged |
resolve(remediated) |
status=resolved, resolved_reason=remediated |
Resolved pending verification |
| open statuses | close(false_positive) |
status=closed, closed_reason=false_positive |
Closed as false positive |
| open statuses | close(duplicate) |
status=closed, closed_reason=duplicate |
Closed as duplicate |
| open statuses | close(no_longer_applicable) |
status=closed, closed_reason=no_longer_applicable |
Closed as no longer applicable |
| open statuses | riskAccept(accepted_risk) |
status=risk_accepted, closed_reason=accepted_risk |
Risk accepted with separate governance validity |
resolved with resolved_reason=remediated |
trusted system clear | status=resolved, resolved_reason=<system clear key> |
Verified cleared |
| open statuses | direct trusted system clear | status=resolved, resolved_reason=<system clear key> |
Verified cleared |
resolved, closed, risk_accepted |
reopen(<canonical reopen key>) |
status=reopened, terminal reason fields cleared, reopen reason recorded in audit metadata |
open finding again |
Entity: FindingException
Persistence: existing finding_exceptions table
Owner: tenant-owned record
Primary responsibility: governance validity for risk_accepted
Relevant fields consumed by this feature
| Field | Type | Notes |
|---|---|---|
status |
string | Existing workflow for exception requests and approvals |
current_validity_state |
string | Current validity used by FindingRiskGovernanceResolver |
effective_from |
datetime nullable | Existing validity window |
expires_at |
datetime nullable | Existing validity window |
review_due_at |
datetime nullable | Existing governance follow-up signal |
Rule in this feature
FindingExceptioncontinues to determine whetherrisk_acceptedis governed safely.FindingExceptiondoes not contribute toverified_clearedand does not change remediation buckets.
Derived read model: FindingOutcomeSemantics
Persistence: not persisted
Owner: findings-local support helper
Primary responsibility: unify list, detail, filter, and reporting semantics from current finding truth
Inputs
| Input | Source |
|---|---|
status |
Finding row |
resolved_reason |
Finding row |
closed_reason |
Finding row |
findingException validity |
FindingException relationship via existing resolver |
system_origin and prior workflow steps |
audit metadata, only when reconstructing history or detailed provenance |
Outputs
| Output | Type | Notes |
|---|---|---|
terminalOutcomeKey |
string | Stable internal key |
label |
string | Canonical operator-facing wording |
verificationState |
string | pending_verification, verified_cleared, not_applicable |
reportBucket |
string | Aggregation bucket for reviews and exports |
historicalContext |
string nullable | Reuses current resolver-style explanatory text where appropriate |
Report bucket mapping
| Terminal outcome key | Report bucket |
|---|---|
resolved_pending_verification |
remediation_pending_verification |
verified_cleared |
remediation_verified |
closed_false_positive |
administrative_closure |
closed_duplicate |
administrative_closure |
closed_no_longer_applicable |
administrative_closure |
risk_accepted |
accepted_risk |
Audit metadata additions or constraints
| Key | Existing/New | Purpose |
|---|---|---|
resolved_reason |
existing | Canonical current resolve key |
closed_reason |
existing | Canonical current close or risk-accept key |
reopened_reason |
existing | Canonical reopen key for reviewability |
system_origin |
existing | Provenance flag for system transitions |
resolved_at, closed_at, reopened_at |
existing | Timeline reconstruction |
Audit rule
- Audit metadata remains the place to reconstruct path history, such as whether a verified-clear outcome happened directly through automation or after a prior manual
remediatedtransition. - The current row remains the source of truth for current filters and summaries.