← Design · c121

plan/04-findings.md

04 · Findings listing (S2) + finding detail (S1) (Wave 2)

TL;DR: "Not enforced" becomes Observed; Observed is hidden by default with a one-click way in and an honest zero state (D3); add severity; consolidate the filters into one bar; the snoozed line goes. Resolve records a decision — reason + text, no inline exception, a follow-on link (S, D10). History marks manual vs re-evaluation; findings show their Policy Config version at detection.

Owns: app/pages/pages/phase4/mock/{findings,finding-detail}.vue, app/components/governance/part/{FindingsTable,FindingHistory,FindingDetails}.vue, app/components/governance/mock/ResolveCapture.vue.

Today

  • findings.vue filters: status Open/Resolved ?status= (l.13–18), framework chips ?fw= (l.20–25), policy/team chips (l.26–27), column search ?q=, group/individual ?view=, enforcement FilterTriggerBar All · Enforced · "Not enforced (N)" ?ne= (l.34–40), clear; variant ?v.observe=tagged|hidden|preview (l.220–233); snoozed amber lines (l.45–50, l.304). No severity filter (sorter only, l.69).
  • FindingsTable.vue mode tags incl. "snoozed" (l.24, l.135–139), group mode line (l.88), "policy v{n}" (l.89), resolve card (l.72–82), lifecycle (l.91–100).
  • ResolveCapture.vue: reasons (fixture RESOLVE_REASONS), text required for Other; optional inline Off exception with until (l.19–33); done message (l.106–114).
  • finding-detail.vue: Resolve + "Too noisy? Quiet the policy →" (l.23–24); ?v.resolve=reasons|exception (l.94–98).
  • FindingHistory.vue: actor = person vs qdash, no explicit kind.

Steps

  1. One filter bar (L: "too many filters, consolidate"). Assemble from the layer's DataListingBar (c95: FilterTriggerBar + column picker) if it fits: Status (Open · Resolved) · Outcome (Enforced · Observed · All, default Enforced; ?outcome=) · Severity (multi; ?sev=) · Framework (?fw=). Policy and team stay as removable chips from deep links only. Group/Individual moves to the bar's right edge. Keep ?q= in the column header. Drop ?ne= and the ?v.observe= variants (map old ?ne=show|only to outcome=all|observed for old links).
  2. Observed hidden by default (D3). Bar shows "Observed (N)" count even when excluded. Zero state when the filtered result is empty: "No enforced findings match. 12 Observed findings are hidden — show them" (use the layer's FilterNoMatches / StateEmpty pattern). Framework zero state "Nothing open against ISO 27001" reads as good news (R: audit-ready view).
  3. Rename. Every "Not enforced" → "Observed"; "Enforced" only when actually blocked (labels come from 00's rows file). Remove "snoozed" tags and the snoozed lines; group mode line shows "Observe · until Nov 15" instead.
  4. Resolve (S, I). ResolveCapture: reasons False positive · By design · Security testing · Other + explanation; remove the inline exception block and ?v.resolve=exception. After resolving: "Resolved as By design. The policy didn't change." + link "Change this policy or add an exception →" to S3 ?policy=<id>&add=user:<subject>#exceptions (D10). Replace "Too noisy? Quiet the policy →" with the same link pattern pointing at #mode.
  5. History. A small kind marker per event: Manual (person) vs Re-evaluation (system, with the Policy Config version that caused it, linking to Changes) vs System (Until passed). Same in the S2 card lifecycle.
  6. Config version. "Policy Config v12 at detection" in FindingDetails and the S2 card (replaces "policy v{n}").

Done when

/pages/phase4/mock/findings default (enforced only), ?outcome=all, ?sev=critical, framework zero state, a finding resolved with each reason, history with all three kinds. Screenshots shots/04-*.png.

Look for (review)

Is the bar fewer things than today? Can you tell in one glance that Observed findings exist but are hidden?

Qpoint Brand Style Guide