Skip to content

feat(dashboard): v3 foundation + Findings view (light, clean-room rebuild)#123

Merged
thejefflarson merged 3 commits into
mainfrom
feat/dashboard-v3-build-phase1
Jun 29, 2026
Merged

feat(dashboard): v3 foundation + Findings view (light, clean-room rebuild)#123
thejefflarson merged 3 commits into
mainfrom
feat/dashboard-v3-build-phase1

Conversation

@thejefflarson

Copy link
Copy Markdown
Owner

Phase 1 of the v3 operator dashboard, implementing docs/dashboard-v3-design.md (the agreed IA, synthesis of nine clean-room passes) and docs/STYLEGUIDE.md (the light-theme token system). Clean-room rebuild from the current working tree — there was no prior dashboard to copy.

Architecture (ADR-0019)

engine/src/engine/dashboard/, the React-like split the repo conventions mandate:

  • view_model/ — shapes engine::/state:: domain state into plain Props. The only layer that touches domain types. The posture/recency honesty mapping (Uncertain/Awaiting never green) and the urgency sort live here, in one tested place.
  • components/ — pure maud Props -> Markup renderers that import no domain type (source-guard-tested). Status strip, finding row + expand-in-place "why" panel, evidence tables, the findings view with honest empty/awaiting/blind states, the 4-tab nav.
  • page.rs — composes the persistent status strip + the 4-tab nav shell + the /fragment live region into full pages / fragments.
  • mod.rs — axum routes (/, /fragment, /assets/dashboard.css, /assets/dashboard.js) + DashboardState, and re-adds serve_dashboard, wired into run_loop.rs behind PROTECTOR_DASHBOARD_ADDR. It reads the SAME Arc state handles the engine holds (findings, judgement ring, reversion log) — read-only, zero-egress, never a decision gate (ADR-0016).

Every file is well under the 1,000-line cap (largest 349).

Assets

engine/web/dist/dashboard.css is generated from the STYLEGUIDE light tokens and served same-origin via include_str! (no third-party CSS). dashboard.js is zero-dependency: <details> expansion persistence + /fragment polling that preserves scroll/expansion and re-pulls readiness (so a model that just went down flips the honest banner immediately).

The Findings view (built fully — brief §5)

Urgency sort, not severity (corroborated-live → model-promoted → escalations → awaiting → cleared tail). Each row: posture rail+chip (colour + glyph + word; Uncertain/Awaiting never green, dashed/dotted rails) · entry node-kind glyph (🌐 for an internet foothold) → objective · Δ (steady shows age, not an alarm) · evidence cluster (CVE count + KEV + runtime + secrets, or explicit "no evidence") · disposition · live/judged sub-tag. Expands in place to the verbatim verdict → the proven path as a text hop-list (structural hops muted, cut point marked) → the evidence tables (CVE / runtime corroborating-vs-context / exposed-secrets / misconfig / RBAC) → the proposed cut + its self-revert condition → a "show model prompt" disclosure to the raw judgement. Argo fan-out collapses to → ×N reachable.

The honesty guards (brief §9)

Stubbed for phase 2

The Trust, Readiness, and Activity tabs are labelled phase-2 placeholders so the nav exists and is honest about what is coming. The persistent status strip renders on every tab. The data for all four already lives in state::.

Gate results

  • cargo build
  • cargo fmt --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test ✓ (296 lib + 23 dashboard-specific, 4 dashboard guards, file_size_guard)
  • helm lint / helm template ✓ (dashboard ClusterIP service + container port + env added behind engine.dashboard.*)

Note

The remote feat/dashboard-v3-build had diverged (5 commits I did not author and could not safely integrate without a pull/merge/reset, which the task forbids), so this PR is pushed from feat/dashboard-v3-build-phase1 to avoid clobbering that work.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP

thejefflarson and others added 3 commits June 28, 2026 21:44
Recover the synthesized design brief onto the build branch, switch the visual register
to a LIGHT theme, and author docs/STYLEGUIDE.md as a light-theme token system
(tokens + component->token map + the test-enforced accessibility gate).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
…uild)

Build Phase 1 of the v3 operator dashboard from docs/dashboard-v3-design.md and
docs/STYLEGUIDE.md: the server-rendered presentation platform, the shared
components, the Findings view end-to-end, and the honesty-invariant guards.

Architecture (ADR-0019), engine/src/engine/dashboard/:
- view_model/ shapes state:: domain state into plain Props (the only layer that
  touches engine::/state::); posture/recency honesty mapping + the urgency sort
  live here in one tested place.
- components/ are pure maud Props->Markup renderers importing no domain type
  (guard-tested); status strip, finding row + expand-in-place "why" panel,
  evidence tables, findings view with honest empty/awaiting/blind states, nav.
- page.rs composes the persistent status strip + 4-tab nav shell + /fragment
  live region.
- mod.rs wires axum routes (/, /fragment, /assets/dashboard.css,
  /assets/dashboard.js) + DashboardState, and re-adds serve_dashboard, wired
  into run_loop behind PROTECTOR_DASHBOARD_ADDR (reads the same Arc state handles
  the engine holds; read-only, zero-egress). Every file < 1000 lines.

Assets generated from the STYLEGUIDE light tokens: web/dist/dashboard.css
(served same-origin via include_str!, no third-party CSS) and dashboard.js
(zero-dep <details> expansion persistence + /fragment polling preserving
scroll/expansion).

Findings view (brief §5): urgency sort (not severity); posture rail+chip with
Uncertain/Awaiting never green; entry/objective/Δ/evidence-cluster/disposition/
live-vs-judged; expand to verbatim verdict -> text hop-list with cut point ->
evidence tables -> cut + self-revert -> show-model-prompt; argo fan-out collapse;
honest empty/awaiting/blind states. Trust/Readiness/Activity stubbed as phase-2.

Guard tests (brief §9): honest-calm (#1), uncertain/awaiting-not-green (#2),
no-blank-evidence (#3), component-no-domain-imports (#4), no-inline-style (#5),
untrusted-text-escaped (#6) — render + source guards; file-size cap (#7) already
guarded.

Helm: dashboard ClusterIP service + container port + PROTECTOR_DASHBOARD_ADDR /
PROTECTOR_CLUSTER_LABEL env behind engine.dashboard.* values.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
Refinement A — green/calm only when the model is SURE nothing is happening.
The overall green/all-clear now renders ONLY when the model has affirmatively
cleared everything it is looking at: judging AND covered (no degraded feed)
AND zero breaches AND zero awaiting AND zero uncertain. If the model is up but
hasn't finished (any entry still awaiting/uncertain, or a feed degraded), the
strip + empty state render a new elevated, NON-green "watching" reading — quiet
because the model hasn't finished, visibly distinct from quiet because it
affirmatively cleared. StatusStripProps gains uncertain_count and the
all_clear()/watching()/fully_covered()/model_is_up() gates (replacing
calm_is_honest). Guard tests extended: green/all-clear is forbidden when any
entry is awaiting or uncertain, not just when !model_judging.

Refinement B — pre-judgements (Awaiting) read slightly elevated.
--posture-awaiting moves from slate #667085 to a muted ochre #9A6B2E (clearly
below Uncertain's stronger amber-brown #B54708), with a new
--posture-awaiting-tint #FBF6EC backing the awaiting chip and tinting the
un-judged row (data-posture="awaiting"). The awaiting glyph (◌) and word
("awaiting judgement") stay — meaning never by colour alone. The strip headline
gains an uncertain count and keeps awaiting in the elevated ochre tone.

Styleguide is the source of truth; CSS, view_model, components, and tests match
it. All gates green: build, fmt, clippy -D warnings, full test suite (incl.
dashboard guards + file_size_guard), helm lint/template. No file > 1000 lines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
@thejefflarson
thejefflarson merged commit 375b56e into main Jun 29, 2026
5 checks passed
@thejefflarson
thejefflarson deleted the feat/dashboard-v3-build-phase1 branch June 29, 2026 05:23
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