Why Dashboard looks the way it does, and the contract the data-fill agent must honour. Pair this doc with Vault Style Guide.

Audience and goal

Solo technical operator. Opens Obsidian to (a) remember architecture and (b) triage bugs. The dashboard must answer “where is the system at?” in one screen. Dense, scannable, no marketing language, no emoji.

Grid system

Obsidian’s Reading view does not honour CSS grid. It renders block-level Markdown top-to-bottom and only one construct gives a true horizontal row without raw HTML: a Markdown table. Callouts placed on consecutive lines stack vertically, even when separated by blank lines.

Consequences for this dashboard:

  • The KPI strip is a 5-column Markdown table, not five callouts. A table is the only no-HTML way to get five values on one row at varying viewport widths.
  • The Coverage Health “2x2” is implemented as four sequential > [!chart] callouts. Real 2x2 is impossible without HTML, and embedding fenced code in table cells fails. Stacking is the honest fallback. The Layout Spec calls this out so the data agent does not try to “fix” it back into a table.
  • Quick Links uses a 3-column Markdown table with stacked rows of links. Tables render link wikilinks correctly in Reading view.

Eye flow

Top to bottom, the dashboard mirrors operator priority:

  1. KPI strip — am I in trouble right now? (counts)
  2. Pipeline — is the system shape clear in my head? (mermaid + foldable embeds)
  3. Coverage health — what is degrading? (charts)
  4. Recent — what changed? (dataview + commit list)
  5. Bases — let me query (ADRs / bugs / tags)
  6. Architecture — let me zoom out (excalidraw + C4 mermaid)
  7. Compliance health — am I about to break the law?
  8. Quick links — three task-oriented entry points

KPI and Pipeline are above the fold. Compliance is intentionally late: it is a gate, not a starting point — once eyes have parsed system state, the compliance callout’s color (success/warning/failure) registers as a closing verdict.

Callout palette

One callout type per semantic role. Don’t reuse types across roles.

CalloutRole on this dashboard
infoNeutral metadata (the KPI strip helper, anything informational)
example-Foldable per-queue detail (Pipeline section)
chartReserved chart panel — wraps a Charts plugin block (Coverage Health)
abstractRecent activity feed (the dataview block)
noteBases query panels
todoAction the operator must take (e.g. create the Excalidraw canvas)
warningCompliance snapshot — default state, agent may swap to success or failure
quoteFooter / attribution / “last updated”

warning is the default compliance pick because a dashboard that opens “all green” trains operator complacency. The data agent must downgrade to success only when all four compliance metrics are within budget.

Placeholder convention

Two markers, no others:

  • {{PLACEHOLDER_NAME}} — inline scalar (number, date, short string). The data agent does a single-pass replace.
  • <!-- DATA-AGENT: ... --> — instruction inside a fenced block (mermaid, chart, base, dataview). The agent reads the comment, replaces the body of the fenced block, and removes the comment.

Placeholders MUST be unique. The data agent runs rg '\{\{[A-Z_]+\}\}' Wiki/Dashboard.md to enumerate them.

Placeholders left in Dashboard.md

PlaceholderTypeExpected fill
{{NOTE_COUNT}}integertotal .md files in Wiki/
{{ADR_COUNT}}integercount of notes tagged adr
{{BUG_COUNT}}integercount of notes tagged bug-verified (or callouts of type bug)
{{ORPHAN_COUNT}}integernotes with zero inbound links per Obsidian graph
{{LAST_UPDATED}}datemost recent updated: frontmatter across Wiki/
{{TODAY_COMMITS_LIST}}inline listgit log --since=midnight --pretty='%h %s' from the canonical repo, joined with ” · “
{{OPT_OUT_HASH_COUNT}}integerSELECT count(*) FROM optout_hashes
{{ROPA_TODAY}}integerSELECT count(*) FROM ropa_log WHERE created_at::date = current_date
{{ART14_PENDING}}integerArticle 14 notifications queued but not sent
{{REKLAMSPARR_7D}}integerreklamspärr block hits in the last 7 days

Fenced-block TODOs left in Dashboard.md

SectionBlock typeExpected fill
Pipeline at a glance```mermaidflowchart LR showing four sources (BV, SCB, Crawlee, Maps) → Scrape_JobEnrich_Job (with reklamspärr + opt-out gates) → Update_Job
Coverage health (1)```chartbar, x = wiki category, y = note count
Coverage health (2)```chartdoughnut, covered vs untested src modules
Coverage health (3)```chartbar, open verified bugs by area
Coverage health (4)```chartline, notes per ISO week
Recent```dataviewTABLE, last 10 notes by updated, columns updated + tags
Bases — ADRs```basetable, filter tag: adr, columns file/status/date/title
Bases — Verified bugs```basetable, filter tag: bug-verified, columns file/severity/area/first-seen
Bases — By tag```basetable grouped by tag
Architecture```mermaidC4-style container diagram of the full system

Embeds left in Dashboard.md

EmbedNotes
![[Pipeline\#Scrape_Job]]Requires [[Pipeline]] to have a ## Scrape_Job heading
![[Pipeline\#Enrich_Job]]Requires ## Enrich_Job heading
![[Pipeline\#Update_Job]]Requires ## Update_Job heading
![[Architecture Canvas.excalidraw]]Canvas not yet created; > [!todo] next to it explains how

Plugin requirements

PluginStatusUsed byDegrades to
BasesBuilt-in (Obsidian 1.9+)Section “Bases”Switch ```base to ```dataview if the syntax is wrong; same intent
DataviewCommunityRecent (and Bases fallback)Falls back to a static markdown list with a > [!todo] reminder to fill manually
ChartsCommunity (obsidian-charts)Coverage healthA > [!todo] callout listing the four data points the chart would have shown
ExcalidrawCommunityArchitectureEmbed renders as a broken link until the canvas exists; the > [!todo] next to it documents creation
MermaidBuilt-inPipeline + Architecture C4n/a — always available

Reading-view rendering notes

  • Callout nesting: Obsidian renders nested callouts up to 3 levels reliably. The Recent section nests a dataview block inside an abstract callout — that is one level of nesting, safe.
  • Foldable callouts: > [!example]- (trailing -) renders folded by default. Used in the Pipeline section so the page is short on first paint and the operator opts into per-queue detail.
  • Table width budget: at the default editor width (~720 px) a 5-column table with bold numbers wraps if any cell exceeds ~12 chars. The KPI strip uses short labels for that reason (“Verified bugs” is the longest at 13 chars, acceptable).
  • Mermaid theme: respects Obsidian’s active theme. No theme directive — let it follow light/dark mode.
  • Charts plugin: requires type: bar | line | doughnut | radar | pie | polar. The reserved blocks use the four kinds the panels need. The agent must not change the wrapping > [!chart] callout — it is the visual frame.
  • Bases: the spec is YAML inside a ```base fence. If a future Obsidian version changes the syntax, swap to dataview and tag the spec note for revisit.
  • Embeds: ![[Note#heading|Heading]] only renders the section content if the heading exists exactly. If [[Pipeline]] does not yet expose ## Scrape_Job, ## Enrich_Job, ## Update_Job headings, the embed renders blank — coordinate with whoever owns [[Pipeline]].
  • Dashboard is intentionally under 250 lines. Density beats completeness — anything that doesn’t fit gets a quick link, not a panel.
  • Layout choices are reversible. If a future agent finds Bases supports embedded charts, the four-callout Coverage Health section can collapse back into a single Bases view; document the migration here when it happens.