11 KiB
Webinar vault dashboard — design
Date: 2026-07-28 Status: approved, ready for implementation planning
Context
The vault is an LLM-maintained wiki governed by CLAUDE.md. It currently holds 12 raw
sources, 9 ingested source summaries, 19 concept pages, and one webinar script
(raw/sources/Webinar script.md) that the concepts are supposed to feed.
Two problems motivated this work:
- The manual catalog drifts.
index.mdlistsIdeas for webinar.mdandmy theses.mdunderraw/sources/, but both live inraw/notes/. It does not mentionAgentic Engineering, explained by a 10x developer.mdat all, which sits un-ingested inraw/sources/. Nothing detects this. - No view of script coverage. There is no way to see which concept pages the webinar script actually delivers. A manual read shows the script is entirely machine-side: all human-side and strategy-side concepts are absent.
Goals
- Show processed and unprocessed sources, with a one-click ingest on the unprocessed.
- Show every concept and whether the webinar script mentions it, plus which script station it lands in.
- Keep coverage status in a separate markdown file, kept synchronized by a rule in
CLAUDE.md.
Non-goals
- Replacing
index.mdorlog.md. Both stay exactly as they are. - A general-purpose Obsidian dashboard framework. This is one vault-specific plugin.
- Publishing the plugin to the community plugin registry.
Decisions taken
| Decision | Choice | Rationale |
|---|---|---|
| Buttons | Own plugin, not Meta Bind | Needs are narrow and vault-specific; Meta Bind's expensive half (inline CM6 widgets, two-way frontmatter binding) is unused here |
| Coverage status source | Claude judges; user can pin | Sync sets status automatically, but a row marked Pinned: yes is never overwritten |
| Ingest mechanism | Headless claude -p via child_process |
One click, fully automatic. Chosen over a queue file with the unsupervised-write trade-off understood |
| Granularity | Status + script station | Turns the table into a pacing map, not just a checklist |
| Layout | Two-pane (Option B) | Sources and coverage both first-class; coverage grouped by station recovers most of the station-board view |
Architecture
.obsidian/plugins/webinar-dash/
manifest.json
main.js # plain CommonJS, no build step
styles.css # tesanti tokens scoped to .webinar-dash
dashboard.md # vault root, beside index.md
wiki/script-coverage.md # the coverage table
dashboard.md holds only a config block; the plugin renders everything:
# Webinar dashboard
```webinar-dash
script: raw/sources/Webinar script.md
coverage: wiki/script-coverage.md
```
Config keys are optional and fall back to those two defaults.
Where truth lives
| Data | Source of truth | Mechanism |
|---|---|---|
| Which sources are processed | Filesystem, read live | Diff raw/sources/*.md against the **Raw path:** value in every wiki/sources/*.md |
| Concept coverage status | wiki/script-coverage.md |
Written by Claude on sync, read by the plugin |
Mechanical facts come from the filesystem, judgment comes from the markdown file.
This makes the index.md class of drift structurally impossible on the sources half:
the dashboard cannot disagree with the filesystem because it derives from it.
The plugin never writes to the vault. It reads files and spawns one subprocess.
Nothing else. It also never reads index.md — the catalog is a human-facing artifact,
and treating it as input would reintroduce exactly the drift this design removes.
Source pipeline derivation
- List
raw/sources/*.md. - For each
wiki/sources/*.md, extract the backticked path from the line matching**Raw path:** \``. Verified consistent across all 9 existing source pages. - A raw file claimed by some source page is processed; unclaimed is unprocessed.
- A source page whose raw path no longer exists is reported as orphaned.
raw/notes/ is out of scope — those are notes, not sources.
Coverage file format
# Script coverage
#coverage
## Metadata
- **Script:** `raw/sources/Webinar script.md`
- **Last synced:** 2026-07-28
- **Stations:** Chat box · ReAct · Tools · Memory · Skills · Process · OS
## Coverage
| Concept | Status | Station | Pinned |
|---|---|---|---|
| [[harness]] | covered | Tools | |
| [[agentic-loops]] | partial | Process | |
| [[levels-of-ai-usage]] | partial | all | |
| [[connections-as-moat]] | absent | — | yes |
Field rules:
- Concept — an Obsidian wikilink to a page in
wiki/concepts/. The plugin extracts the page name from inside the brackets. - Status — exactly one of
covered,partial,absent. Any other value renders asinvalidrather than being silently coerced. - Station — one of the seven station names, a comma-separated list of them,
all, or—for none. - Pinned —
yes, or blank. Blank is the default.
The seven stations are the # headings of the script that represent technology levels:
Chat box, ReAct, Tools, Memory, Skills, Process, OS. The script's Intro,
Mail from boss, and Notes headings are setup and are not stations.
v1 does not write this file from the plugin. Pinning is a hand-edit of one cell — a pin toggle button would make the plugin a writer and risk clobbering concurrent user edits, which is not worth it for a one-word change.
Rendering — two-pane layout
Left pane (38%):
- Queue — unprocessed sources, each row showing filename, byte size, and an
Ingest button. Files missing from
index.mdneed no special flag: they appear here purely because no source page claims them, which is howAgentic Engineering, explained by a 10x developer.mdsurfaces despite being absent from the catalog. - Ingested — processed sources as compact rows: date from the source page filename prefix, plus title.
Right pane (62%):
- Coverage meter — a stacked bar of covered / partial / absent with a 2px gap between segments, plus a counted key.
- Coverage table grouped by station, with
No stationlast.
Collapses to a single column below 820px so a narrow Obsidian pane stays usable.
Styling follows the tesanti design system: black / white / red only, Space Grotesk
display, Inter body, JetBrains Mono for eyebrows and data, radii at most 6px, 1px
hairlines instead of shadows, Lucide stroke icons, no emoji. Status colors use the
system's --ok / --warn / --danger tokens and always ship with a text label, never
color alone. Dark mode is derived from the same ink ramp, with the red lifted to
#ff4a3d so small text clears contrast on near-black.
Ingest mechanism
const { spawn } = require("child_process");
spawn("claude", ["-p", `ingest "${file}"`], { cwd: vaultPath, shell: true });
shell: true is required on Windows to resolve claude.cmd, which places the filename
inside a shell string. Before spawning, the filename is rejected if it contains any of:
" ` $ & | ; < > % or a newline. Existing filenames include
Cyrillic, spaces, and !, all of which pass. A rejected filename shows an error in its
row and does not spawn.
Vault path comes from app.vault.adapter.getBasePath() on FileSystemAdapter.
Per-row states: idle → running with elapsed seconds → done or failed · exit <n>
with captured stderr in an expandable block. On success the pipeline is re-derived and
the row moves to the ingested list. A second click while running is ignored.
A global Sync coverage button spawns claude -p "sync script coverage".
Preflight: if claude is not resolvable on PATH, all buttons render disabled with that
reason stated.
Accepted risk
Headless ingest writes source summaries, concept pages, index.md, and log.md without
the user watching. This was chosen deliberately over a review checkpoint. Mitigations:
one file per click rather than a batch, captured output retained per row, and visible
per-row status. The writes land before the user reads them; this is understood and
accepted.
CLAUDE.md changes
- Folder convention — add
wiki/script-coverage.mdto the tree with a note that it is machine-maintained. - Tagging rules — add the row
wiki/script-coverage.md→#coverage. No existing page type fits: it is generated tabular data, not prose analysis. - Workflow D: Sync Script Coverage — re-read the script and every
wiki/concepts/*.md; setStatusandStationfor each; never modify a row whosePinnedisyes; add rows for new concept pages; remove rows for deleted ones; updateLast synced; then updateindex.mdand append tolog.md. - Sync triggers — Workflow D runs at the end of any ingest that creates or modifies
a concept page, whenever
Webinar script.mdchanges, and on the explicitsync script coverageintent, which is added to Operational Commands.
The coverage baseline is always the raw script at raw/sources/Webinar script.md.
Ingesting the script into wiki/sources/ later does not change the baseline.
Failure modes
| Condition | Behavior |
|---|---|
| Coverage file missing or table malformed | Sources pane renders normally; coverage pane shows a parse error with the offending line |
| Concept page exists with no table row | Rendered as unsynced, so a stale sync is visible rather than silent |
| Table row points at a nonexistent concept page | Rendered as stale, kept in place, not auto-removed |
| Source page whose raw path is missing | Listed under orphaned in the left pane |
child_process unavailable (mobile) |
Buttons render disabled with the reason |
claude not on PATH |
Buttons render disabled with the reason |
Initial coverage assessment
Read manually while designing; the first real sync will regenerate it. 19 concepts: 5 covered, 4 partial, 10 absent.
- covered — harness (Tools), skills-as-memory (Skills), solve-first-then-skillify (Skills), personal-ai-operating-system (OS), evolution-of-agent-tooling (Tools)
- partial — agentic-loops (Process), context-as-scarce-resource (Memory), levels-of-ai-usage (all), code-as-throwaway (OS)
- absent — connections-as-moat, product-ownership, seniority-and-the-junior-squeeze, decoupling-identity-from-profession, network-from-a-standing-start, think-wider-not-bigger, make-more-cheap-code, enterprise-ai-reality, integration-dead-ends, leave-less-room-for-imagination
Every absent concept is human-side or strategy-side. The ReAct station carries no wiki
concept at all.
Out of scope for v1
- Pin toggle button (hand-edit instead).
- Alternate station-board view toggle. The data file is layout-independent, so this is a render change if wanted later.
- Coverage for entities, sources, or queries — concepts only.
- Any view of
raw/notes/.
Note on spec location
This file introduces a docs/ folder at the vault root, which is not part of the
CLAUDE.md folder convention and will appear in Obsidian's file explorer. It can be
moved or deleted without affecting the implementation.
The vault is not a git repository, so this spec is not committed.