design-harness — the human adjudicates, the agent runs errands
Markdown is the single source of truth; the canvas HTML, every layer index, and the
location registry are projections, rebuildable at any time. Judgment comes only from the
human; you lay out options and evidence and run the errands — never adjudicate in the
human's place.
Structure
Workspace layout
<workspace>/
├── target.md the human's acceptance criteria; source of the output form
├── logs.md append-only change ledger (see "Disciplines · Ledger")
├── index.md workspace entry point
├── sources/<type>/*.md ① evidence: one source, one card
├── ideas/*.md ② judgments (archive/ holds archived cards)
├── output/… ③ assembly; internal structure set by the chosen output form
└── board/*.md free surface: one file, one board
A host may hold many workspaces under any directory names; the sole registry is
.design-harness/config.json at the host root — discovery trusts the registry, never
the directory name.
Every layer also carries an index.md, grouped under tag headings (## TAG: one line on why they belong together), unclassified cards flat at the end after a --- divider and
an unclassified marker. Search goes through the indexes; there is no classification
layer.
Three layers + the free surface
- sources/ — evidence. One source, one card, filed under
sources/<type>/. The
agent ingests and grades freely, and invents the type directories itself — no preset
list; name them after what the source is (papers, github, podcasts, …), adding
directories as needed. The directory name projects verbatim as the card's badge on
the canvas.
- ideas/ — judgments. Judgment comes only from the human: decision judgments the
human voices in conversation are transcribed into idea cards automatically, and the
human can simply ask for a card to be added; the agent transcribes, never invents.
Two states: live (the file exists) and archived (moved into
ideas/archive/, never
deleted). A new idea repeating an old card's judgment merges automatically; a
conflicting one puts both on the board for the human to pick.
- output/ — assembly, the convergent layer. First assembly is human-initiated;
its form comes from references/output-forms/
and the human's
target.md. Ideas diverge, output converges: output changes only on
the human's word (see "Workflow · Sync").
One free surface besides: board/ — the human's own boards, one markdown file per
board (comparison matrices, theme grids, any scratch reasoning). No schema, no
required fields — this freedom belongs to the human; edit only when asked ("lay these
three sources out as a comparison").
Card schema and templates
- A card = frontmatter + title + summary. No fixed sections; references are inline
links in the summary.
ideas/ cards (including archive/): frontmatter requires id and type;
tags and conflicts optional (see "Exactly three structured facts"). No status
field — the file's existence is the live state, sitting under archive/ is the
archived state.
sources/ cards: frontmatter optional; when present only tags is read.
board/ documents: no schema. The board is terminal: it may reference any
layer, but sources/ideas/output must never reference the board — distill a board's
conclusions into idea cards.
idea card template:
---
id: <kebab-slug, matching the filename>
type: idea
tags: [<at most one; omit the whole line>]
conflicts: [<ids of prior cards this judgment contends with; omit when none>]
---
# <the judgment in one sentence>
<Summary: what the judgment says and why; the evidence and prior cards it stands on as
inline links.>
source card template:
---
tags: [<at most one; the whole frontmatter may be omitted>]
---
# <source name + one-line characterization>
<Core content + relevance to this project; the original provenance (arXiv/URL/date/
authors) as inline links.>
target template:
# target — acceptance criteria for the output
## Purpose
(One paragraph: what this workspace decides, for whom, and where the boundaries are.)
## Current requirements
- (One checkable requirement per line.)
## Fulfilment map
- (Requirement → output file mapping, updated with each assembly.)
Exactly three structured facts
- References — inline markdown links in the card body. Forward only: a link points
at the evidence or prior cards this card stands on, never at supporters. "Who cites
me" is a projection-derived backlink, never written down. Acyclic: mutual
references mean the two cards should merge, or one edge is a mistakenly written
backlink — delete it.
- Tags — single level, at most one per card, optional. A card that truly
belongs to two tags is two cards: split it. No tag just means unclassified. Tags may
be proposed by the human or assigned by the agent.
- Conflict — the optional
conflicts: [<other card's id>] in the newer card's
frontmatter, declaring a judgment conflict with prior cards (written on the new card
only, acyclic, same direction as references). The field lives exactly as long as the
conflict: the human's adjudication removes it — archive the loser and the edge
vanishes with the card; rewrite in place and the entry is deleted. The indexes'
pending-conflict annotations derive from this field.
Everything else — backlinks, distances, coordinates, clusters, index groupings — is
derived and never enters the truth.
Rendering contract — the engine's minimum
Everything projection needs. This section is the engine; everything else in this file
is methodology, freely reconfigurable per schema — one engine serves many thin
schemas, a new scenario is a new schema configuration rather than a new engine; the
contract binds files, and the CLI is only the first host.
- Four layer names:
sources/ and ideas/ become canvas nodes, output/ and board/
become document panels. Paths containing an archive/ segment are excluded;
index.md is excluded.
- Files are UTF-8 markdown.
- Frontmatter is optional; when present it must close (
--- … ---).
Everything else degrades without breaking: no H1 → show the filename stem; no tag → the
unclassified group; any source type directory → the name projects verbatim;
unresolvable link → no edge. Silent data loss is a validator error, never waved through:
markdown under an unknown top-level directory, or frontmatter opened but never closed,
is INVALID.
Canvas — the only projection surface
The canvas is the human's main thinking surface and only a projection — it holds no
facts and accepts no writes that bypass markdown. It doubles as the sharing surface:
every card links back to its evidence, so handing someone the board hands them the
reasoning behind the design. There is exactly one canvas — the
unified canvas (template; its interaction contract lives in the
workspace's canvas module doc) — and exactly one builder: the rules (truth-driven,
edges = in-card references, layout derived from references + tags) are built in, and
the workspace path is the only required input. Conflict edges render red — a
template-base default every style pack inherits and may override.
Visual style is pure CSS, living entirely in the canvas/styles/ style pack; the
template carries a single /*__CSS__*/ slot and no CSS of its own. The default build
embeds the whole pack plus the top-right toolbar switcher — the human reskins live there
(every open starts from the pin-and-paper native look); you only hint at the switcher
once. --css canvas/styles/<slug>/canvas.css pins one style with no switcher — to name
a slug, read canvas/styles/selection-index.json,
never bulk-read the whole pack. Never fork the template or the builder for looks; when a
style's design.md changes, recompile its canvas.css in the same change — the spec is
the truth (the pack validator checks the dual palettes and bans external reach).
Workflow
Discover the workspace (the first step on every trigger). Order: a path the human
gives > the .design-harness/config.json registry > the default
docs/design-harness/. More than one candidate, or zero: ask — never initialize a
new workspace silently (init in the wrong place forks the truth). The canvas path
follows the same rule: whichever of the two locations the registry is missing —
the workspace, or the "canvas" key — is asked as an option picker, one prompt when
both are missing; canvas options are docs/canvas.html (keeps zero-workflow GitHub
Pages open: that mode serves only / or /docs) and beside-the-workspace; record
the choice under the registry's "canvas" key. Only
the bootstrap writes the registry; on finding a workspace the registry missed, re-run
the bootstrap to record it. A fresh bootstrap ends by asking the human for the target
(purpose and acceptance criteria, in the human's words) — transcribe the answer into
target.md, never invent one; the human may defer. Scripts run at the host project root (<skill-dir> is
wherever this skill is installed):
python3 <skill-dir>/scripts/discover_workspace.py # prints which workspace resolves
python3 <skill-dir>/scripts/init_workspace.py <workspace> # bootstrap: skeleton + registry + both validators
Ingest evidence: file source cards, grade, anchor to provenance; source
disagreements go on the board as-is.
Transcribe judgments: judgments the human voices in conversation transcribe into
idea cards automatically; same-judgment cards merge automatically, conflicts go on
the board for the human to pick; derive references and tag classification (one
pass — both are relationship reads).
Sync (on the human's command, never automatic): idea → output re-derivation runs
only when the human orders it — apply directly with a one-line receipt, a diff first
for large changes; re-derive the affected elements, never rewrite the whole document.
A human edit to output is itself an adjudication: back-transcribe it into idea cards
automatically (transcription only). Target changes follow the same discipline as idea
changes. When output lags you may hint once ("output is N ideas behind"), never
twice.
Assemble: first assembly is human-initiated; the form comes from target.md +
the output-forms library — when the human sets a target, recommend a form from the
library; if target.md is still blank when assembly is called for, ask for the
target once more before recommending; the first is system-design (mermaid diagrams are the markdown body
itself, plus a modules/ layer, one module per file). Anchor every output claim to
evidence; write principles as bullet lists, one principle per bullet — so each can be
referenced, edited, and ledgered on its own; in the system-design form the diagram
IS the markdown body: mermaid blocks carry click declarations into module files.
Project and deliver: whenever the truth changes, regenerate the touched layers'
indexes, rebuild the canvas, and republish in the same turn; append the ledger.
python3 <skill-dir>/scripts/build_canvas.py <workspace> -o <temp-dir> [--css …] [--title '…']
Without <workspace> the tool discovers on its own (registry → default location) and
refuses to act on ambiguity. -o takes a directory (the file lands inside as
canvas.html) or a .html path written as-is. Build targets are the fixed
canvas.html beside the workspace — commit it when the workspace lives in git, so
whoever gets the repo gets the board — or a temp dir or artifact for throwaway views;
the builder refuses to write inside the workspace either way. A build is not delivered until the human holds a clickable link. The
product is one fully self-contained HTML file (all dependencies inlined, zero network
requests). The default delivery is a published HTML link: publish as an artifact
or the host's hosted pages (GitHub Pages recipe)
and hand over that URL; in Claude Code (a local CLI
session) a local link suffices — an absolute file://…/canvas.html URL or a local
static server. Every rebuild reuses the existing link — mint a new one only when
the human asks. So the link survives the session: record the delivery target — the
HTML output path or the published link — under a "canvas" key in the
.design-harness/config.json registry, and read it back before building; later
sessions then rebuild the same file and republish the same link.
Teach in place, never lecture
- First contact (the workspace just bootstrapped, whether the skill fired passively or
on demand): the first move is always to build the workspace, construct the initial
canvas in the same turn, hand over the link, then orient in one sentence — "From now
on every resource we look into files itself, and the decision judgments we talk
through land on the board; you can also just ask me to change things, and tell me
whenever you want the design assembled." Never repeat it.
- The workspace is young, no output yet: leave the assembly seat visibly empty — say
once "output starts on the human's word".
- Ideas look mature: suggest assembly in one sentence; never push twice.
- The human asks for output too early: comply, deliver the honestly thin result, and say
which ideas are missing — never refuse.
- First assembly: state the sync rule in one sentence ("from here output updates only
when you order a sync; edit output directly and I back-transcribe it into ideas").
- Every sync: give a one-line receipt of what was re-derived or back-transcribed.
Disciplines
The never list: never pick sides, never invent judgments, never archive an idea on
your own, never initiate first assembly on your own, never touch output outside the
human's command.
Markdown first: change the truth first, then rebuild every affected projection
(indexes, canvas HTML) in the same turn; never edit a projection directly. Projections
hold no facts and rebuild wholesale from markdown — committing one (the shared canvas)
never promotes it to truth, and a committed canvas rides in the same change as the
truth edit that made it stale.
The ledger: logs.md is append-only and covers both ideas and output. Every
change, one line per touched layer:
- YYYY-MM-DD · card or doc · action · delta (old → new) · reason
Record the delta itself — the minimal old → new — not just the action and reason.
When the workspace sits outside git, the ledger is the only way back.
Validation: two validators — scripts/check_workspace.py and
scripts/check_doc_links.py — run automatically at bootstrap and on every canvas
build: failure exits nonzero, prints the full problem list, and produces no HTML; fix
the truth, then rebuild. When this file's contract changes, the validators change in
the same commit.
Toolbox
All live in <skill-dir>/scripts/ and run standalone; command syntax sits at the usage
sites (workflow steps 0 and 5).
init_workspace.py — bootstrap: skeleton (idempotent, never clobbers) + registry
write + runs both validators itself.
discover_workspace.py — read-only discovery, prints which workspace resolves;
ambiguity is reported, never resolved silently.
build_canvas.py — the only canvas build entry; runs both validators as a gate, then
emits the self-contained HTML.
check_workspace.py — schema validation: required frontmatter, single tag, conflicts
targets, forward-only acyclic references, no inbound board references.
check_doc_links.py — link integrity: no dangling links inside the workspace.
check_style_pack.py — style pack validation: dual palettes, no external reach,
tokens aligned to the template.
1---2name: design-harness3description: A decision board for evidence-based calls — the human adjudicates, the agent runs the errands. Three layers (sources → ideas → output) in plain markdown, synced on the human's command, projected onto a visual canvas. Use for vendor/tool selection, literature reviews, due diligence, competitive analysis, or any contested call that must stand on traceable evidence — triggers like "file these papers", "put this on the board", "assemble the design", "how do we decide this".4license: MIT5---67# design-harness — the human adjudicates, the agent runs errands89Markdown is the **single source of truth**; the canvas HTML, every layer index, and the10location registry are projections, rebuildable at any time. Judgment comes only from the11human; you lay out options and evidence and run the errands — never adjudicate in the12human's place.1314## Structure1516### Workspace layout1718```19<workspace>/20├── target.md the human's acceptance criteria; source of the output form21├── logs.md append-only change ledger (see "Disciplines · Ledger")22├── index.md workspace entry point23├── sources/<type>/*.md ① evidence: one source, one card24├── ideas/*.md ② judgments (archive/ holds archived cards)25├── output/… ③ assembly; internal structure set by the chosen output form26└── board/*.md free surface: one file, one board27```2829A host may hold many workspaces under any directory names; the sole registry is30`.design-harness/config.json` at the host root — discovery trusts the registry, never31the directory name.3233Every layer also carries an `index.md`, grouped under tag headings (`## TAG: one line on34why they belong together`), unclassified cards flat at the end after a `---` divider and35an *unclassified* marker. Search goes through the indexes; there is no classification36layer.3738### Three layers + the free surface39401. **sources/ — evidence.** One source, one card, filed under `sources/<type>/`. The41 agent ingests and grades freely, and invents the type directories itself — no preset42 list; name them after what the source is (papers, github, podcasts, …), adding43 directories as needed. The directory name projects verbatim as the card's badge on44 the canvas.452. **ideas/ — judgments.** **Judgment comes only from the human**: decision judgments the46 human voices in conversation are transcribed into idea cards automatically, and the47 human can simply ask for a card to be added; the agent transcribes, never invents.48 Two states: live (the file exists) and archived (moved into `ideas/archive/`, never49 deleted). A new idea repeating an old card's judgment merges automatically; a50 conflicting one puts both on the board for the human to pick.513. **output/ — assembly, the convergent layer.** **First assembly is human-initiated**;52 its form comes from [references/output-forms/](references/output-forms/system-design.md)53 and the human's `target.md`. Ideas diverge, output converges: output changes only on54 the human's word (see "Workflow · Sync").5556One free surface besides: **board/** — the human's own boards, one markdown file per57board (comparison matrices, theme grids, any scratch reasoning). **No schema, no58required fields** — this freedom belongs to the human; edit only when asked ("lay these59three sources out as a comparison").6061### Card schema and templates6263- A card = **frontmatter + title + summary**. No fixed sections; references are inline64 links in the summary.65- `ideas/` cards (including `archive/`): frontmatter **requires `id` and `type`**;66 `tags` and `conflicts` optional (see "Exactly three structured facts"). No status67 field — the file's existence is the live state, sitting under `archive/` is the68 archived state.69- `sources/` cards: frontmatter optional; when present only `tags` is read.70- `board/` documents: **no schema**. The board is **terminal**: it may reference any71 layer, but sources/ideas/output must never reference the board — distill a board's72 conclusions into idea cards.7374idea card template:7576```markdown77---78id: <kebab-slug, matching the filename>79type: idea80tags: [<at most one; omit the whole line>]81conflicts: [<ids of prior cards this judgment contends with; omit when none>]82---8384# <the judgment in one sentence>8586<Summary: what the judgment says and why; the evidence and prior cards it stands on as87inline links.>88```8990source card template:9192```markdown93---94tags: [<at most one; the whole frontmatter may be omitted>]95---9697# <source name + one-line characterization>9899<Core content + relevance to this project; the original provenance (arXiv/URL/date/100authors) as inline links.>101```102103target template:104105```markdown106# target — acceptance criteria for the output107108## Purpose109(One paragraph: what this workspace decides, for whom, and where the boundaries are.)110111## Current requirements112- (One checkable requirement per line.)113114## Fulfilment map115- (Requirement → output file mapping, updated with each assembly.)116```117118### Exactly three structured facts1191201. **References** — inline markdown links in the card body. Forward only: a link points121 at the evidence or prior cards this card stands on, never at supporters. "Who cites122 me" is a projection-derived backlink, never written down. **Acyclic**: mutual123 references mean the two cards should merge, or one edge is a mistakenly written124 backlink — delete it.1252. **Tags** — single level, **at most one per card**, optional. A card that truly126 belongs to two tags is two cards: split it. No tag just means unclassified. Tags may127 be proposed by the human or assigned by the agent.1283. **Conflict** — the optional `conflicts: [<other card's id>]` in the **newer** card's129 frontmatter, declaring a judgment conflict with prior cards (written on the new card130 only, acyclic, same direction as references). The field lives exactly as long as the131 conflict: the human's adjudication removes it — archive the loser and the edge132 vanishes with the card; rewrite in place and the entry is deleted. The indexes'133 pending-conflict annotations derive from this field.134135Everything else — backlinks, distances, coordinates, clusters, index groupings — is136derived and never enters the truth.137138### Rendering contract — the engine's minimum139140Everything projection needs. **This section is the engine; everything else in this file141is methodology**, freely reconfigurable per schema — one engine serves many thin142schemas, a new scenario is a new schema configuration rather than a new engine; the143contract binds files, and the CLI is only the first host.144145- Four layer names: `sources/` and `ideas/` become canvas nodes, `output/` and `board/`146 become document panels. Paths containing an `archive/` segment are excluded;147 `index.md` is excluded.148- Files are UTF-8 markdown.149- Frontmatter is optional; when present it must close (`---` … `---`).150151Everything else degrades without breaking: no H1 → show the filename stem; no tag → the152*unclassified* group; any source type directory → the name projects verbatim;153unresolvable link → no edge. Silent data loss is a validator error, never waved through:154markdown under an unknown top-level directory, or frontmatter opened but never closed,155is INVALID.156157### Canvas — the only projection surface158159The canvas is the human's main thinking surface and **only a projection** — it holds no160facts and accepts no writes that bypass markdown. It doubles as the sharing surface:161every card links back to its evidence, so handing someone the board hands them the162reasoning behind the design. There is exactly one canvas — the163unified canvas ([template](canvas/template.html); its interaction contract lives in the164workspace's canvas module doc) — and exactly one builder: the rules (truth-driven,165edges = in-card references, layout derived from references + tags) are built in, and166the workspace path is the only required input. Conflict edges render **red** — a167template-base default every style pack inherits and may override.168169Visual style is pure CSS, living entirely in the `canvas/styles/` style pack; the170template carries a single `/*__CSS__*/` slot and no CSS of its own. The default build171embeds the whole pack plus the top-right toolbar switcher — the human reskins live there172(every open starts from the pin-and-paper native look); you only hint at the switcher173once. `--css canvas/styles/<slug>/canvas.css` pins one style with no switcher — to name174a slug, read [canvas/styles/selection-index.json](canvas/styles/selection-index.json),175never bulk-read the whole pack. Never fork the template or the builder for looks; when a176style's `design.md` changes, recompile its `canvas.css` in the same change — the spec is177the truth (the pack validator checks the dual palettes and bans external reach).178179## Workflow1801810. **Discover the workspace** (the first step on every trigger). Order: a path the human182 gives > the `.design-harness/config.json` registry > the default183 `docs/design-harness/`. More than one candidate, or zero: ask — **never initialize a184 new workspace silently** (init in the wrong place forks the truth). The canvas path185 follows the same rule: whichever of the two locations the registry is missing —186 the workspace, or the `"canvas"` key — is asked as an option picker, one prompt when187 both are missing; canvas options are `docs/canvas.html` (keeps zero-workflow GitHub188 Pages open: that mode serves only `/` or `/docs`) and beside-the-workspace; record189 the choice under the registry's `"canvas"` key. Only190 the bootstrap writes the registry; on finding a workspace the registry missed, re-run191 the bootstrap to record it. A fresh bootstrap ends by asking the human for the target192 (purpose and acceptance criteria, in the human's words) — transcribe the answer into193 `target.md`, never invent one; the human may defer. Scripts run at the host project root (`<skill-dir>` is194 wherever this skill is installed):195196 ```bash197 python3 <skill-dir>/scripts/discover_workspace.py # prints which workspace resolves198 python3 <skill-dir>/scripts/init_workspace.py <workspace> # bootstrap: skeleton + registry + both validators199 ```2001. **Ingest evidence**: file source cards, grade, anchor to provenance; source201 disagreements go on the board as-is.2022. **Transcribe judgments**: judgments the human voices in conversation transcribe into203 idea cards automatically; same-judgment cards merge automatically, conflicts go on204 the board for the human to pick; derive references and tag classification (one205 pass — both are relationship reads).2063. **Sync (on the human's command, never automatic)**: idea → output re-derivation runs207 only when the human orders it — apply directly with a one-line receipt, a diff first208 for large changes; re-derive the affected elements, never rewrite the whole document.209 A human edit to output is itself an adjudication: back-transcribe it into idea cards210 automatically (transcription only). Target changes follow the same discipline as idea211 changes. When output lags you may hint once ("output is N ideas behind"), never212 twice.2134. **Assemble**: first assembly is human-initiated; the form comes from `target.md` +214 the output-forms library — when the human sets a target, recommend a form from the215 library; if `target.md` is still blank when assembly is called for, ask for the216 target once more before recommending; the first is **system-design** (mermaid diagrams are the markdown body217 itself, plus a `modules/` layer, one module per file). Anchor every output claim to218 evidence; write principles as bullet lists, one principle per bullet — so each can be219 referenced, edited, and ledgered on its own; in the system-design form the diagram220 IS the markdown body: mermaid blocks carry `click` declarations into module files.2215. **Project and deliver**: whenever the truth changes, regenerate the touched layers'222 indexes, rebuild the canvas, and republish in the same turn; append the ledger.223224 ```bash225 python3 <skill-dir>/scripts/build_canvas.py <workspace> -o <temp-dir> [--css …] [--title '…']226 ```227228 Without `<workspace>` the tool discovers on its own (registry → default location) and229 refuses to act on ambiguity. `-o` takes a directory (the file lands inside as230 `canvas.html`) or a `.html` path written as-is. Build targets are the fixed231 `canvas.html` beside the workspace — commit it when the workspace lives in git, so232 whoever gets the repo gets the board — or a temp dir or artifact for throwaway views;233 the builder refuses to write inside the workspace either way. A build is not delivered until the human holds a clickable link. The234 product is one fully self-contained HTML file (all dependencies inlined, zero network235 requests). **The default delivery is a published HTML link**: publish as an artifact236 or the host's hosted pages ([GitHub Pages recipe](references/publish-canvas-github-pages.md))237 and hand over that URL; in Claude Code (a local CLI238 session) a local link suffices — an absolute `file://…/canvas.html` URL or a local239 static server. **Every rebuild reuses the existing link** — mint a new one only when240 the human asks. So the link survives the session: record the delivery target — the241 HTML output path or the published link — under a `"canvas"` key in the242 `.design-harness/config.json` registry, and read it back before building; later243 sessions then rebuild the same file and republish the same link.244245### Teach in place, never lecture246247- First contact (the workspace just bootstrapped, whether the skill fired passively or248 on demand): the first move is always to build the workspace, construct the initial249 canvas in the same turn, hand over the link, then orient in one sentence — "From now250 on every resource we look into files itself, and the decision judgments we talk251 through land on the board; you can also just ask me to change things, and tell me252 whenever you want the design assembled." Never repeat it.253- The workspace is young, no output yet: leave the assembly seat visibly empty — say254 once "output starts on the human's word".255- Ideas look mature: suggest assembly in one sentence; never push twice.256- The human asks for output too early: comply, deliver the honestly thin result, and say257 which ideas are missing — never refuse.258- First assembly: state the sync rule in one sentence ("from here output updates only259 when you order a sync; edit output directly and I back-transcribe it into ideas").260- Every sync: give a one-line receipt of what was re-derived or back-transcribed.261262## Disciplines263264- **The never list**: never pick sides, never invent judgments, never archive an idea on265 your own, never initiate first assembly on your own, never touch output outside the266 human's command.267- **Markdown first**: change the truth first, then rebuild every affected projection268 (indexes, canvas HTML) in the same turn; never edit a projection directly. Projections269 hold no facts and rebuild wholesale from markdown — committing one (the shared canvas)270 never promotes it to truth, and a committed canvas rides in the same change as the271 truth edit that made it stale.272- **The ledger**: `logs.md` is append-only and covers both ideas **and** output. Every273 change, one line per touched layer:274275 ```276 - YYYY-MM-DD · card or doc · action · delta (old → new) · reason277 ```278279 Record **the delta itself** — the minimal old → new — not just the action and reason.280 When the workspace sits outside git, the ledger is the only way back.281- **Validation**: two validators — `scripts/check_workspace.py` and282 `scripts/check_doc_links.py` — run automatically at bootstrap and on every canvas283 build: failure exits nonzero, prints the full problem list, and produces no HTML; fix284 the truth, then rebuild. When this file's contract changes, the validators change in285 the same commit.286287## Toolbox288289All live in `<skill-dir>/scripts/` and run standalone; command syntax sits at the usage290sites (workflow steps 0 and 5).291292- `init_workspace.py` — bootstrap: skeleton (idempotent, never clobbers) + registry293 write + runs both validators itself.294- `discover_workspace.py` — read-only discovery, prints which workspace resolves;295 ambiguity is reported, never resolved silently.296- `build_canvas.py` — the only canvas build entry; runs both validators as a gate, then297 emits the self-contained HTML.298- `check_workspace.py` — schema validation: required frontmatter, single tag, conflicts299 targets, forward-only acyclic references, no inbound board references.300- `check_doc_links.py` — link integrity: no dangling links inside the workspace.301- `check_style_pack.py` — style pack validation: dual palettes, no external reach,302 tokens aligned to the template.