Files
WebinarNotes/docs/superpowers/specs/2026-07-28-webinar-dashboard-design.md
meels 5de50b155a chore: renormalize line endings under the new .gitattributes
Line-ending only. Verified: 3667 insertions against 3667 deletions with
zero content difference under --ignore-cr-at-eol.
2026-07-28 15:20:55 +02:00

12 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:

  1. The manual catalog drifts. index.md lists Ideas for webinar.md and my theses.md under raw/sources/, but both live in raw/notes/. It does not mention Agentic Engineering, explained by a 10x developer.md at all, which sits un-ingested in raw/sources/. Nothing detects this.
  2. 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.md or log.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

  1. List raw/sources/*.md.
  2. For each wiki/sources/*.md, extract the backticked path from the line matching **Raw path:** \``. Verified consistent across all 9 existing source pages.
  3. A raw file claimed by some source page is processed; unclaimed is unprocessed.
  4. 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 as invalid rather than being silently coerced.
  • Station — one of the seven station names, a comma-separated list of them, all, or for none.
  • Pinnedyes, 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.md need no special flag: they appear here purely because no source page claims them, which is how Agentic Engineering, explained by a 10x developer.md surfaces 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 station last.

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: idlerunning 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

  1. Folder convention — add wiki/script-coverage.md to the tree with a note that it is machine-maintained.
  2. Tagging rules — add the row wiki/script-coverage.md#coverage. No existing page type fits: it is generated tabular data, not prose analysis.
  3. Workflow D: Sync Script Coverage — re-read the script and every wiki/concepts/*.md; set Status and Station for each; never modify a row whose Pinned is yes; add rows for new concept pages; remove rows for deleted ones; update Last synced; then update index.md and append to log.md.
  4. Sync triggers — Workflow D runs at the end of any ingest that creates or modifies a concept page, whenever Webinar script.md changes, and on the explicit sync script coverage intent, 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

Eight of the ten absent concepts are human-side or strategy-side per index.md's grouping. The other two — integration-dead-ends and leave-less-room-for-imagination — are machine-side, and are absent because the script demonstrates the happy path and so never reaches connector gating or spec ambiguity, the two ways the machine side fails in practice. 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.