feat(dashboard): v3 foundation + Findings view (light, clean-room rebuild)#123
Merged
Conversation
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 1 of the v3 operator dashboard, implementing
docs/dashboard-v3-design.md(the agreed IA, synthesis of nine clean-room passes) anddocs/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/— shapesengine::/state::domain state into plainProps. 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/— puremaudProps -> Markuprenderers 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/fragmentlive region into full pages / fragments.mod.rs— axum routes (/,/fragment,/assets/dashboard.css,/assets/dashboard.js) +DashboardState, and re-addsserve_dashboard, wired intorun_loop.rsbehindPROTECTOR_DASHBOARD_ADDR. It reads the SAMEArcstate 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.cssis generated from the STYLEGUIDE light tokens and served same-origin viainclude_str!(no third-party CSS).dashboard.jsis zero-dependency:<details>expansion persistence +/fragmentpolling 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)
Uncertainand awaiting (None) never map to the cleared/green token (view_model + render tests).engine::/state::domain type (source guard,engine/tests/dashboard_guards.rs).<style>/style=(source guard).<script>payload).file_size_guard).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 behindengine.dashboard.*)Note
The remote
feat/dashboard-v3-buildhad diverged (5 commits I did not author and could not safely integrate without apull/merge/reset, which the task forbids), so this PR is pushed fromfeat/dashboard-v3-build-phase1to avoid clobbering that work.🤖 Generated with Claude Code
https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP