BC Canvas

The docs

One file, and every tool that touches it.

Every surface in this project draws the same sheet from the same small file: the editor where a canvas is argued into shape, the command line that keeps it honest, the fence that draws it in your docs, the server that reads it to an agent. These pages cover each one end to end — install, the day-to-day, and the edges where it stops. SPEC.md is the letter of the law underneath.

editor

The editor

One canvas, edited in place. There is no form beside a preview and no Save ahead of an export: the sheet on the projector during the workshop is the file in the repo after it. Blur commits a field, Esc reverts it, and everything else materializes on approach.

Three views of one canvas

Sheet · JSON · Markdown are tabs over the same document, not three documents. The Sheet is where editing happens. The JSON view shows the exact bytes an export would write, and is editable with an explicit Apply — one commit, one undo step, validated by the same parser as import, so a pasted canvas never replaces a good one halfway. The Markdown view is read-only, and only ever an export.

Nothing leaves the browser

No account, no server. The canvas autosaves to localStorage on every commit, but that slot is a safety net, not storage — the durable form is the Canvas file you export. The chrome keeps one score, unexported changes: has this canvas left the browser in a form that can come back? Exporting or importing a Canvas file or HTML artifact clears it; PNG, SVG and Markdown never do, because none of them could bring the canvas back. Anything that would replace a canvas still carrying unexported changes — an import, an example, a blank sheet — asks first. That is the app's only dialog.

Undo, by commit

Every accepted change — a field committed on blur, one add, one removal, one reorder — is one undo step. ⌘Z undoes, ⇧⌘Z redoes; mid-edit, ⌘Z reverts the field first. Importing or replacing the canvas is a session boundary, not an edit: it clears history.

Keyboard & the Reference

The whole sheet is operable from the keyboard — every add, removal, pick and reorder. ⌘/ (Ctrl+/ on Windows and Linux) opens the Reference: the shortcut list, and the ddd-crew's own material on the method. Everything else the editor teaches in place — picker descriptions, the placeholder questions in an empty section, the footer legend.

Examples

Four invented domains ship in the Examples menu, from every section filled to mid-workshop with the open questions still winning. The same files are committed under examples/ and re-import as-is.

file

The Canvas file

Everything on this page reads or writes one format: <name>.bcc.json, a small, flat JSON file meant to be committed beside the code it describes. It is deliberately boring — one key order, one indent, one trailing newline — because boring is what diffs well.

  • The schema is this project's own, and versioned. The root version is currently 2; an older file is migrated up on read, in the editor and everywhere else, because everything reads through the one parser.
  • Eleven sections in a canonical order — name, purpose, strategic classification, domain roles, inbound communication, ubiquitous language, business decisions, outbound communication, assumptions, verification metrics, open questions. The ddd-crew canvas, as data.
  • A canvas survives the round trip byte-identical. Export → import → export writes the same bytes, and bcc fmt restores them for a file edited by hand. Honest diffs are the point: a canvas that churns bytes it does not mean cannot live next to code.
  • The name has to end .bcc.json. That is what bcc ls globs on and what the editor's Import… accepts; a canvas saved as shipping.json is invisible to both. The directory and the rest of the name are yours.

The full schema, shape rules and migration story are in SPEC.md §3, with a complete reference example.

exports

Exports

Five ways out of the browser; two of them come back. The split is the one that matters, because the editor's only dirty state is a canvas that has not left in a form that can return — and the editor's export and bcc render call the same function, so the files are byte-identical whichever wrote them.

comes back

.bcc.json — Canvas file
The canvas itself. The durable form; everything else is derived from it.
.bcc.html — HTML artifact
One self-contained file for sharing: all three views pre-rendered so none needs script, with the Canvas file embedded inside — importing the artifact recovers the canvas whole.

one way out

.bcc.svg — image
The sheet as one self-contained image — the one meant to be committed beside its canvas, so any markdown host that will never draw a fence can still point an <img> at it. bcc check re-renders committed images and compares bytes, so a stale picture fails a check instead of being believed.
.bcc.png — image
The sheet as pixels, for chat and slides.
.bcc.md — Markdown
The canvas as prose. There is no Markdown import — keep the Canvas file if you mean to edit again.
bcc

The command line

bcc treats canvases the way a toolchain treats source: list them, check them, format them, build artifacts from them. It runs in plain Node, straight off this repo — nothing is published, and there is nothing to install:

$ alias bcc='npx --yes github:mitchellvanw/bc-canvas-editor'

The first call clones and installs; later ones come out of npm's cache and start in about a second. npx resolves main at the moment it runs — pin a commit (…bc-canvas-editor#<sha>) if you need reproducibility.

$ bcc ls                        # what canvases are here, what each is for, how full each one is
$ bcc check                     # do they all still read, and are the images beside them current
$ bcc fmt                       # canonical bytes, in place
$ bcc render orders.bcc.json    # the HTML artifact, beside the canvas
$ bcc render --svg orders.bcc.json

check and fmt are what make a canvas behave like source code rather than an attachment. check reads every canvas through the parser the editor's Import… uses — a canvas that passes here opens there — and exits 1 if anything does not check out, stale images included. fmt rewrites a canvas in its canonical bytes; fmt --check names what would change and writes nothing, for CI. render writes the .bcc.html artifact, or a .bcc.svg with --svg — --out <file> redirects a single render, and only render --svg ever needs a browser, and only to measure a height that --height <pixels> can supply instead.

The root, and what counts as a canvas

Every command takes --root <directory>: where bcc looks, and the furthest it goes. It defaults to the working directory, and no path — symlinks resolved first — ever reaches outside it. A canvas is any *.bcc.json, or the canvas embedded in a *.bcc.html artifact, found by walking the root and skipping hidden directories, node_modules, dist and build. A directory the walk cannot open stops that branch and nothing else; bcc ls names it at the end rather than losing every canvas already found.

fence

The bcc fence

A fence is how documentation stops lying: point it at the canvas, and the sheet is drawn where the fence stands every time the file is built or previewed. The canvas stays the single source, and the picture can no longer fall behind it.

orders.md
```bcc
../canvases/order-fulfillment.bcc.json
```

what the fence draws

The Order Fulfillment canvas as a rendered sheet

One path, resolved relative to the markdown file holding it. Nothing else goes in the fence — no JSON, no options. Everywhere the fence is not drawn, the path is what a reader sees, which is why it holds a pointer rather than a canvas.

Two adapters draw it — the remark plugin when a site builds, the VS Code extension while you write — over one shared contract, with the same renderer inlined into both: a fence means the same thing on both, and the sheet it draws cannot drift from the one the editor exports. A fence that cannot be drawn leaves a visible placeholder saying why, never a blank, and the build or preview carries on.

remark

The remark plugin

One plugin covers every site generator built on unified. Two lines for Astro; two paragraphs for Docusaurus, because two of its choices fight raw HTML and inline styles. Install this repo — there is no registry package; #<sha> pins a commit:

$ npm i github:mitchellvanw/bc-canvas-editor

Astro

astro.config.mjs
import remarkBcc from 'bc-canvas-editor/remark';

export default defineConfig({ markdown: { remarkPlugins: [remarkBcc] } });

Docusaurus

Docusaurus compiles both .md and .mdx through MDX, which fails the build on a raw HTML node unless rehype-raw is in the pipeline. And it renders through React, whose server pass escapes the text inside a <style> element — an inlined stylesheet arrives mangled and the sheet draws in Times. So the CSS comes from a file instead:

docusaurus.config.js
// inside the docs/blog preset options
remarkPlugins: [[remarkBcc, { css: 'imported' }]],
rehypePlugins: [[rehypeRaw, { passThrough: ['mdxjsEsm', 'mdxFlowExpression',
  'mdxJsxFlowElement', 'mdxJsxTextElement', 'mdxTextExpression'] }]]
src/css/custom.css
@import 'bc-canvas-editor/sheet.css';

Anywhere else

Two rules. Raw HTML has to survive the pipeline — remark-rehype and rehype-stringify both take allowDangerousHtml: true — and the sheet's CSS has to reach the page one of two ways:

css what it does when
'inline' (default) a <style> in the page, once, ahead of the first fence one or two pages; nothing to configure
'imported' nothing — you import bc-canvas-editor/sheet.css React-rendered sites, and fences on many pages: the fonts are ~190 KB and a stylesheet is fetched once

The sheet brings its own fonts, its own reset and its own design tokens, all under one .bcc-canvas wrapper — it neither picks up your site's styles nor pushes anything onto the page around it. When a fence cannot be drawn, the placeholder lands in the page and the plugin puts a warning on the VFile; escalating is your site's call, through its own fail-on-warn. root is the other option the plugin takes: paths never resolve outside it, and it defaults to the directory the build runs in.

vscode

The VS Code extension

The extension puts the drawn sheet in VS Code's built-in markdown preview, live: edit the canvas, and every preview holding a fence to it redraws — including a fence pointing at a file you have not written yet, which heals the moment you write it.

There is no marketplace listing. Build a .vsix from a checkout of the repo and install it by hand:

$ cd vscode && npx --yes @vscode/vsce package --no-dependencies
$ code --install-extension bc-canvas-fence-0.0.1.vsix

A fence that cannot be drawn gets the same visible placeholder as everywhere else; the full detail, which names paths on your machine, goes to an output channel instead — BC Canvas: Show fence log in the command palette. A problem is reported once, not once per keystroke.

Where it does not reach

  • Notebook cells. The notebook markdown renderer runs in a webview with no filesystem; a bcc fence there stays a code block.
  • Web hosts (vscode.dev, github.dev) have no filesystem for a synchronous render to read, so the extension does not load there.
  • A file opened outside any workspace folder resolves against its own directory — a pointer beside it reads, ../ does not.
mcp

The MCP server & plugin

The server's whole job is getting a canvas into a conversation, as prose. It never writes — changing files is bcc's job — and that split is the design: reading is a server, writing is a command line, and the plugin is what knows the choreography. stdio only; nothing leaves the machine.

Install

The server ships inside the bc-canvas plugin, together with three skills and a reviewer agent. There is nothing to build — the plugin carries the server ready to run, and Node is its only requirement:

/plugin marketplace add mitchellvanw/bc-canvas-editor
/plugin install bc-canvas@bc-canvas-editor

The plugin does not carry bcc; set up the npx alias in the project you are working in.

What the server offers

  • bcc_read_canvas — one canvas as prose: the sheet in words, a third to a half shorter than the file. It reads a .bcc.json or the canvas embedded in a .bcc.html, and brings an older file up to date on the way through.
  • bcc_explain — what a section is for, in the ddd-crew's own questions, with the vocabulary it draws on and a row to calibrate against.
  • Resources — every canvas under the root, at bcc://canvas/<path>. Attach one from the host's own UI to give a conversation a context to work against.

What the plugin adds

The facilitation layer, findable by typing / and the name:

  • canvas-workshop — a facilitated session. The model asks, one section at a time; you answer; the sheet fills in your words, and what you defer lands under Open questions instead of staying silently blank.
  • draft-canvas-from-code — a draft drawn from what the code shows, handed back for correction. The judgments a codebase cannot answer arrive as open questions rather than invented rows.
  • draft-canvas-from-docs — the same draft from the specs, ADRs and glossaries instead. A document can decide what code cannot, so those rows arrive quoted; two documents that disagree arrive as an open question naming both.
  • canvas-reviewer — an agent that reviews by asking: it names what is missing or thin and puts the open questions back to you, answering none of them.

Claude Code, and Claude Desktop

In Claude Code the plugin is the whole setup: the server's root defaults to the project the session started in, and bcc runs over Bash like any other command. Claude Desktop has no shell, so it has no bcc — Desktop gets reading and explaining, and the skills cannot write there. Desktop also starts servers at the filesystem root, so --root is required — the plugin's own server entry leaves it out and is refused at launch rather than walking your disk. Connect the server yourself, naming the directory your canvases live under:

claude_desktop_config.json
{
  "mcpServers": {
    "bc-canvas": {
      "command": "node",
      "args": [
        "/path/to/bc-canvas-editor/mcp/dist/server.js",
        "--root", "/path/to/your-project"
      ]
    }
  }
}

--root is the only directory the server reads — a path that resolves outside it is refused, symlinks followed out of it included — and it is fixed for the life of the config, whichever project you happen to be standing in.

Conflicts

There is no conflict check — no mtime, no revision hash. Canvases are committed files, so git is already the conflict detector and git is already the undo; anything here would be a second, weaker history. The rest — protocol revision, development setup, what the resource listing leaves out and why — is in mcp/README.md.

attribution

The Bounded Context Canvas is by the ddd-crew, licensed CC BY 4.0. These docs just explain the paper.