Skip to content

fix(dashboard): findings-view polish — selection-safe poll, unique row ids, active-tab highlight, header/nav realign, collapse pod replicas#132

Merged
thejefflarson merged 1 commit into
mainfrom
fix/findings-view-polish
Jun 30, 2026
Merged

fix(dashboard): findings-view polish — selection-safe poll, unique row ids, active-tab highlight, header/nav realign, collapse pod replicas#132
thejefflarson merged 1 commit into
mainfrom
fix/findings-view-polish

Conversation

@thejefflarson

Copy link
Copy Markdown
Owner

Five Findings-view fixes (scope: Findings view only — finding_row.rs, nav.rs, findings.rs map/collapse/id, dashboard.css, dashboard.js, the preview + tests; behavior/evidence rendering and observe/graph/reason untouched).

1. Copy-paste / scroll-jump on the live poll (bug)

dashboard.js did live.innerHTML = html every 5s, destroying any in-progress text selection. The poll now checks window.getSelection() and defers the swap while a non-collapsed selection is anchored inside #live, swapping on the next tick once it clears. Scroll-restore preserved; zero-dependency, same-origin.

2. Rows open the wrong row (bug) — root cause

finding_id() slugified every non-alphanumeric to -, so distinct entry keys (e.g. secret/app/db and secret-app-db, or endpoint/a and endpoint-a) collapsed to the same id — duplicate data-finding / id / aria-controls="detail-<id>". expanded.has(id) and getElementById/.row.open + .row-detail then matched multiple rows, opening the wrong detail. Fixed by appending a short, stable FNV-1a hash of the full entry key (deterministic across renders so the JS open-state keying survives a poll), making every row + detail id unique. Test: two distinct entries that previously collided now get distinct ids.

3. Highlight the active tab

.tab-active gains a raised --surface-raised fill + a stronger 3px accent rail, keeping bold weight + the accent — clearly highlighted, meaning not by colour alone, tokens only (no raw hex).

4. Realign header + nav to the table's + (reverses the gutter decision)

New shared --brand-indent token = --page-gutter + --space-2 (.cell-expand pad) + --space-1 (.expander pad), reproducing the x of the first-column + glyph. Applied to .strip and .tabs left padding so the brand 'p', the active tab's first letter, and the + form one vertical line. Verified in the dev preview.

5. Collapse pod replicas into one workload row (feature)

New collapse_pod_replicas step in map_findings (mirrors collapse_fanout). Derives a workload-group key from a workload/<ns>/Pod/<name> entry by stripping the replica suffix:

  • StatefulSet name-<ordinal> (trailing -<digits>)
  • Deployment name-<rs-hash>-<pod-hash> (two trailing hash segments)
  • DaemonSet / ReplicaSet name-<pod-hash> (one trailing 5-char hash)

Collapse heuristic (conservative): a hash segment must mix at least one letter and one digit, so a dictionary-word tail like debug-shell is treated as part of the name and left un-merged; and only groups with 2+ pods collapse. So unrelated pods and standalone pods are never merged. The representative row carries the worst/most-urgent posture (urgency_rank) and a ×N replica count, relabeled to the workload (analytics/murmurify-aggregator). Replicas run the same image, so a merged posture is sound. Tests: 3 StatefulSet replicas → one ×3 breach row; Deployment replicas collapse; unrelated + standalone + single-replica never merge.

Checks (from engine/)

  • cargo fmt — clean
  • cargo build — ok
  • cargo build --example dashboard_preview — ok
  • cargo clippy --all-targets — 0 warnings
  • cargo test — 355 lib + 9 dashboard_guards + 1 file-size + 1 bin, all green
  • Visual: dev preview at ?scenario=breach shows the single analytics/murmurify-aggregator ×3 row, no duplicate data-finding ids, brand/tab/+ aligned, raised active tab.

Closes JEF-... (findings-view polish)

🤖 Generated with Claude Code

…w ids, active-tab highlight, header/nav realign, collapse pod replicas

Five Findings-view fixes:

1. Selection-safe live poll. dashboard.js now skips the /fragment innerHTML
   swap while the operator has an active, non-collapsed text selection anchored
   inside #live (window.getSelection), deferring to the next tick so a poll
   never rips away the model judgement mid-drag. Scroll-restore is kept;
   zero-dependency, same-origin.

2. Rows open the wrong row (root cause). finding_id() slugified non-alphanumerics
   to '-', so distinct entry keys (e.g. secret/app/db and secret-app-db) mapped to
   the SAME id — duplicate data-finding / id / aria-controls="detail-<id>", so
   expanded.has(id) and getElementById matched multiple rows and the
   .row.open + .row-detail reveal targeted the wrong node. Fixed by appending a
   short, stable FNV-1a hash of the full entry key, so every row + its detail row
   are unique.

3. Active tab highlight. .tab-active gains a raised surface fill + a stronger
   (3px) accent rail, keeping the bold weight — meaning not by colour alone, tokens
   only.

4. Header/nav realign to the table's `+`. Reverses the shared-gutter decision:
   the brand text and the tab row now left-align with the table's first-column `+`
   expander glyph via a new --brand-indent token (page gutter + .cell-expand pad +
   .expander pad), applied to .strip and .tabs.

5. Collapse pod replicas into one workload row. New collapse_pod_replicas step in
   map_findings derives a workload-group key from a workload/<ns>/Pod/<name> entry
   by stripping the replica suffix — StatefulSet name-<ordinal>, Deployment
   name-<rs-hash>-<pod-hash>, DaemonSet/ReplicaSet name-<pod-hash>. Conservative:
   hash segments must mix a letter and a digit (a dictionary-word tail like
   debug-shell is left un-merged), and only groups with 2+ pods collapse — so
   unrelated and standalone pods are never merged. The representative row carries
   the worst/most-urgent posture (urgency_rank) and a ×N replica count.

Tests: findings view_model tests (id collision-free + stable; StatefulSet/Deployment
collapse; unrelated + standalone + single-replica never merge; workload_group_key
units) and render-level tests (unique detail ids, ×N workload row). dashboard_guards
extended for the selection-deferring poll, the raised active tab, and the
brand-indent alignment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
@thejefflarson thejefflarson merged commit 77b4694 into main Jun 30, 2026
2 of 3 checks passed
@thejefflarson thejefflarson deleted the fix/findings-view-polish branch June 30, 2026 03:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant