{"id":50714184,"url":"https://github.com/benelser/docent","last_synced_at":"2026-06-09T17:32:22.607Z","repository":{"id":359836765,"uuid":"1246855560","full_name":"benelser/docent","owner":"benelser","description":"Point it at anything — a codebase, a PR, an essay, a paper, a URL — and get back a narrated, animated film that argues for what it explains. A closed grammar of explanation rendered by a coding agent, judged before it ships.","archived":false,"fork":false,"pushed_at":"2026-06-04T20:41:12.000Z","size":81942,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-04T21:26:39.332Z","etag":null,"topics":["agent-skills","ai-agent","apm","apm-plugin","claude-code","codex","codex-plugin","depth-review","developer-tools","explainer","explainer-engine","explainer-videos","remotion"],"latest_commit_sha":null,"homepage":"https://github.com/benelser/docent","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benelser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-22T16:21:39.000Z","updated_at":"2026-06-04T20:41:17.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/benelser/docent","commit_stats":null,"previous_names":["benelser/docent"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/benelser/docent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benelser%2Fdocent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benelser%2Fdocent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benelser%2Fdocent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benelser%2Fdocent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benelser","download_url":"https://codeload.github.com/benelser/docent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benelser%2Fdocent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34118751,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-09T02:00:06.510Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent-skills","ai-agent","apm","apm-plugin","claude-code","codex","codex-plugin","depth-review","developer-tools","explainer","explainer-engine","explainer-videos","remotion"],"created_at":"2026-06-09T17:32:20.356Z","updated_at":"2026-06-09T17:32:22.526Z","avatar_url":"https://github.com/benelser.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# docent\n\n\u003e **A visualization rendering engine for explanation itself.**\n\u003e\n\u003e A closed grammar of cognitive moves — `frame`, `structure`, `tension`,\n\u003e `quantities`, `recap` — rendered by a deterministic Remotion pipeline.\n\u003e You author a JSON film spec. The engine renders an HD MP4 that *argues*\n\u003e for what it explains.\n\u003e\n\u003e **v3.0 is the framework/implementation split.** `@docent/kit` is the\n\u003e framework. `@docent/core` is the 29-scene default implementation. A\n\u003e third party can ship `@theirorg/docent-*` plugins against the same\n\u003e public protocol `@docent/core` uses — no fork.\n\n[![release](https://img.shields.io/github/v/release/benelser/docent?label=release\u0026color=32d287)](https://github.com/benelser/docent/releases)\n[![corpus](https://img.shields.io/badge/corpus-5%2F5%20PASS-32d287)](#the-corpus)\n[![architecture](https://img.shields.io/badge/architecture-v3.0%20framework%2Fimpl-6ea8fe)](#the-architecture)\n[![license](https://img.shields.io/badge/license-MIT-c0c5cf)](LICENSE)\n\n## Table of contents\n\n- [Watch docent on four real subjects](#watch-docent-on-four-real-subjects)\n- [What changed in v3.0](#what-changed-in-v30)\n- [Install](#install)\n- [Quick start](#quick-start)\n- [Plugin authoring](#plugin-authoring) — *ship `@theirorg/docent-*`*\n- [The architecture](#the-architecture)\n- [The 29 canonical scenes](#the-29-canonical-scenes)\n- [CLI reference](#cli-reference)\n- [Versioning](#versioning)\n- [Contributing](#contributing)\n\n---\n\n## Watch docent on four real subjects\n\nFour films, four domains, four agent-authored cascades through the same\nclosed grammar. **No film below shares a template with any other** —\nthe agent picked the scenes, named the trade-off, committed to the\ntakeaway. The engine just rendered.\n\nHover for motion, click for the full HD mp4.\n\n|  |  |\n|---|---|\n| [![docent reviewing its own architecture](docs/stills/docent-self-preview.gif)](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/docent-self.mp4) | [![OpenClaw — one local daemon, twenty-two channels](docs/stills/openclaw-ar-preview.gif)](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/openclaw-ar.mp4) |\n| **▶ docent** *— reviewing its own architecture* — 11 min · 52 MB · [▶ play full HD](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/docent-self.mp4)\u003cbr/\u003e`frame · prior-art · structure · progression · compare · tension · quantities · recap` | **▶ OpenClaw** *— one local daemon, twenty-two channels* — 12 min · 80 MB · [▶ play full HD](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/openclaw-ar.mp4)\u003cbr/\u003e`frame · prior-art · structure · walkthrough · structure · tension · quantities · recap` |\n| [![The Lethal Trifecta](docs/stills/lethal-trifecta-blog-preview.gif)](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/lethal-trifecta-blog.mp4) | [![Let the Barbarians In](docs/stills/arxiv-2512-14806-preview.gif)](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/arxiv-2512-14806.mp4) |\n| **▶ The Lethal Trifecta** *— Simon Willison's essay on agent security* — 12 min · 62 MB · [▶ play full HD](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/lethal-trifecta-blog.mp4)\u003cbr/\u003e`frame · structure · passage · walkthrough · quantities · compare · tension · big-idea · recap` | **▶ Let the Barbarians In** *— a recent arXiv paper, fetched as PDF* — 11 min · 56 MB · [▶ play full HD](https://github.com/benelser/docent/releases/download/v3.0.0-rc.0/arxiv-2512-14806.mp4)\u003cbr/\u003e`frame · compare · structure · quantities · tension · probe · big-idea · recap` |\n\nEach film went **survey → treatment → spec → judge → render** through\nthe same engine. The grammar is what's shared. The argument is what\neach film commits to.\n\n\u003e **v3.0-rc.0 re-renders.** Every film above is freshly rendered through\n\u003e `@docent/cli` against the new `@docent/kit` + `@docent/core` architecture\n\u003e (full HD, full Kokoro narration, AAC 48 kHz). The §10 acceptance test —\n\u003e a third-party plugin pack rendering end-to-end without forking `@docent/core` —\n\u003e is green. The 18-item stabilization sprint that landed the new architecture\n\u003e is captured in [`docs/design/v3-stabilization.COMPLETE.md`](docs/design/v3-stabilization.COMPLETE.md).\n\n---\n\n## What changed in v3.0\n\n`v2.5.x` was a monolithic Remotion app: a single `engine` package, a 29-arm\nswitch in `Film.tsx`, a hand-written `film.schema.json`, every TTS\nprovider and preset hard-coded inside the bin. The monolith was retired\nin v3.0-rc.0 — the codebase ships only the kit/core/cli split below.\n\n`v3.0` carves that monolith into:\n\n- **`@docent/kit`** — the framework. Zero opinions, zero implementations.\n  Owns the plugin protocols (`ScenePlugin`, `PresetPlugin`, `FeaturePlugin`,\n  `TtsProviderPlugin`), the registries, the spec validator, the cascade\n  orchestrator, the Remotion bindings, **and the depthcheck + judge\n  contract every scene must honor**.\n- **`@docent/core`** — the default implementation. The 29 canonical scene\n  plugins, 6 presets, the Kokoro TTS adapter, the default narration and\n  audio-rhythm features. Depends on `@docent/kit` and registers every\n  default through the framework's public API. **There is no private\n  path.** A third-party plugin pack has exactly the same powers and\n  constraints as `@docent/core`.\n- **`@docent/cli`** — the thin `docent` binary. Loads `@docent/core` by\n  default plus any `docent.config.ts` your project ships.\n- **`@docent/agent`** — the existing skill / survey / prompt layer\n  (consumer of the new public CLI surface; no architectural change).\n- **`@docent/tts-*`** — per-provider TTS plugins (Kokoro local, OpenAI,\n  ElevenLabs, OpenAI-compatible). Each ships as a separate npm package\n  with `peerDependencies` on the underlying SDK — you only pay (in\n  bundle, install time, code) for what you install.\n\nThe acceptance test (rendered, green): `tests/example-docent-scifi/` is\na third-party plugin pack that adds a custom `holodeck` scene type and\na custom `scifi-noir` preset, registered via `docent.config.ts`, and\nrenders end-to-end through `docent build` without touching\n`@docent/core`. The framework/implementation split is real.\n\n**The scene library is open; the rendering discipline stays closed.**\nAnyone can register a scene type. What enforces quality is the\ndepthcheck + judge contract every scene must declare — not membership\nin a curated list. See [§11.5 of the strategy doc](docs/design/plugin-architecture-strategy.md)\nfor the explicit list of what's open vs. closed.\n\n---\n\n## Install\n\n```bash\nbun add @docent/kit @docent/core @docent/cli\n```\n\nOr with npm:\n\n```bash\nnpm install @docent/kit @docent/core @docent/cli\n```\n\nThe only mandatory peer is `bun` (or Node ≥ 22). Remotion handles its own\nruntime. Kokoro voice weights download on first synth.\n\nPaid TTS providers ship separately — install on demand:\n\n```bash\nbun add @docent/tts-openai openai                      # peer: openai SDK\nbun add @docent/tts-elevenlabs @elevenlabs/elevenlabs-js  # peer: elevenlabs SDK\nbun add @docent/tts-compatible                         # OpenAI-compatible endpoints\n```\n\nThe `peerDependency` model is intentional: a docent install with only\n`@docent/kit` + `@docent/core` has *zero* paid-API code in its bundle.\n\n---\n\n## Quick start\n\n### 1. Write a film spec\n\n`films/hello.json`:\n\n```jsonc\n{\n  \"meta\": {\n    \"id\": \"hello\",\n    \"title\": \"What docent is\",\n    \"subject\": \"the visualization rendering engine for explanation\",\n    \"prompt\": \"explainer\",\n    \"fps\": 30,\n    \"width\": 1920,\n    \"height\": 1080,\n    \"voice\": \"af_heart\"\n  },\n  \"style\": { \"preset\": \"engineering\" },\n  \"scenes\": [\n    {\n      \"type\": \"frame\",\n      \"id\": \"open\",\n      \"kicker\": \"DOCENT // v3.0\",\n      \"title\": \"Render an idea\",\n      \"tagline\": \"Closed grammar, deterministic pipeline, plugin protocol\",\n      \"beats\": [\n        { \"id\": \"f1\", \"narration\": \"Hand the engine a spec. Get back a film that argues for what it explains.\" }\n      ]\n    },\n    {\n      \"type\": \"recap\",\n      \"id\": \"close\",\n      \"title\": \"Three packages.\",\n      \"beats\": [\n        { \"id\": \"r1\", \"narration\": \"Kit, core, CLI. Everything else is a plugin.\" }\n      ]\n    }\n  ]\n}\n```\n\n### 2. Render it\n\n```bash\ndocent build hello --scale 0.5\n```\n\nThe cascade runs in four cached stages — `validate → tts → render` —\nand writes `out/hello.mp4`. Re-run; only changed beats re-synthesize and\nre-render.\n\n### 3. Watch\n\n```bash\nopen out/hello.mp4\n```\n\n---\n\n## Plugin authoring\n\n\u003e **The \"stranger could ship a plugin\" bar.** This section is the\n\u003e contract: an external developer reads it once and ships\n\u003e `@theirorg/docent-*` in an hour.\n\nA plugin is a tagged value. The framework's `engine.use(plugin)` sniffs\n`plugin.kind` and dispatches to the right registry. Authors do not\nsubclass anything.\n\n```ts\ntype PluginKind = 'scene' | 'preset' | 'tts' | 'feature';\n\ninterface PluginBase {\n  readonly name: string;     // '@theirorg/docent-finance/ohlc'\n  readonly version: string;  // semver\n  readonly kind: PluginKind;\n}\n```\n\nSeven runnable reference packs live under [`tests/example-docent-*/`](tests/) — each is a complete `@example/docent-*` package with a working film, ready to fork. Every documented hook in the framework has a demo:\n\n| Pack | What it demonstrates | Film |\n|---|---|---|\n| [`tests/example-docent-scifi/`](tests/example-docent-scifi/) | A third-party **scene + preset** pack (one `holodeck` scene, one `scifi-noir` preset) — the §10 acceptance test that proves you can extend the surface without forking `@docent/core`. | `scifi-demo.json` |\n| [`tests/example-docent-finance/`](tests/example-docent-finance/) | A **vertical scene pack** — two new `scene.type` discriminators (`ohlc`, `candlestick`), each with its own schema branch, depth rules, judge dimensions, and `scene-fit` cues + signals. The pattern for `docent-scenes-\u003cvertical\u003e`. | `finance-primer.json` |\n| [`tests/example-docent-preset-brand/`](tests/example-docent-preset-brand/) | A **brand preset pack** with `acme` (deep navy + gold) and `acme-dark` that **composes via `extends: \"acme\"` (R4)** — overrides only `bg.*`, inherits the rest. | `acme-quarterly.json`, `acme-quarterly-dark.json` |\n| [`tests/example-docent-feature-captions/`](tests/example-docent-feature-captions/) | A **feature plugin** writing a sidecar SRT next to the mp4 via the `FeaturePlugin.afterRender` hook. The pattern for transcripts, chapter markers, lower-thirds. | `captions-demo.json` |\n| [`tests/example-docent-feature-microsyntax/`](tests/example-docent-feature-microsyntax/) | A **feature plugin** using `FeaturePlugin.preprocessSpec` **(R6)** to expand inline directives — `@@@auto-id`, `@@@reveal-all`, `@@@beat-stride`. Author shorthand compiles to canonical spec before validation. | `microsyntax-demo.json` |\n| [`tests/example-docent-feature-modifier/`](tests/example-docent-feature-modifier/) | A **feature plugin** using `FeaturePlugin.registerModifiers` **(R3)** to advertise inline directives across all three tiers — film (`kicker-prefix`), scene (`highlight`), beat (`pace-override`). | `modifier-demo.json` |\n| [`tests/example-docent-tts-silence/`](tests/example-docent-tts-silence/) | A **community-built `TtsProviderPlugin`** — synthetic WAV silence sized to text length. The starter for any real TTS adapter (replace `synth` with the API call). | `silence-demo.json` |\n\nTreat **scifi** as the simplest fork point. Reach for the others when your plugin shape matches theirs.\n\n### The four plugin kinds\n\n| `kind` | What it ships | Example |\n|---|---|---|\n| `scene` | A new `scene.type` discriminator with its own schema branch, Remotion component, depth rules, and judge dimensions. | `@example/docent-scifi/holodeck`, `@example/docent-finance/{ohlc,candlestick}` |\n| `preset` | A new visual register — design tokens, visualization style. Composes via optional `extends: \u003cbase\u003e` (R4). | `@example/docent-scifi/scifi-noir`, `@example/docent-preset-brand/{acme, acme-dark}` |\n| `feature` | Cross-cutting concerns (captions, watermarks, music). Touches multiple registries; can write sidecars via `afterRender`. | `narrationFeature` in `@docent/core`, `@example/docent-feature-captions` |\n| `tts` | A speech provider implementing `TtsProvider`. | `@docent/tts-openai`, `@docent/tts-elevenlabs`, `@docent/tts-compatible` |\n\n### `ScenePlugin` — the load-bearing shape\n\n```ts\nimport type { ScenePlugin } from '@docent/kit';\n\nexport const holodeckPlugin: ScenePlugin\u003cHolodeckSceneSpec\u003e = {\n  kind: 'scene',\n  name: '@theirorg/docent-scifi/holodeck',\n  version: '0.1.0',\n\n  // The discriminator value in spec.scenes[].type. Globally unique\n  // within the active engine — conflicts hard-fail at engine.use().\n  sceneType: 'holodeck',\n\n  // Which cognitive cluster this scene belongs to — drawn from the\n  // CLOSED 7-cluster taxonomy. `null` is reserved for chrome scenes\n  // (frame, recap) that bracket the film without a cognitive move.\n  cluster: 'experience',\n\n  // JSON Schema fragment for this scene type. The kit assembles the\n  // full discriminated union at Engine.schema() call time — no hand-\n  // written film.schema.json.\n  schema: holodeckSchema,\n\n  // The Remotion-compatible React component that renders the scene.\n  component: HolodeckSceneComponent,\n\n  // Optional structural validation beyond JSON Schema. Empty array = clean.\n  validate: (scene) =\u003e { /* … */ return []; },\n\n  // depthcheck rules contributed by this scene type. The framework\n  // refuses to render anything that doesn't declare a contract.\n  depthRules: [\n    {\n      id: 'holodeck-needs-anchor',\n      severity: 'warn',\n      check: (scene) =\u003e scene.title ? null : { path: 'title', message: '…' },\n    },\n  ],\n\n  // Judge dimensions contributed by this scene type.\n  judgeDimensions: [/* … */],\n\n  // R5 cross-bind: scenes declare what they need from the active TTS.\n  // The engine checks at spec-resolution time (warn / hard-fail\n  // per meta.tts.strict).\n  requiresTtsCapabilities: { nativeAlignment: 'word' },\n};\n```\n\nSee [`packages/kit/src/protocols.ts`](packages/kit/src/protocols.ts) for\nthe full, JSDoc'd type surface and [`tests/example-docent-scifi/src/scenes/holodeck/index.ts`](tests/example-docent-scifi/src/scenes/holodeck/index.ts)\nfor the working example.\n\n### The cognitive-cluster taxonomy (closed list)\n\nEvery `ScenePlugin` declares its cluster from this **closed** list of 7.\nThe taxonomy is what makes the recommender (`docent scene-fit`)\ndeterministic even as the library grows. Adding a new cluster is a\nmajor version bump of `@docent/kit`.\n\n| Cluster | The cognitive move |\n|---|---|\n| `connection` | Relationships, dependencies, links between entities (graph, tree, dependency). |\n| `time` | Temporal sequencing, before/after, progressions, timelines, epochs, phases. |\n| `flow` | Control flow, data flow, state transitions, pipelines, cycles, feedback loops. |\n| `comparison` | Side-by-side options, trade-offs, scoring, ranking, measurements, charts on real axes. |\n| `categorization` | Taxonomies, set membership, boundaries between kinds, matrices. |\n| `experience` | The human angle — a journey, a perception, a walk through what it feels like. |\n| `narrative` | Story, argument, commitment, the rhetorical \"we chose X because of Y.\" |\n\nChrome-only scenes (`frame`, `recap`) declare `cluster: null` — they\nbracket the film but perform no cognitive move. See\n[`packages/kit/src/taxonomy/cognitive-clusters.ts`](packages/kit/src/taxonomy/cognitive-clusters.ts)\nfor the canonical definition.\n\n### The depthcheck + judge contract — what every scene must honor\n\nThe framework refuses to render anything that doesn't declare its\ndepthcheck rules and judge dimensions. **This contract is what enforces\nquality across the open library — not membership in a curated list.**\n\n- `depthRules: DepthRule\u003cTSpec\u003e[]` — rules that grade the *spec*. Each\n  rule has an `id`, `severity` (`error` | `warn`), and a `check(scene,\n  ctx)` function returning a finding or `null`. Rules run as part of\n  `docent depthcheck` and gate the cascade.\n- `judgeDimensions: JudgeDimension[]` — dimensions the adversarial\n  judge grades each film on. Standard dimensions (carried over from\n  v2.5.x): `triage`, `where-wrong`, `tests-prove-it`, `the-numbers`,\n  `the-trade-off`, `verdict-adjudicates`, `takeaway-earned`. A scene\n  plugin can add a dimension specific to its move.\n\nA film the judge rejects does not ship. The loop reliably lifts\nfirst-draft specs by ~7 points on a 30-point scale (carried over from\nv2.5.x; v3.0 keeps the same contract).\n\n### Project-side registration: `docent.config.ts`\n\nThe CLI walks up from `cwd` looking for a `docent.config.ts` (or `.js`\nor `.json`). When found, its `plugins` array is registered on top of\n`@docent/core`'s defaults. Conflicts (same `sceneType`, same\n`presetName`) hard-fail at `engine.use()` time with both plugin names\nsurfaced.\n\n```ts\n// docent.config.ts\nimport scifi from '@theirorg/docent-scifi';\nimport { ohlcPlugin } from '@theirorg/docent-finance';\n\nexport default {\n  plugins: [...scifi, ohlcPlugin],\n};\n```\n\nA plugin pack typically exports an array of plugins as its default\nexport — see [`tests/example-docent-scifi/src/index.ts`](tests/example-docent-scifi/src/index.ts):\n\n```ts\nimport type { Plugin } from '@docent/kit';\nimport { holodeckPlugin } from './scenes/holodeck';\nimport { scifiNoirPreset } from './presets/scifi-noir';\n\nconst plugins: ReadonlyArray\u003cPlugin\u003e = [holodeckPlugin, scifiNoirPreset];\nexport default plugins;\n```\n\n### The `peerDependency` model for paid TTS adapters\n\nA paid TTS plugin declares the underlying SDK as a `peerDependency`,\nnot a hard `dependency`:\n\n```jsonc\n// @docent/tts-elevenlabs/package.json\n{\n  \"name\": \"@docent/tts-elevenlabs\",\n  \"peerDependencies\": {\n    \"@docent/kit\": \"^3.0.0\",\n    \"@elevenlabs/elevenlabs-js\": \"^1.0.0\"\n  }\n}\n```\n\nA user who wants ElevenLabs runs `bun add @docent/tts-elevenlabs\n@elevenlabs/elevenlabs-js`. The SDK is not in `@docent/core`'s dep tree\nat all. **The plugin package is the entire feature flag.** This pattern\ngeneralizes to any heavy dependency a plugin may need (a custom Manim\nruntime, a remote rendering service, a font subsetter).\n\nA `TtsProviderPlugin` declares its capabilities at the type level:\n\n```ts\ninterface TtsCapabilities {\n  nativeAlignment: 'word' | 'character' | 'chunk' | 'none';\n  streaming: boolean;\n  ssml: boolean;\n  voiceCloning: boolean;\n  local: boolean;\n}\n```\n\nScenes that need word-level alignment (e.g. `passage`) declare it via\n`requiresTtsCapabilities`; the engine refuses to schedule incompatible\ncombinations at spec-resolution time, not five minutes into a render.\n\n### Publishing checklist\n\n1. Name the package `@theirorg/docent-*` (the `docent-` prefix is the\n   convention — `docent-scenes-finance`, `docent-preset-brand`,\n   `docent-tts-azure`).\n2. Set `\"peerDependencies\": { \"@docent/kit\": \"^3.0.0\" }`. Pin the\n   `@docent/kit` major; minor/patch float.\n3. Export your plugins as the default export, or expose them\n   individually as named exports.\n4. Declare every `ScenePlugin`'s `depthRules` and `judgeDimensions` —\n   the framework refuses to render scenes without a contract.\n5. Publish under MIT (or a compatible OSS license). The reference\n   implementation is MIT.\n\nSee [`docs/design/plugin-architecture.md`](docs/design/plugin-architecture.md)\nfor the full design — verbatim interfaces, lifecycle hooks, and the\nforward-compat surface for R3 (custom modifiers), R4 (preset\ncomposition), and R6 (inline microsyntax).\n\n---\n\n## The architecture\n\n```\n                       films/\u003cid\u003e.json\n                              │\n                              ▼\n┌────────────────────────────────────────────────────────────────────┐\n│  @docent/agent — skills, surveys, prompts (LLM-author layer)       │\n└────────────────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n┌────────────────────────────────────────────────────────────────────┐\n│  @docent/cli — docent build / validate / depthcheck / hermetic     │\n└────────────────────────────────────────────────────────────────────┘\n                              │\n                              ▼\n┌────────────────────────────────────────────────────────────────────┐\n│  @docent/kit — THE FRAMEWORK (zero opinions)                       │\n│                                                                    │\n│    class Engine {                                                  │\n│      use(plugin): this              // sniff plugin.kind           │\n│      scenes / presets / tts / features / modifiers                 │\n│      schema(): JSONSchema7          // computed from registry      │\n│      validate(spec): Issue[]                                       │\n│      render(spec, opts): Promise\u003cRenderResult\u003e                     │\n│    }                                                               │\n│                                                                    │\n│  Protocols: PluginBase, ScenePlugin, PresetPlugin, FeaturePlugin,  │\n│             TtsProviderPlugin, ModifierRegistry (R3 stub)          │\n│  Cascade: validate → tts → render. Remotion bindings.              │\n└────────────────────────────────────────────────────────────────────┘\n                              ▲\n              ┌───────────────┼───────────────────┐\n              │               │                    │\n              ▼               ▼                    ▼\n┌──────────────────────┐ ┌───────────────┐ ┌────────────────────────┐\n│  @docent/core        │ │ @docent/tts-* │ │  @theirorg/docent-*    │\n│    29 ScenePlugin    │ │   kokoro      │ │    scenes (e.g. ohlc)  │\n│    6 PresetPlugin    │ │   openai      │ │    preset-fintech      │\n│    narration feat    │ │   elevenlabs  │ │    feature-captions    │\n│    audio-rhythm feat │ │   compatible  │ │                        │\n└──────────────────────┘ └───────────────┘ └────────────────────────┘\n```\n\nThe discipline: **`@docent/core` is one consumer of `@docent/kit`'s\npublic API. It is never the privileged path.** A third-party plugin\npack has exactly the same powers and constraints.\n\nFor the deep dive — sequencing, protocols verbatim, forward-compat for\nR3/R4/R6, and the locked-in commitments — read\n[`docs/design/plugin-architecture-strategy.md`](docs/design/plugin-architecture-strategy.md).\n\nThe architectural DAG (which depends on which) lives at\n[`docs/design/plugin-architecture-dag.md`](docs/design/plugin-architecture-dag.md).\n\n---\n\n## The 29 canonical scenes\n\nThe opinionated default `@docent/core` ships. Pick the scene whose\n*native shape* is your move — don't force-fit. A quoted text belongs in\n`passage`, not `closeup`; an image in `figure`.\n\n| Scene | Cluster | Reach for this when… |\n|---|---|---|\n| `frame` | *(chrome)* | …you need to set up the subject — kicker, title, tagline, footnote. |\n| `recap` | *(chrome)* | …you need to land the takeaway — one held sentence the viewer leaves with. |\n| `structure` | connection | …the parts of the subject relate in a graph (entails, causes, depends-on). |\n| `tree` | connection | …relationships are hierarchical — a taxonomy, an org chart, a tree. |\n| `map` | connection | …entities are positioned by something *other* than time (a landscape, a territory). |\n| `walkthrough` | connection | …one instance unfolds step by step — a trace through a specific path. |\n| `progression` | time | …stages along a path — linear, cycle, braided, or iterate. |\n| `timeline` | time | …explicit epochs / phases / dates on a real time axis. |\n| `mechanism` | flow | …how the *machine* works — gears, ports, the inside view of the process. |\n| `causal-loop` | flow | …feedback loops, reinforcing / balancing dynamics, system archetypes. |\n| `diff` | flow | …before vs. after — what changed in a code or design transformation (PR films). |\n| `compare` | comparison | …two or three options held side by side. |\n| `quantities` | comparison | …a number that must *land* — figure cards, animated counters. |\n| `chart` | comparison | …data on real axes — a curve, bars that grow, a point on a curve. |\n| `probe` | comparison | …vary one input, follow the consequence (sensitivity, what-if). |\n| `prior-art` | comparison | …survey of what came before — name what others tried, name what's new here. |\n| `landscape` | comparison | …a 2D positioning of options against two axes. |\n| `venn` | comparison | …set membership — overlap and disjoint regions. |\n| `tension` | categorization | …the trade-off, the failure mode, where the design breaks. |\n| `closeup` | experience | …annotate one code artifact — a function, a struct, a config block. |\n| `journey-map` | experience | …a perception arc — how something *feels* across stages. |\n| `passage` | narrative | …annotate a plain text by phrase — a poem, prose, a primary source. |\n| `figure` | narrative | …annotate a still image by region — a painting, a map, a photograph. |\n| `epigraph` | narrative | …a single quote held alone, attribution beneath. |\n| `provocation` | narrative | …the question or claim the film opens with — to pull tension forward. |\n| `objection` | narrative | …name the counter-argument the film must answer. |\n| `concession` | narrative | …grant what's true on the other side, before pressing yours. |\n| `big-idea` | narrative | …the held sentence the rest of the film *earned*. |\n| `demonstrate` | narrative | …play the phenomenon itself — an audio clip, an interaction, the thing in motion. |\n\n29 scenes. 7 clusters. The taxonomy is closed; the library is open —\nship a 30th scene in `@theirorg/docent-scenes-x` if your domain has a\nmove docent didn't.\n\n---\n\n## CLI reference\n\n`@docent/cli` is intentionally thin — every subcommand is a few lines\non top of `@docent/kit`'s public Engine surface.\n\n```\ndocent — render explanatory films via @docent/kit.\n\nUSAGE\n  docent \u003ccommand\u003e [args]\n\nCOMMANDS\n  build \u003cfilm-id\u003e      Render a film to MP4 at out/\u003cfilm-id\u003e.mp4.\n  validate \u003cfilm-id\u003e   Structurally validate a film spec via engine.validate().\n  depthcheck \u003cfilm-id\u003e Aggregate every plugin's depthRules over a film spec.\n  render-check \u003cid\u003e    Render at low scale + assert every narrated scene\n                       evolves visibly across its window.\n  assert \u003cfilm-id\u003e     Visual regression: diff per-scene midpoint frames\n                       against golden/\u003cid\u003e/. --update to (re)capture.\n  hermetic             Render the 4 gallery fixtures end to end.\n  help                 Print this usage and exit.\n\nBUILD FLAGS\n  --scale \u003cn\u003e          Render scale (0.25, 0.5, 1). Default: 1.\n  --concurrency \u003cn\u003e    Render frame concurrency. Default: Remotion's auto.\n  --still \u003cs\u003e          Render a single still at second offset s.\n  --skip-tts           Skip the TTS stage — produces a silent mp4.\n  --output-dir \u003cp\u003e     Override the output directory.\n  --films-dir \u003cp\u003e      Override the films/ directory.\n  --project-root \u003cp\u003e   Override the project root (config + entry generation).\n\nEXAMPLES\n  docent build linear-algebra --scale 0.5\n  docent validate kubernetes-pr\n  docent depthcheck euclid-primes\n  docent render-check openclaw-ar\n  docent assert docent-self --update\n  docent assert docent-self --threshold 0.02\n  docent grammar-check\n  docent scene-fit list\n  docent scene-fit recommend linear-algebra --top 8\n  docent doctor\n  docent hermetic --scale 0.5\n```\n\n### `docent build`\n\nRuns the full cascade: validate → tts (Kokoro by default) → render.\nPer-beat audio is mounted into the Remotion composition; per-stage\ncaching means re-runs only synthesize and render what changed.\n\n### `docent validate`\n\nRuns `engine.validate(spec)` — JSON Schema (the computed union of every\nregistered scene's schema fragment) plus each plugin's structural\n`validate(scene, ctx)` hook. Exit code is non-zero on any `error`-level\nissue.\n\n### `docent depthcheck`\n\nAggregates every plugin's `depthRules` over the spec and reports\nfindings. The cascade gates on this in CI — a depth regression is a\nhard fail. The judge surface (the 7-dimension grader) runs on top of\ndepthcheck; see [`docs/design/plugin-architecture-strategy.md`](docs/design/plugin-architecture-strategy.md)\n§4.2.\n\n### `docent render-check`\n\nHolds the **visual-integrity invariant**:\n\n\u003e A film with narration cannot ship blank scene bodies.\n\nThe build path is the kind that can fail silently — audio plays, chrome\nrenders, but a scene's body content (nodes, edges, panels, quantities)\nstays at frame-0 state because a reveal-gate never fires. `render-check`\ncatches it: builds at low scale + `--skip-tts`, then per scene with\nnarration samples three frames at 10% / 50% / 90% of the window, hashes\neach, and **fails if a scene's three samples are pixel-identical**.\n\n```bash\ndocent render-check openclaw-ar\n```\n\nExit code 0 on full pass, 4 when at least one narrated scene is static.\nA per-film sidecar (`out/.render-check-\u003cid\u003e/check.json`) records every\nsample for follow-up forensics.\n\n### `docent assert`\n\nHolds the **visual-regression invariant**:\n\n\u003e A film that built last time must still look like itself today.\n\nWhere `render-check` guards *within a single render* (every scene must\nevolve), `assert` guards *across renders* — today's frames vs. the\ngoldens committed alongside the spec. The killer case is the Cassini\n\"rings overlap saturn\" bug: a rendering regression that ships silently\nbecause no automated step compares this render to the last good one.\n\nPer scene, one key frame at the scene midpoint is extracted from\n`out/\u003cid\u003e.mp4` and diffed against the golden in\n`golden/\u003cid\u003e/scene-\u003cNN\u003e-\u003ctype\u003e.jpg`. The diff metric is **mean absolute\npixel difference** over rgb24 bytes, normalized to `[0, 1]` — no\n`sharp`, no `canvas`, just `ffmpeg` shell + a `Buffer` walk. Default\nthreshold is `0.05` (5%).\n\n```bash\n# 1. First time, after the film renders cleanly: capture goldens.\ndocent build docent-self\ndocent assert docent-self --update          # writes golden/docent-self/\n\n# 2. On every subsequent build, diff today's frames against the goldens.\ndocent build docent-self\ndocent assert docent-self                   # exits 2 on regression\n\n# 3. Tune the bar for a tightly-controlled film.\ndocent assert docent-self --threshold 0.02\n```\n\nScene midpoints come from `out/.render-check-\u003cid\u003e/check.json` when it\nexists (the canonical sidecar `render-check` already writes); when it\ndoesn't, the schedule is rebuilt from the spec on the fly. Commit\n`golden/\u003cid\u003e/` alongside `films/\u003cid\u003e.json` — the goldens are part of\nthe film's contract.\n\nExit codes: **0** pass (or capture succeeded), **1** missing inputs\n(no mp4 — run `docent build` first), **2** at least one scene's diff\nexceeds the threshold *or* the run has a missing golden, **4** ffmpeg\nextraction or decode error. A `golden/\u003cid\u003e/assert.json` sidecar records\nevery per-scene diff for follow-up forensics.\n\n### `docent doctor`\n\nThe **plugin conformance** check. Reads the engine registry (core + any\nplugins from `docent.config.ts`) and grades every registered plugin\nagainst the protocol contract.\n\n```bash\ndocent doctor          # human-readable\ndocent doctor --json   # machine-readable for CI gates\n```\n\nReports:\n\n- **ERROR** — structural violations that will fail renders (missing\n  `sceneType`, bad `cluster`, missing `schema` or `component`, bad\n  signal weights, registry conflicts). Exit code **6**.\n- **WARN** — valid but missing things authors want (no `cue` for\n  `scene-fit list`, no `signals` on a non-chrome scene, undeclared\n  `depthRules`/`judgeDimensions` arrays). Exit code **0** — warnings\n  are informational.\n- **INFO** — counts + cluster distribution for orientation.\n\nRun from a project root and `doctor` sees the user's plugins too —\nthe line `+2 from docent.config.ts` confirms the pack is loaded.\nThis is the first command extension authors should reach for after\n`bunx tsc --noEmit`: it answers \"did I honor the contract?\" without\nneeding to render a film.\n\n### `docent scene-fit`\n\nThe **agent-facing introspection** over the 29-scene grammar — the\nrecommender that closes the \"which scene fits which cognitive move\"\nloop. Without it, an undirected agent reflex-defaults to\n`frame / structure / compare / tension / recap` on every film, producing\ntour-shaped specs instead of arguments.\n\n```bash\n# enumerate registered scene plugins by cluster, with a \"reach for it when\" cue\ndocent scene-fit list [--json]\n\n# read analysis/\u003cid\u003e.md and recommend the top N scene types with rationales\ndocent scene-fit recommend \u003csubject-id\u003e [--top N] [--json]\n```\n\n`list` reads from the engine registry, so third-party plugins\nregistered via `docent.config.ts` surface alongside core. `recommend`\nruns a rule-based mapper (NOT an LLM call) — every signal needle in\nthe survey contributes a weighted vote toward one scene type. When the\ntop N is a subset of the default-rut five, the result raises a\n`warningOnDefault` flag prompting the author to re-read the survey for\nthe more specific primitives they may have skipped.\n\n### `docent grammar-check`\n\nThe **closed-grammar invariant** — one command, three asks:\n\n1. **Coverage** — every registered `ScenePlugin`'s `sceneType` appears\n   in at least one demo film in the cover set. A scene plugin nobody\n   ever uses is dead weight; a scene plugin nobody can use is a bug.\n2. **Taxonomy** — every registered `ScenePlugin` declares a `cluster`\n   field from the closed 7-cluster taxonomy (or `null` for chrome).\n   The recommender (`docent scene-fit`) navigates by these clusters;\n   a missing or typo'd cluster breaks scene-fit.\n3. **Pipeline** — every film in the cover set survives validate → render\n   → `render-check`. A scene plugin that can't make it through the\n   cascade end-to-end isn't usable.\n\nThe default cover set is **six small demo films** that union-cover all\n29 canonical scene types in minutes, not hours:\n`grammar-check` (15 scenes), `rhetorical-primer` (4 unique),\n`sprint-b-composition-demo` (7 unique via embeds), `causal-loop-primer`,\n`multi-region-db`, `prior-art-primer`.\n\n```bash\ndocent grammar-check\n```\n\nPer-scene status table prints the cluster tag + which film(s) exercise\nit. Exit code 0 on green; 5 on any taxonomy, coverage, or pipeline\nfailure. Third-party plugin packs registered via `docent.config.ts`\nare folded in automatically — register a `@yourorg/docent-finance/ohlc`\nscene and the next grammar-check will surface \"uncovered\" until a demo\nfilm cites it.\n\n### `docent hermetic`\n\nRenders the 4 gallery fixtures (`linear-algebra`, `kubernetes-pr`,\n`euclid-primes`, `stopping-by-woods`) end to end through the same\n`@docent/cli` path a user takes. The CI signal for \"v3.0 still ships\nthe gallery.\"\n\n### The corpus\n\nThe four gallery fixtures + the kitchen-sink test — every one passes\nthe depthcheck contract through `@docent/cli`:\n\n| Film | Subject | Domain | Verdict |\n|---|---|---|---|\n| `linear-algebra` | The dot product as the keystone operation | Math | 26 / 30 PASS |\n| `kubernetes-pr` | The Kubernetes scheduler heap refactor | Software | 26 / 30 PASS |\n| `euclid-primes` | Euclid's proof of infinitely many primes | Math proof | 23 / 30 PASS |\n| `stopping-by-woods` | A close reading of Robert Frost | Literature | 27 / 30 PASS, first try |\n| `grammar-check` | Kitchen-sink scene grammar test | Engineering | (test fixture) |\n\nSpecs live in `films/*.json`. Render any of them: `docent build \u003cid\u003e`.\n\n---\n\n## Versioning\n\nThe `@docent/*` packages move in lockstep at the major. Each package's\n`peerDependencies` pin `@docent/kit: ^X.0.0` where X is the current\nmajor. Inside a major, packages move independently.\n\n| Change | semver impact |\n|---|---|\n| Breaking a protocol in [`packages/kit/src/protocols.ts`](packages/kit/src/protocols.ts) (`ScenePlugin`, `PresetPlugin`, `FeaturePlugin`, `TtsProviderPlugin`, `ModifierRegistry`). | **major** |\n| Removing a `@docent/kit` public export. | **major** |\n| Removing the `engine.use(plugin)` API or any `Engine` method. | **major** |\n| Changing the closed cognitive-cluster taxonomy (adding or removing a cluster). | **major** |\n| Removing a canonical scene from `@docent/core`. | **major** |\n| Adding an optional field to a protocol interface. | **minor** |\n| Adding a new scene plugin to `@docent/core`. | **minor** |\n| Adding a new CLI subcommand. | **minor** |\n| Tightening a depth rule's `check` (false-positive surface narrows). | **patch** |\n| Loosening a depth rule's `check` (false-positive surface widens). | **minor** (behavioral change) |\n| Bug fixes that don't change a public type or visible behavior. | **patch** |\n\nWhat v3.0 locked in **permanently** (only recoverable through a v4):\n\n- The plugin API is public. Breaking any protocol is a breaking change.\n- Registry-based dispatch — no fast path that bypasses the registry.\n- Schema is computed from the registry — `film.schema.json` is a build\n  artifact, not a hand-written source of truth.\n- The `@docent/*` npm scope.\n- The framework/implementation split — no private path from\n  `@docent/core` into `@docent/kit` internals.\n- The open scene library — verification, if it ever ships, is layered\n  on top as a quality signal, never as a gate.\n\nSee [`docs/design/plugin-architecture-strategy.md`](docs/design/plugin-architecture-strategy.md)\n§12 for the full list.\n\n---\n\n## Contributing\n\n### To `@docent/core` — proposing a new canonical scene\n\nThe bar for landing a scene type in `@docent/core` is high: **the scene\nmust be a cognitive move, not a visual treatment.** Concretely:\n\n1. The scene's *native shape* is something existing scenes can't carry\n   without force-fit. A `treemap` is a visual treatment of `tree`; a\n   `sankey` is a distinct cognitive move (continuous flow with\n   conserved magnitude).\n2. The scene declares a `cluster` from the 7-closed list. If no cluster\n   fits, the proposal needs to extend the taxonomy — a major version\n   bump of `@docent/kit`, separately considered.\n3. The scene contributes `depthRules` that catch the *failure mode\n   specific to this move*. A `tension` scene without a named\n   trade-off fails its own depth rule.\n4. The scene contributes `judgeDimensions` if the standard 7 don't\n   capture what makes *this* scene's quality.\n5. At least one corpus film uses the scene end-to-end, and the\n   acceptance test (`docent hermetic`) is green.\n\nOpen an issue with the proposed scene's name, cluster, depth rules,\nand a paragraph on the cognitive move it makes that the existing 29\ncan't. PRs without an accepted proposal are not reviewed — the bar is\ndeliberately high.\n\n### To ship outside `@docent/core` — the easy path\n\nIf your scene is domain-specific (an OHLC chart for finance, a Sankey\nfor material flow, a UML diagram for software) — ship it as\n`@theirorg/docent-scenes-x` and skip the core PR. The plugin protocol\nis the public contract; there is no second-class citizenship.\n\n### To `@docent/kit` — protocol changes\n\n`@docent/kit` is the frozen surface. Protocol changes need a written\ncase for *why* the existing protocol can't carry the use case. The\n`FeaturePlugin` lifecycle hooks are intentionally open — most\n\"protocol change\" instincts are actually \"new optional `FeaturePlugin`\nhook\" requests, which are additive and non-breaking.\n\n---\n\n## License\n\nMIT (see [`LICENSE`](LICENSE)). All `@docent/*` packages publish under\nthe same scope; the reference plugin pack at\n[`tests/example-docent-scifi/`](tests/example-docent-scifi/) is the\nworking starter you can fork.\n\n---\n\n\u003e *\"docent gets better as it runs.\"* The judge grades every film. The\n\u003e revise loop closes the gap. The outer flywheel distills recurring\n\u003e weaknesses back into the brief. Each generation raises the floor for\n\u003e the next.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenelser%2Fdocent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenelser%2Fdocent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenelser%2Fdocent/lists"}