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:
- KPI strip — am I in trouble right now? (counts)
- Pipeline — is the system shape clear in my head? (mermaid + foldable embeds)
- Coverage health — what is degrading? (charts)
- Recent — what changed? (dataview + commit list)
- Bases — let me query (ADRs / bugs / tags)
- Architecture — let me zoom out (excalidraw + C4 mermaid)
- Compliance health — am I about to break the law?
- 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.
| Callout | Role on this dashboard |
|---|---|
info | Neutral metadata (the KPI strip helper, anything informational) |
example- | Foldable per-queue detail (Pipeline section) |
chart | Reserved chart panel — wraps a Charts plugin block (Coverage Health) |
abstract | Recent activity feed (the dataview block) |
note | Bases query panels |
todo | Action the operator must take (e.g. create the Excalidraw canvas) |
warning | Compliance snapshot — default state, agent may swap to success or failure |
quote | Footer / 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
| Placeholder | Type | Expected fill |
|---|---|---|
{{NOTE_COUNT}} | integer | total .md files in Wiki/ |
{{ADR_COUNT}} | integer | count of notes tagged adr |
{{BUG_COUNT}} | integer | count of notes tagged bug-verified (or callouts of type bug) |
{{ORPHAN_COUNT}} | integer | notes with zero inbound links per Obsidian graph |
{{LAST_UPDATED}} | date | most recent updated: frontmatter across Wiki/ |
{{TODAY_COMMITS_LIST}} | inline list | git log --since=midnight --pretty='%h %s' from the canonical repo, joined with ” · “ |
{{OPT_OUT_HASH_COUNT}} | integer | SELECT count(*) FROM optout_hashes |
{{ROPA_TODAY}} | integer | SELECT count(*) FROM ropa_log WHERE created_at::date = current_date |
{{ART14_PENDING}} | integer | Article 14 notifications queued but not sent |
{{REKLAMSPARR_7D}} | integer | reklamspärr block hits in the last 7 days |
Fenced-block TODOs left in Dashboard.md
| Section | Block type | Expected fill |
|---|---|---|
| Pipeline at a glance | ```mermaid | flowchart LR showing four sources (BV, SCB, Crawlee, Maps) → Scrape_Job → Enrich_Job (with reklamspärr + opt-out gates) → Update_Job |
| Coverage health (1) | ```chart | bar, x = wiki category, y = note count |
| Coverage health (2) | ```chart | doughnut, covered vs untested src modules |
| Coverage health (3) | ```chart | bar, open verified bugs by area |
| Coverage health (4) | ```chart | line, notes per ISO week |
| Recent | ```dataview | TABLE, last 10 notes by updated, columns updated + tags |
| Bases — ADRs | ```base | table, filter tag: adr, columns file/status/date/title |
| Bases — Verified bugs | ```base | table, filter tag: bug-verified, columns file/severity/area/first-seen |
| Bases — By tag | ```base | table grouped by tag |
| Architecture | ```mermaid | C4-style container diagram of the full system |
Embeds left in Dashboard.md
| Embed | Notes |
|---|---|
![[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
| Plugin | Status | Used by | Degrades to |
|---|---|---|---|
| Bases | Built-in (Obsidian 1.9+) | Section “Bases” | Switch ```base to ```dataview if the syntax is wrong; same intent |
| Dataview | Community | Recent (and Bases fallback) | Falls back to a static markdown list with a > [!todo] reminder to fill manually |
| Charts | Community (obsidian-charts) | Coverage health | A > [!todo] callout listing the four data points the chart would have shown |
| Excalidraw | Community | Architecture | Embed renders as a broken link until the canvas exists; the > [!todo] next to it documents creation |
| Mermaid | Built-in | Pipeline + Architecture C4 | n/a — always available |
Reading-view rendering notes
- Callout nesting: Obsidian renders nested callouts up to 3 levels reliably. The Recent section nests a
dataviewblock inside anabstractcallout — 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
```basefence. If a future Obsidian version changes the syntax, swap todataviewand 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_Jobheadings, the embed renders blank — coordinate with whoever owns[[Pipeline]].
Footer
- 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.