Line-ending only. Verified: 3667 insertions against 3667 deletions with zero content difference under --ignore-cr-at-eol.
257 lines
12 KiB
Markdown
257 lines
12 KiB
Markdown
# 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:
|
|
|
|
````markdown
|
|
# 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:** \`<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
|
|
|
|
```markdown
|
|
# 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.
|
|
- **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.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
|
|
|
|
```js
|
|
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
|
|
|
|
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.
|