explainer-video 0.0.0-stage → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (150) hide show
  1. package/.claude-plugin/plugin.json +12 -0
  2. package/LICENSE +21 -0
  3. package/README.md +152 -2
  4. package/cordis.patch.yml +7 -0
  5. package/lib/index.js +35 -0
  6. package/package.json +56 -4
  7. package/plugin.json +13 -0
  8. package/skills/coursework-author/.synced-from +5 -0
  9. package/skills/coursework-author/SKILL.md +209 -0
  10. package/skills/craft-codeflow-flows/SKILL.md +116 -0
  11. package/skills/craft-codeflow-mine/SKILL.md +207 -0
  12. package/skills/craft-codeflow-plan/SKILL.md +65 -0
  13. package/skills/craft-codeflow-scene/SKILL.md +168 -0
  14. package/skills/craft-video-align/SKILL.md +109 -0
  15. package/skills/craft-video-assemble/SKILL.md +69 -0
  16. package/skills/craft-video-audio/SKILL.md +59 -0
  17. package/skills/craft-video-enrich/SKILL.md +46 -0
  18. package/skills/craft-video-plan/SKILL.md +57 -0
  19. package/skills/craft-video-publish/SKILL.md +79 -0
  20. package/skills/craft-video-render/SKILL.md +68 -0
  21. package/skills/craft-video-scene/SKILL.md +112 -0
  22. package/skills/craft-video-script/SKILL.md +80 -0
  23. package/skills/explainer-video-from-codebase/FLOWS.md +577 -0
  24. package/skills/explainer-video-from-codebase/SKILL.md +659 -0
  25. package/skills/explainer-video-from-codebase/tools/apply-flows.mjs +531 -0
  26. package/skills/explainer-video-from-codebase/tools/approve-flows.mjs +56 -0
  27. package/skills/explainer-video-from-codebase/tools/codemap-md.mjs +116 -0
  28. package/skills/explainer-video-from-codebase/tools/config/dotenv.mjs +13 -0
  29. package/skills/explainer-video-from-codebase/tools/config/json.mjs +14 -0
  30. package/skills/explainer-video-from-codebase/tools/config/redact.mjs +16 -0
  31. package/skills/explainer-video-from-codebase/tools/config/toml-lite.mjs +13 -0
  32. package/skills/explainer-video-from-codebase/tools/config/yaml-lite.mjs +18 -0
  33. package/skills/explainer-video-from-codebase/tools/excerpt-check.mjs +186 -0
  34. package/skills/explainer-video-from-codebase/tools/excerpt.mjs +116 -0
  35. package/skills/explainer-video-from-codebase/tools/flow-diagram.mjs +168 -0
  36. package/skills/explainer-video-from-codebase/tools/flow-plan.mjs +129 -0
  37. package/skills/explainer-video-from-codebase/tools/flows-md.mjs +87 -0
  38. package/skills/explainer-video-from-codebase/tools/lang/c.mjs +684 -0
  39. package/skills/explainer-video-from-codebase/tools/lang/go.mjs +673 -0
  40. package/skills/explainer-video-from-codebase/tools/lang/python-config.mjs +143 -0
  41. package/skills/explainer-video-from-codebase/tools/lang/python-deps.mjs +62 -0
  42. package/skills/explainer-video-from-codebase/tools/lang/python-stdlib.mjs +24 -0
  43. package/skills/explainer-video-from-codebase/tools/lang/python.mjs +609 -0
  44. package/skills/explainer-video-from-codebase/tools/lang/typescript.mjs +963 -0
  45. package/skills/explainer-video-from-codebase/tools/lib/decompose.mjs +102 -0
  46. package/skills/explainer-video-from-codebase/tools/lib/groups.mjs +77 -0
  47. package/skills/explainer-video-from-codebase/tools/lib/observe-coverage.mjs +68 -0
  48. package/skills/explainer-video-from-codebase/tools/lib/observe-v8.mjs +232 -0
  49. package/skills/explainer-video-from-codebase/tools/lib/observe.mjs +162 -0
  50. package/skills/explainer-video-from-codebase/tools/lib/redact.mjs +28 -0
  51. package/skills/explainer-video-from-codebase/tools/lib/sandbox.mjs +97 -0
  52. package/skills/explainer-video-from-codebase/tools/lib/test-links.mjs +28 -0
  53. package/skills/explainer-video-from-codebase/tools/lib/toml.mjs +129 -0
  54. package/skills/explainer-video-from-codebase/tools/lib/trace-c.mjs +95 -0
  55. package/skills/explainer-video-from-codebase/tools/lib/trace-go.mjs +101 -0
  56. package/skills/explainer-video-from-codebase/tools/lib/trace-js.mjs +136 -0
  57. package/skills/explainer-video-from-codebase/tools/lib/tree-sitter.mjs +19 -0
  58. package/skills/explainer-video-from-codebase/tools/lib/walk.mjs +138 -0
  59. package/skills/explainer-video-from-codebase/tools/mine.mjs +409 -0
  60. package/skills/explainer-video-from-codebase/tools/propose.mjs +127 -0
  61. package/skills/explainer-video-from-codebase/tools/rank.mjs +119 -0
  62. package/skills/explainer-video-from-codebase/tools/ranking-md.mjs +76 -0
  63. package/skills/explainer-video-from-codebase/tools/rules/c-handlers.mjs +53 -0
  64. package/skills/explainer-video-from-codebase/tools/rules/celery-taskiq.mjs +48 -0
  65. package/skills/explainer-video-from-codebase/tools/rules/click-typer.mjs +6 -0
  66. package/skills/explainer-video-from-codebase/tools/rules/express.mjs +72 -0
  67. package/skills/explainer-video-from-codebase/tools/rules/fastapi.mjs +99 -0
  68. package/skills/explainer-video-from-codebase/tools/rules/go-http.mjs +173 -0
  69. package/skills/explainer-video-from-codebase/tools/rules/go-test.mjs +108 -0
  70. package/skills/explainer-video-from-codebase/tools/rules/grpc.mjs +57 -0
  71. package/skills/explainer-video-from-codebase/tools/rules/gtest.mjs +97 -0
  72. package/skills/explainer-video-from-codebase/tools/rules/haystack.mjs +55 -0
  73. package/skills/explainer-video-from-codebase/tools/rules/jest-vitest.mjs +100 -0
  74. package/skills/explainer-video-from-codebase/tools/rules/js-http-client.mjs +111 -0
  75. package/skills/explainer-video-from-codebase/tools/rules/nextjs.mjs +53 -0
  76. package/skills/explainer-video-from-codebase/tools/rules/process-spawn.mjs +49 -0
  77. package/skills/explainer-video-from-codebase/tools/rules/py-http-client.mjs +26 -0
  78. package/skills/explainer-video-from-codebase/tools/rules/pytest.mjs +107 -0
  79. package/skills/explainer-video-from-codebase/tools/rules/react-router.mjs +17 -0
  80. package/skills/explainer-video-from-codebase/tools/rules/registry.mjs +31 -0
  81. package/skills/explainer-video-from-codebase/tools/trace/c/fmtrace-sh +14 -0
  82. package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-net.cjs +33 -0
  83. package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-srcmap.cjs +41 -0
  84. package/skills/explainer-video-from-codebase/tools/trace/python/fmtrace_plugin.py +87 -0
  85. package/skills/explainer-video-from-codebase/tools/trace/python/sitecustomize.py +330 -0
  86. package/skills/explainer-video-from-codebase/tools/trace/sandbox/linux-bwrap.sh +73 -0
  87. package/skills/explainer-video-from-codebase/tools/trace/sandbox/macos-nonet.sb +41 -0
  88. package/skills/explainer-video-from-codebase/tools/trace.mjs +577 -0
  89. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-c.txt +21 -0
  90. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-cpp.txt +21 -0
  91. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-go.txt +21 -0
  92. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-javascript.txt +21 -0
  93. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-python.txt +21 -0
  94. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-typescript.txt +21 -0
  95. package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/web-tree-sitter.txt +21 -0
  96. package/skills/explainer-video-from-codebase/vendor/tree-sitter/MANIFEST.json +172 -0
  97. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-c.wasm +0 -0
  98. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-cpp.wasm +0 -0
  99. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-go.wasm +0 -0
  100. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-javascript.wasm +0 -0
  101. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-python.wasm +0 -0
  102. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-tsx.wasm +0 -0
  103. package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-typescript.wasm +0 -0
  104. package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.js +4081 -0
  105. package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.wasm +0 -0
  106. package/skills/explainer-video-from-coursework/ARCHITECTURE.md +59 -0
  107. package/skills/explainer-video-from-coursework/PIPELINE.md +343 -0
  108. package/skills/explainer-video-from-coursework/ROADMAP.md +725 -0
  109. package/skills/explainer-video-from-coursework/SERIES.md +232 -0
  110. package/skills/explainer-video-from-coursework/SETUP.md +105 -0
  111. package/skills/explainer-video-from-coursework/SKILL.md +858 -0
  112. package/skills/explainer-video-from-coursework/scenes/_pt-chart.js +12 -0
  113. package/skills/explainer-video-from-coursework/scenes/_pt-equation.js +10 -0
  114. package/skills/explainer-video-from-coursework/scenes/_pt-hubspoke.js +7 -0
  115. package/skills/explainer-video-from-coursework/scenes/_pt-loopflow.js +7 -0
  116. package/skills/explainer-video-from-coursework/scenes/_pt-sequence.js +12 -0
  117. package/skills/explainer-video-from-coursework/scenes/_pt-timeline.js +11 -0
  118. package/skills/explainer-video-from-coursework/src/scene-code.html +127 -0
  119. package/skills/explainer-video-from-coursework/src/scene-code.js +379 -0
  120. package/skills/explainer-video-from-coursework/src/scene-core.js +1250 -0
  121. package/skills/explainer-video-from-coursework/src/scene-diagram.html +50 -0
  122. package/skills/explainer-video-from-coursework/src/scene-svg.html +50 -0
  123. package/skills/explainer-video-from-coursework/src/timeline.js +297 -0
  124. package/skills/explainer-video-from-coursework/src/timing-chart.js +160 -0
  125. package/skills/explainer-video-from-coursework/tools/align.mjs +419 -0
  126. package/skills/explainer-video-from-coursework/tools/apply-enrichment.mjs +206 -0
  127. package/skills/explainer-video-from-coursework/tools/assemble.mjs +223 -0
  128. package/skills/explainer-video-from-coursework/tools/build-audio.mjs +234 -0
  129. package/skills/explainer-video-from-coursework/tools/chromium.mjs +73 -0
  130. package/skills/explainer-video-from-coursework/tools/debug-page.mjs +75 -0
  131. package/skills/explainer-video-from-coursework/tools/env.mjs +30 -0
  132. package/skills/explainer-video-from-coursework/tools/frame-dedup.mjs +131 -0
  133. package/skills/explainer-video-from-coursework/tools/key-source.mjs +151 -0
  134. package/skills/explainer-video-from-coursework/tools/model.mjs +21 -0
  135. package/skills/explainer-video-from-coursework/tools/naming.mjs +38 -0
  136. package/skills/explainer-video-from-coursework/tools/onset-audit.mjs +51 -0
  137. package/skills/explainer-video-from-coursework/tools/pacing-check.mjs +350 -0
  138. package/skills/explainer-video-from-coursework/tools/planner.mjs +616 -0
  139. package/skills/explainer-video-from-coursework/tools/publish.mjs +230 -0
  140. package/skills/explainer-video-from-coursework/tools/render.mjs +205 -0
  141. package/skills/explainer-video-from-coursework/tools/resolve-beats.mjs +239 -0
  142. package/skills/explainer-video-from-coursework/tools/script-check.mjs +174 -0
  143. package/skills/explainer-video-from-coursework/tools/stamp.mjs +71 -0
  144. package/skills/explainer-video-from-coursework/tools/timing-report.mjs +299 -0
  145. package/skills/explainer-video-from-coursework/tools/transcribe-gemini.mjs +96 -0
  146. package/skills/explainer-video-from-coursework/tools/tts-gemini.mjs +394 -0
  147. package/skills/explainer-video-from-coursework/tools/verify-audio.mjs +88 -0
  148. package/skills/explainer-video-from-coursework/tools/workdir.mjs +124 -0
  149. package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js +8 -0
  150. package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js.LICENSE.txt +16 -0
@@ -0,0 +1,659 @@
1
+ ---
2
+ name: explainer-video-from-codebase
3
+ description: Turn the major code flows of a codebase (Python, TypeScript / TSX, JavaScript, Go, C, C++, or a mix such as a Python backend with a TypeScript frontend) into a narrated explainer-video series. Mine the repo into a grouped code map, rank candidate flows as evidence, optionally trace the test suite in a sandbox, propose the series with a reason per flow for the user to approve, plan the videos under a 25-minute cap, then script, voice, animate (verified code panels and generated flow diagrams), publish, render and assemble each one. Use when the user wants explainer videos of how a codebase works.
4
+ ---
5
+
6
+ # explainer-video-from-codebase — mine, propose, approve, plan, then the per-video stages
7
+
8
+ Turn a repo into a **video series about its code flows**. Stage 0 gathers the repo, the destination and the
9
+ scratch root. Stages 1–1c run once over the whole repo:
10
+
11
+ - code mines the repo and proposes one candidate flow per entry point, ranked as evidence;
12
+ - you propose the series, with a reason per flow;
13
+ - **the user approves the flow list**;
14
+ - code plans the videos in the coursework plan's shape.
15
+
16
+ Stages 2–8 are `explainer-video-from-coursework`'s own stages, run on that plan: retitle once, then per video
17
+ write the narration, synthesize the audio, author the visuals, generate the publishing metadata, render and
18
+ assemble. Three places differ for code. Stage 3 adds the code-video rules below, and stages 6–8 add a render and
19
+ a title rule. Stage 5 uses `craft-codeflow-scene`: code panels cut from the repo and diagrams generated from the
20
+ validated flows.
21
+
22
+ **Paths.** `<skill>` is this skill's folder and `<coursework>` is the sibling `../explainer-video-from-coursework`,
23
+ both resolved against this skill's folder. The tools self-locate everything else, so they run regardless of cwd.
24
+ The work paths (`out/`, `--out`, `--work`) resolve against the **current directory**, so run every command from
25
+ the **scratch root** that Stage 0 fixes. The scratch root is always **outside the repo**:
26
+ `mine`, `propose`, `rank` and `apply-flows` refuse to write inside it.
27
+
28
+ > **For maintainers — the dual-file rule.** This file is the **contract of record** for the whole pipeline and
29
+ > is deliberately monolithic, so a full run loads in one pass. Four stages also ship as their own sibling skill:
30
+ >
31
+ > - `craft-codeflow-mine`: stages 1 and 1t;
32
+ > - `craft-codeflow-flows`: stages 1b and 1b′;
33
+ > - `craft-codeflow-plan`: stage 1c;
34
+ > - `craft-codeflow-scene`: stage 5 for code videos.
35
+ >
36
+ > Each is a complete contract for its stage, top-level under `skills/` so any stage can be re-run alone.
37
+ > Stages 2–4b and 6–8 are the coursework `craft-video-<stage>` skills. Stages 2, 4 and 4b are unchanged for code;
38
+ > `craft-video-script` (stage 3) and `craft-video-publish`, `-render` and `-assemble` (stages 6–8) also carry this
39
+ > file's code-video rules for their stage, in a sync block that opens **Code-flow videos only**. Every
40
+ > `craft-codeflow-*` skill ends with a **Maintenance** note naming its section here. **If you change a stage, update
41
+ > both files.**
42
+ > `scripts/check-skill-sync.mjs` (CI) fails when they drift:
43
+ >
44
+ > - a Maintenance note names a section that does not exist;
45
+ > - a child and its sections here invoke different tools or flags;
46
+ > - a passage marked `<!-- sync:<id> -->` differs between the two files.
47
+ >
48
+ > Child skills reach the tools as `../explainer-video-from-codebase/tools/<tool>.mjs`. The field reference for
49
+ > every artifact is [`FLOWS.md`](./FLOWS.md).
50
+
51
+ ## Prerequisites
52
+
53
+ - **Stages 0–1c and the stage 5 code tools need only Node ≥ 22.** The tree-sitter runtime and the seven grammars
54
+ are vendored in `vendor/tree-sitter/`. There is no `npm install`, no network and no key.
55
+ - **Stage 1t** (opt-in) uses the repo's own toolchain (its interpreter or virtualenv, its installed jest or
56
+ vitest, `go`, or `cc` + `make` + `gcov`) and the OS sandbox: `sandbox-exec` on macOS, `bwrap` on Linux. It
57
+ never installs anything.
58
+ - **Stages 4–8 need the coursework pipeline's machine setup**: `ffmpeg` / `ffprobe`, the headless Chromium, and
59
+ the **Gemini API key**, stored in the OS secret store (macOS Keychain · Linux `secret-tool` · Windows
60
+ Credential Manager), never in env or a plaintext file. The commands and per-OS steps are in
61
+ [`<coursework>/SETUP.md`](../explainer-video-from-coursework/SETUP.md).
62
+
63
+ **Relay rule.** The tools fail loud on a missing prerequisite: `[node] …`, `[setup] …`,
64
+ `Playwright cache not found …`, `No API key found …`. When one appears, do not debug the scene. Tell the user
65
+ what is missing and the install steps from the matching `SETUP.md` section, then re-run.
66
+
67
+ ### Who owns what
68
+
69
+ - **Code** does what must be exact. It owns grouping, test links, candidate proposal, ranking, framework rules,
70
+ config resolution and tracing (`craft-codeflow-mine` is their single owner). It also owns validation, the
71
+ approval binding, planning, and cutting and verifying code panels.
72
+ - **You** do the judgment: the series proposal and its reasons (`craft-codeflow-flows` is the **only** place the
73
+ series is decided and the only place the mined outputs are overridden), the narration and the scenes. Never edit
74
+ a tool's output file. Change its input and re-run.
75
+ - **The user** decides three things: whether the test suite is traced (stage 1t), the flow list (stage 1b′), and
76
+ which videos are made (scope).
77
+ - **Gemini** is used only at stages 4 and 4b, for speech and transcription.
78
+
79
+ ### Secrets
80
+
81
+ - `mine` reads config only from git-tracked files. It keeps a value only for a key a dispatch guard reads, and
82
+ writes a secret-looking key or a credential-shaped value as `set (redacted)` everywhere in `codemap.json` and
83
+ `codemap.md`. Never look up, guess or narrate a redacted value.
84
+ - The trace runs with an empty environment, so no credentials reach it. It redacts credential-shaped strings
85
+ from `trace.log`, the output tail it prints and the failure messages in `trace.json`.
86
+ - A code panel shows the file byte for byte. When a narrated line holds a credential (a hardcoded token, a URL
87
+ with a password), leave that line out of `--lines` so it collapses into an elision marker. Never say the
88
+ value aloud.
89
+ - The Gemini key stays in the OS secret store. `build-audio` prints the exact store command when it is missing.
90
+
91
+ ### Series scope — the per-video loop
92
+
93
+ - **Stages 0–2 run once**; nothing before the scope choice iterates the video list.
94
+ - **Stages 3–8 repeat once per video**, each in its own work dir `out/<name>/<NN>/`. The coursework tools stamp
95
+ a `.video-identity` marker into it and refuse a run whose `videoIndex` disagrees.
96
+ - **Videos are independent.** Give every concurrent `render` / `assemble` its own `--port`.
97
+ - **One video end to end is a valid stopping point** (a pilot, or a single-target run).
98
+
99
+ ## The stages
100
+
101
+ | Stage | Name | Runs | Skill | Done when |
102
+ |---|---|---|---|---|
103
+ | 0 | `/inputs` | once | — | repo, destination and scratch root set; optional scope flags noted |
104
+ | 1 | `/mine` | once | `craft-codeflow-mine` | `mine`, `propose`, `rank` exit 0; `codemap.md` and `ranking.md` shown in full, every group |
105
+ | 1t | `/trace` (opt-in) | at most once per mined commit | `craft-codeflow-mine` | offered only within its boundary; a "no" skips it; on a yes, the run ended and its report was relayed |
106
+ | 1b | `/flows` | once | `craft-codeflow-flows` | `apply-flows` exits 0 |
107
+ | 1b′ | approve | once, again after any change | `craft-codeflow-flows` | the user said yes to the approval table as shown; `flows.approved.json` written |
108
+ | 1c | `/plan` | once | `craft-codeflow-plan` | `flow-plan` exits 0 (exit 5 without approval); every video shown with the calibration banner |
109
+ | 2 | `/enrich` | once | `craft-video-enrich` | `apply-enrichment` exits 0; the full list shown |
110
+ | scope | choose | once | — | the user chose the videos by index |
111
+ | 3 | `/script` | per video | `craft-video-script` (its code-flow block, the addendum below) | `script-check` exits 0 (pause rule included) |
112
+ | 4, 4b | `/audio`, `/align` | per video | `craft-video-audio`, `craft-video-align` | unchanged |
113
+ | 5 | `/scene` | per video | **`craft-codeflow-scene`** | `excerpt-check` and `pacing-check` exit 0; the stills looked at |
114
+ | 6–8 | `/publish`, `/render`, `/assemble` | per video | `craft-video-publish`, `-render`, `-assemble` (their code-flow block) | unchanged |
115
+
116
+ ## Stage 0 — `/inputs`: repo, destination, scratch
117
+
118
+ Ask for what the user must decide. If the invocation already names them, skip the question. Ask **together, in
119
+ one prompt, with defaults**, so the user only adjusts and never invents:
120
+
121
+ - **Repo** — the root of the codebase. If they referenced a path, use it; otherwise ask for one.
122
+ - **Destination** — where the finished videos land. Default: `videos/` in the scratch root. Each video lands in
123
+ its own `<destination>/<NN>-<slug>/` folder. Never put it inside the repo: the next mine would read it.
124
+ - **Scratch root** — the directory that holds `out/`, the cwd for every command from stage 1 on. Default: a new
125
+ folder beside the repo, `<repo>/../<repo name>-videos/`. It must be outside the repo, since the tools refuse to
126
+ write there. A series runs to hundreds of MB of WAV and MP4, so it is worth one line in the prompt. Say
127
+ plainly that the default is fine.
128
+
129
+ Note, without asking unless the user raises them, the optional scope flags stage 1 takes:
130
+
131
+ - `--lang` to mine only some languages;
132
+ - `--exclude` for a vendored or generated tree;
133
+ - `--group 'glob=prod|test|eval|dev'` for a file the group rules misplace;
134
+ - `--config-root` for config kept outside the repo, such as a deploy repo's Helm values.
135
+
136
+ Then change to the scratch root once and stay there. Proceed to stage 1 only once all three are set.
137
+
138
+ ## Stage 1 — `/mine`: code map, candidates, ranking
139
+
140
+ <!-- sync:mine -->
141
+ ```sh
142
+ node <skill>/tools/mine.mjs <repo> --out out/<name> [--group 'glob=prod|test|eval|dev' ...] \
143
+ [--exclude path,...] [--lang python,typescript,go,c,cpp] [--config-root <dir>]
144
+ ```
145
+
146
+ It writes `out/<name>/codemap.json` and `out/<name>/codemap.md`. It never writes inside the repo, never uses the
147
+ network and never runs repo code.
148
+
149
+ | Exit | Meaning |
150
+ |---|---|
151
+ | 0 | done |
152
+ | 1 | a bad path or flag, `--out` inside the repo, or a repo with no supported files |
153
+ | 2 | a failed self-check: a definition the parser saw was not extracted, or two collapsed into one symbol |
154
+
155
+ On exit 2, report the failure and do not edit the output.
156
+
157
+ **What it mines.** Python, TypeScript / TSX, JavaScript, Go, C and C++ are mined into one code map;
158
+ `--lang typescript` covers JavaScript too. Across languages:
159
+
160
+ - a frontend's or a client's HTTP calls link to the routes they hit in any language (FastAPI, Express, Next.js,
161
+ net/http, gin, echo, chi);
162
+ - UI routes, gRPC methods and C / C++ command tables are entries;
163
+ - each language's tests (pytest, unittest, jest / vitest, node:test, go test, gtest, Catch2, C test functions)
164
+ are evidence.
165
+
166
+ **Groups.** Every file is in exactly one group, `prod`, `test`, `eval` or `dev`, by ordered path rules, then by
167
+ content. A `--group` override wins over all of them. An entry that lands in the test group is not a flow;
168
+ `codemap.md` prints the override that moves it.
169
+
170
+ **Show the user `codemap.md` in full:**
171
+
172
+ - every group, with the rule that placed each file;
173
+ - every entry;
174
+ - every rule link with its evidence, and the frameworks imported without a rule pack (their wiring is not
175
+ linked);
176
+ - the test inventory with the mocks every test runs under (session-wide and autouse ones included);
177
+ - the config profiles with their group, and the config-selected dispatch;
178
+ - the self-check.
179
+
180
+ If the self-check reports calls into imports named like in-repo directories, ambiguous imports, or dependency
181
+ files it could not parse, say so: those calls are not linked.
182
+ <!-- /sync:mine -->
183
+
184
+ <!-- sync:propose-rank -->
185
+ ```sh
186
+ node <skill>/tools/propose.mjs --codemap out/<name>/codemap.json [--fanin 6] [--leafy 5] [--no-iface-fold]
187
+ node <skill>/tools/rank.mjs --candidates out/<name>/candidates.json [--explain-top 10] [--explain-top-eval 5]
188
+ ```
189
+
190
+ `propose` writes `out/<name>/candidates.json`: one candidate flow per prod, eval and dev entry, and how far each
191
+ test's direct links reach. A non-test walk never enters test code. An eval or dev walk folds production code
192
+ into `uses-prod` leaves and records the production flows it `exercises`. Exit 2 means an entry could not be
193
+ walked; report it.
194
+
195
+ `rank` writes `ranking.json` and `ranking.md`, each group ranked on its own by the new code each row covers.
196
+ **Show the user `ranking.md` in full**:
197
+
198
+ - every row of every group, with what each eval row exercises;
199
+ - the dev rows, filtered by default;
200
+ - the shared heavily-tested table, the continuations and the merged order;
201
+ - the explained top rows.
202
+
203
+ There is no cut line. Do not present the top rows as the series, and do not drop a row because its gain is small.
204
+ Choosing the series is stage 1b.
205
+
206
+ **Done when:** `mine`, `propose` and `rank` all exit 0, the self-check is clean, and `codemap.md` and
207
+ `ranking.md` have been shown in full: every group, nothing sampled.
208
+ <!-- /sync:propose-rank -->
209
+
210
+ ## Stage 1t — `/trace`: the test suite, observed (opt-in)
211
+
212
+ <!-- sync:trace-offer -->
213
+ Tracing runs the repo's own test code on this machine, so it is **never** run without the user's explicit yes.
214
+ Nothing but the test suite is ever traced: not a server, a sample request or an eval harness. Offer it at most
215
+ once per mined commit, and only when both of these hold.
216
+
217
+ **The repo has a test command you can name**, plus the arguments its own config or docs use:
218
+
219
+ - **Python:** `pytest`, `python -m pytest` or `python -m unittest`, run with the repo's own interpreter (its
220
+ `.venv/bin/python` when it has one).
221
+ - **JavaScript / TypeScript:** `node --test`, `npx jest` or `npx vitest run`.
222
+ - The runner must already be installed in the repo's `node_modules`, at its root or in the one package below
223
+ it that holds the tests, such as `frontend/`. Paths are then given as you would from that package.
224
+ - A jest or vitest flag's value is written as `-t=name` or `-t name`. Any other flag is boolean, so a path
225
+ after it stays a path.
226
+ - **Go:** `go test ./...`, or `go -C <module dir> test ./...` when the repo holds several Go modules, with the
227
+ `go` on PATH.
228
+ - **C / C++:** `make test` or `make check` (or `make -C <dir> test`), when the repo's Makefile builds and runs its
229
+ tests with `$(CC)` / `$(CXX)`, and `cc`, `make` and `gcov` are installed.
230
+
231
+ **This platform has a sandbox**: macOS (`sandbox-exec`), or Linux with `bwrap` (bubblewrap). Elsewhere, do not
232
+ offer it.
233
+ <!-- /sync:trace-offer -->
234
+
235
+ Ask with exactly this text, filling in the command and the test count from `codemap.md`:
236
+
237
+ <!-- sync:trace-prompt -->
238
+ > This repo has a test suite (`<command>`, N tests). I can run it once, under a profiler, to mark which call paths
239
+ > the tests actually exercise. It runs the repo's test code on this machine, inside a sandbox, on a copy of the files
240
+ > git does not ignore: no network, no writes outside a scratch folder, no inherited environment variables, no
241
+ > handing work to a program outside the sandbox (opening a URL or an app, scripting another app), and no reads of your home folder or the original repo folder (only the copy, the Python interpreter and its virtualenv, node and the repo's node_modules, or, for Go, exactly the two folders `go env` names as GOROOT and GOMODCACHE, read-only, even inside your home folder; never anything else of it). It
242
+ > can still read system files outside those folders. Anything it prints that looks like a credential is redacted from
243
+ > its log. Run it?
244
+ <!-- /sync:trace-prompt -->
245
+
246
+ <!-- sync:trace-run -->
247
+ A "no" skips the stage with no penalty: stage 1b works from the static code map alone. **Only on a yes** to that
248
+ exact command:
249
+
250
+ ```sh
251
+ node <skill>/tools/trace.mjs --codemap out/<name>/codemap.json --test-command "<command>" --i-ran-this-by-the-user \
252
+ [--timeout 900]
253
+ ```
254
+
255
+ `--i-ran-this-by-the-user` means the user said yes. Never pass it otherwise; a different command needs a new yes.
256
+
257
+ The run is held to this boundary:
258
+
259
+ - it runs in a scratch copy of the repo's git-tracked and unignored files, inside this platform's sandbox;
260
+ - it has no network of any kind, localhost and unix sockets included;
261
+ - it writes nothing outside scratch;
262
+ - no program outside the sandbox acts for it;
263
+ - it reads neither the home folder nor the original repo;
264
+ - it starts with an empty environment;
265
+ - it writes no bytecode or cache;
266
+ - it stops at a time limit.
267
+
268
+ There is no option to weaken or skip the sandbox, and no sandbox means no run.
269
+
270
+ It writes `out/<name>/trace.json` and `trace.log`, the run's output with credential-shaped strings redacted.
271
+ `trace.json` is an overlay recording the opt-in (who, when, the exact command). The code map is never edited for
272
+ it, and no static edge is ever removed or demoted.
273
+
274
+ | Exit | Meaning |
275
+ |---|---|
276
+ | 0 | done |
277
+ | 1 | refused: no opt-in flag, bad flags or inputs, a jest / vitest the repo has not installed, two JS packages holding tests, several Go modules and no `-C`, a GOROOT or GOMODCACHE that is or holds the home folder, no Makefile, no gcov |
278
+ | 2 | no test ran: show the user `trace.log`'s end |
279
+ | 3 | refused: not a recognised test command (shell syntax and wrappers such as `uv run` or `env` included) |
280
+ | 4 | refused: no sandbox on this platform, or it does not start here |
281
+ | 5 | the time limit: the partial trace is kept and marked `timedOut` |
282
+
283
+ Report a refusal and never work around it, least of all by running the tests yourself.
284
+
285
+ Tell the user:
286
+
287
+ - the outcome (passed / failed / errors) and the static edges observed;
288
+ - the share of direct test links that ran under their own test. jest / vitest, go test and make record coverage,
289
+ so they say what ran in each test file, test or test binary (the trace's `granularity`) and record no
290
+ caller → callee pair;
291
+ - the callback links the trace confirmed, and the calls that ran with no static edge;
292
+ - **every test in `deniedBySandbox`**. Those tests need a service or a file the sandbox refused (a database, a
293
+ socket, the network, a file in the home folder or one git ignores), so they mark code the suite cannot
294
+ exercise offline.
295
+
296
+ Failing tests are data: never retry one, never weaken the sandbox, never install anything to make them pass. A
297
+ trace says what ran under the test configuration. Narrate it as "observed in the test suite", never as observed
298
+ in production.
299
+
300
+ **Done when:** the user declined, or the run ended (exit 0 or 5) and its report has been relayed, or a refusal
301
+ has been reported.
302
+ <!-- /sync:trace-run -->
303
+
304
+ ## Stage 1b — `/flows`: the series proposal
305
+
306
+ <!-- sync:flows-rules -->
307
+ You propose the series in `out/<name>/flows.json` (fields in [`FLOWS.md`](./FLOWS.md)), from every row of
308
+ `ranking.md`, the candidates you consider, the code, and `trace.json` when stage 1t ran. Each video has to teach
309
+ something, so choose flows that tell distinct stories. The ranking is evidence, not a series. Follow these rules.
310
+
311
+ **Account for every row.**
312
+
313
+ - Every series flow and core gets a one-line `why`. So does every **high-ranked** row you do not include: include
314
+ it, list it as a variant, or skip it with a reason.
315
+ - High-ranked means the top 10 production rows, the top 5 eval rows, and every row at or above the
316
+ lowest-ranked row you include. The floor is the ranking's `--explain-top` / `--explain-top-eval` when those
317
+ are larger; a smaller value never lowers it.
318
+ - A glob never explains a high-ranked row.
319
+ - Every other entry is triaged: a flow, a continuation of one, a variant, skipped, or excluded with a reason
320
+ (globs allowed). Eval rows are never implicit. Dev rows stay filtered unless promoted with a reason.
321
+ - Every segment of a kept candidate is kept, mentioned with a `say`, or excluded with a `why`.
322
+
323
+ **Hops.**
324
+
325
+ - Hops are a subset of the candidate's code, in teaching order, each connected to the hops before it.
326
+ - A config alternative that no profile selects is taught (`teach`, naming its selector), not narrated, unless an
327
+ `outOfRepo` claim's `selects` names it.
328
+ - Declare a link only where no rule fires, with `file:line` evidence.
329
+ - Shared code with no entry of its own (auth, retries, a fee rule) is a `core`, which a hop hands over to with
330
+ `uses`.
331
+ - `break` on a hop starts a new video.
332
+
333
+ **Tests.**
334
+
335
+ - Cite a test on a hop only where the test has a direct link to it and does not mock it. Narration may call a hop
336
+ "tested" or "pinned" only for such a citation.
337
+ - A test that only reaches the hop is cited with `"strength": "reachable"` and is never called tested.
338
+ - Check `codemap.md`'s mocks first: a target every test in scope runs mocked is never tested.
339
+
340
+ **Eval flows.** An eval flow names the production flow it drives with `exercises` instead of re-narrating it.
341
+
342
+ Reasons (`why`, `say`, `claim`) are at least three words and never a placeholder (`tbd`, `todo`, `…`).
343
+ <!-- /sync:flows-rules -->
344
+
345
+ <!-- sync:flows-validate -->
346
+ ```sh
347
+ node <skill>/tools/apply-flows.mjs --candidates out/<name>/candidates.json --flows out/<name>/flows.json
348
+ ```
349
+
350
+ It reads `codemap.json` and `ranking.json` beside the candidates, and the trace when one is there.
351
+
352
+ - **Exit 0** writes `flows.validated.json` and `flows.md`.
353
+ - **Exit 2** lists every problem in `flows.md` and on stderr, and removes a stale `flows.validated.json`. The
354
+ checks are reasons, C1′ high-ranked rows, C1 entry triage, C2 segment triage, C3 hop fidelity, dispatch,
355
+ C3′ test fidelity, groups and shape. Fix `flows.json` and rerun until it exits 0.
356
+ - **Exit 1** is a bad flag, unreadable or mismatched inputs, or an output inside the repo.
357
+
358
+ Never edit `flows.validated.json` or `flows.md`.
359
+
360
+ **Done when:** `apply-flows` exits 0 with `0 problems`: reasons on every series item and every high-ranked skip,
361
+ every entry triaged, every hop connected, and every cited test pinning its hop.
362
+ <!-- /sync:flows-validate -->
363
+
364
+ ## Stage 1b′ — approve the flow list
365
+
366
+ <!-- sync:approve -->
367
+ Show the user the **approval table** at the top of `flows.md` in full:
368
+
369
+ - the series in video order, with each reason;
370
+ - the skipped rows with theirs;
371
+ - the variants, the promoted rows and the dev rows;
372
+ - the estimated videos and minutes.
373
+
374
+ Ask them to approve it or to edit it (add, drop, reorder, promote); never assume approval. **Never write
375
+ `flows.approved.json` without the user's explicit yes to the table as shown.** Silence, "looks interesting" or an
376
+ approval of an earlier table is not a yes. **Only on that yes:**
377
+
378
+ ```sh
379
+ node <skill>/tools/approve-flows.mjs --flows out/<name>/flows.validated.json --by user --note "<the user's words>"
380
+ ```
381
+
382
+ It writes `flows.approved.json`, bound to the validated list's sha256. Exit 2 means the validated list is
383
+ missing, did not pass, or `flows.json` changed since it was validated: rerun `apply-flows`.
384
+
385
+ On any edit, change `flows.json`, rerun `apply-flows`, show the new table, and ask again. The old approval no
386
+ longer matches, and stage 1c refuses until there is a new one.
387
+
388
+ **Done when:** the user said yes to the table as shown and `approve-flows` exited 0.
389
+ <!-- /sync:approve -->
390
+
391
+ ## Stage 1c — `/plan`: videos under the cap
392
+
393
+ <!-- sync:plan -->
394
+ ```sh
395
+ node <skill>/tools/flow-plan.mjs --flows out/<name>/flows.validated.json [--max-min 25] [--estimate] [--force]
396
+ ```
397
+
398
+ It writes `plan.json` and `plan.md` beside the validated flows: an overview video first, then each series flow
399
+ and core in order. A `break` hop starts a new video. Hops pack under the cap (25 minutes by default), and an item
400
+ that overflows splits at its latest crossing, else at the overflowing hop. A hop is never split, and two items
401
+ are never glued.
402
+
403
+ | Exit | Meaning |
404
+ |---|---|
405
+ | 0 | done |
406
+ | 1 | a bad flag or unreadable input |
407
+ | 2 | an orphaned hop or a video over the cap (C4: every kept hop lands in exactly one video) |
408
+ | 4 | the run would overwrite the plan an enriched plan was built from: rerun with `--force`, then rerun stage 2 |
409
+ | 5 | no matching approval (none, or the list changed after it): go back to stage 1b′, never work around it |
410
+
411
+ No plan, and so no script, exists before the user approves the list. `--estimate` prints the plan without an
412
+ approval and writes nothing; it is the estimate in the approval table, not a plan.
413
+
414
+ **`--max-min` is part of the plan.** Video indices are keyed by the split, so a different cap gives different
415
+ videos and invalidates any enrichment written for the old ones. Keep 25 unless the user asks otherwise.
416
+
417
+ The plan has the coursework plan's shape, so the coursework stages read it unchanged:
418
+
419
+ - `forms` is `overview`, `flow` and `core`;
420
+ - each video's `group` is its flow or core id, which `timing-report` groups by;
421
+ - `sections` are `"<file> :: <symbol> @L<start>"`, what a script's `covers` names;
422
+ - `sectionSpans` carry each hop's span, narrated lines and cited tests.
423
+
424
+ **Done when:** `flow-plan` exits 0, and every planned video has been shown to the user with the plan's
425
+ calibration banner, which says what the cost model is calibrated on and what is still uncalibrated. Show the index,
426
+ title, form and minutes of every video, with no sample and no "…and N more". The minutes are a planning
427
+ estimate, not measured audio.
428
+ <!-- /sync:plan -->
429
+
430
+ ## Stage 2 — `/enrich`: retitle the plan
431
+
432
+ Run `craft-video-enrich` on `out/<name>/plan.json` unchanged:
433
+
434
+ ```sh
435
+ node <coursework>/tools/apply-enrichment.mjs --plan out/<name>/plan.json --enrichment out/<name>/enrichment.json
436
+ ```
437
+
438
+ - **Forms.** `form` is one of the plan's own `forms`: `overview`, `flow` or `core`.
439
+ - **Titles.** The plan's titles are placeholders. Title each video from its hops' code and the series reason
440
+ (`formWhy`), for a human scrolling YouTube.
441
+ - **Skip and merge.** A `skip` or `mergeInto` here changes the series the user approved, so ask before making
442
+ one. To drop a whole flow, go back to stage 1b instead.
443
+
444
+ **Done when:** `apply-enrichment` exits 0 and the complete enriched list has been shown to the user.
445
+
446
+ ## Choose the scope — after `/enrich`, before `/script`
447
+
448
+ **Ask the user which videos to run through stages 3–8; never assume "all of them".** Present every video (index
449
+ and title), then ask for all, or a subset by index (`3`, `2, 4`, `1-3, 6`). TTS and rendering are the expensive
450
+ stages, so never spend them on a video the user has not chosen.
451
+
452
+ ## Timing — every stage 3–8 is stamped
453
+
454
+ Bracket every per-video stage with a stamp pair, as in the coursework skill. The `end` stamp is part of each
455
+ stage's done-when:
456
+
457
+ ```sh
458
+ node <coursework>/tools/stamp.mjs begin --out out/<name> --video <NN> --stage <n>
459
+ node <coursework>/tools/stamp.mjs end --out out/<name> --video <NN> --stage <n>
460
+ node <coursework>/tools/timing-report.mjs --out out/<name>
461
+ ```
462
+
463
+ `timing-report` groups the videos by flow (the plan's `group`). Render it at series end, and at a flow boundary
464
+ during a long run.
465
+
466
+ ## Stage 3 — `/script`: the code-video addendum
467
+
468
+ Write `out/<name>/scripts/<NN>-<slug>.json` and validate it with `craft-video-script`. Copy `videoIndex`,
469
+ `title`, `form`, `sources` and `artefacts` from the enriched plan's video. `planTargetMinutes` is its
470
+ `minutes`, and `covers` names its `sections` exactly.
471
+
472
+ ```sh
473
+ node <coursework>/tools/script-check.mjs --script out/<name>/scripts/<NN>-*.json --plan out/<name>/plan.enriched.json
474
+ ```
475
+
476
+ <!-- sync:stage3-addendum -->
477
+ **Code-flow videos only.** A code-flow video's plan, `plan.json` or `plan.enriched.json`, carries
478
+ `"kind": "codebase"`, and its `forms` are `overview`, `flow` and `core`. A coursework plan has no `kind`; nothing
479
+ in this block applies to its videos.
480
+
481
+ A code video's narration is the reading time: a panel shows only the lines being narrated, each highlighted while
482
+ its sentence plays. The script for a code video adds these rules:
483
+
484
+ - **Open** on the concrete request, UI action or command that starts the flow.
485
+ - **One hop per segment.** Name identifiers in words; spoken text holds no code, no URLs and no markdown.
486
+ - **Narrate every line a panel will show**: the hop's narrated lines (`sectionSpans[].lines` in the plan, or the
487
+ lines you choose when that is `null`, at most 22), plus a cited test's def, call and assertion lines.
488
+ - **Branches.** Teach each `teach` branch in its own segment, naming its selector: the setting, flag or profile
489
+ that picks it.
490
+ - **Tests.** Describe a cited test by what it asserts. Call a hop "tested" or "pinned" only for a direct, unmocked
491
+ link, never for a `"strength": "reachable"` citation. For a traced run say "observed in the test suite", never
492
+ "in production".
493
+ - **Evals.** An eval hop that drives a production flow names that flow's video instead of re-narrating it.
494
+ - **Pause prompts** are rare and bounded, only for a block that must be read whole.
495
+ - The segment's text says "pause" out loud.
496
+ - It carries `"pause": {"dwell": <s>}`, with 0 < dwell ≤ 2 and `pad` equal to the dwell.
497
+ - A video has at most 3. `script-check` errors on a fourth, so split the video instead.
498
+ - **Secrets.** Never say a secret or a redacted value.
499
+ <!-- /sync:stage3-addendum -->
500
+
501
+ **Done when:** `script-check` exits 0 (`✓ script is valid`, the pause rule included).
502
+
503
+ ## Stages 4 and 4b — `/audio` and `/align`
504
+
505
+ Run `craft-video-audio` (`build-audio.mjs`, the only stage that spends the Gemini TTS key), then
506
+ `craft-video-align` (`align.mjs`, one transcription request per video). Both are unchanged for code videos.
507
+ Stage 5 needs `lines.json` from 4b, since every focus window is timed from it.
508
+
509
+ ## Stage 5 — `/scene` for code videos: verified code and generated diagrams
510
+
511
+ ```sh
512
+ node <coursework>/tools/stamp.mjs begin --out out/<name> --video <NN> --stage 5
513
+ ```
514
+
515
+ <!-- sync:code-scene -->
516
+ `craft-video-scene`'s whole contract holds:
517
+
518
+ - beats anchor to narration lines, never to seconds;
519
+ - `resolve-beats` passes before `pacing-check`;
520
+ - a shortfall means the scene over-asked: widen the beat's `lines` first, redesign it second, `pad` last;
521
+ - the stills are looked at.
522
+
523
+ Code videos add what follows. **Prerequisites:** `out/<name>/<NN>/timeline.json` and `lines.json` (stages 4 and
524
+ 4b), the video's script, `out/<name>/codemap.json`, `flows.validated.json` and `plan.json`.
525
+
526
+ **Hosts, per segment** (`out/<name>/<NN>/manifest.json`, `key` → `{host, scene}`):
527
+
528
+ - a hop or test segment is a `code` scene (`src/scene-code.html`);
529
+ - the overview's system map, a flow's sequence and an eval's hand-off are `diagram` or `svg` scenes.
530
+
531
+ Each scene lives at `out/<name>/<NN>/scenes/<key>.js` and sets `window.SCENE_CONFIG`. Like any scene, it also
532
+ declares `window.SCENE_BEATS`, one beat per narration line, which is what `resolve-beats` and `pacing-check`
533
+ read.
534
+
535
+ **Code panels.** Never type code into a scene; cut every panel from the repo as mined:
536
+
537
+ ```sh
538
+ node <skill>/tools/excerpt.mjs --codemap out/<name>/codemap.json --symbol <id> [--lines 54-65,70]
539
+ node <skill>/tools/excerpt.mjs --codemap out/<name>/codemap.json --symbol <id> --test <test id> [--lines 54-65,70]
540
+ ```
541
+
542
+ It prints the panel fragment (`file`, `lang`, `lines`, `numbers`, `elided`, `source`) to paste whole into the
543
+ scene's `panels`, with an `id` added.
544
+
545
+ - `--lines` picks the narrated lines: the hop's `lines` in the plan, or, when that is `null`, the lines the script
546
+ narrates. Every unshown run inside the span becomes one `⋯ N lines` marker. Without `--lines` the whole span is
547
+ shown.
548
+ - **Exit 2** covers more than 22 shown lines (narrow `--lines` or split the hop), a missing symbol or test, a line
549
+ outside the span, and a file changed since it was mined (mine again).
550
+ - `--test` cuts the test's side of a "pinned by" pair: its def line, the line that calls the hop, and its
551
+ assertion lines. It exits 2 for a test that mocks the hop or only reaches it. For a cited test, show the hop's
552
+ panel and the test's panel together.
553
+
554
+ **The panel's acts.**
555
+
556
+ - **Focus.** Give each panel one `focus` act per sentence, `{op: "focus", panel, lines, from, to, narration}`.
557
+ - `lines` are 1-based panel lines.
558
+ - `narration` is the narration line it is synced to: an index, or `[from, to]`, into the segment's lines in
559
+ `lines.json`.
560
+ - `from` is that line's onset (`start + lead`), and `to` falls between the line's end and the next line's onset.
561
+ - Every shown non-blank line is in some focus window.
562
+ - **Pause.** A pause segment gets a `{op: "pause", from, to}` act over its spoken prompt: the on-screen badge. Only
563
+ a pause segment gets one.
564
+ - **Segment.** Every code scene names its own segment, `segment: "<key>"`, equal to its manifest key. Its acts are
565
+ timed from that segment's window, and it draws no progress bar.
566
+ - **Taught branches.** A `teach` branch's segment shows each alternative as its own excerpted panel, placed side
567
+ by side with `initLayout` / `layout` acts, and the narration names the selector.
568
+
569
+ **Diagram skeletons.** The boxes and arrows come from the validated flows; the layout polish and the beats come
570
+ from you:
571
+
572
+ ```sh
573
+ node <skill>/tools/flow-diagram.mjs --flows out/<name>/flows.validated.json --as map [--flow <id>]
574
+ node <skill>/tools/flow-diagram.mjs --flows out/<name>/flows.validated.json --flow <id> --as sequence
575
+ node <skill>/tools/flow-diagram.mjs --flows out/<name>/flows.validated.json --flow <id> --as exercises
576
+ ```
577
+
578
+ - `map` is a `diagram` host config: one box per module the hops run through, with the queue, HTTP and spawn
579
+ crossings for banners. It is the overview's system map.
580
+ - `sequence` is the params of the `svg` lane's `SceneCore.primitives.sequence`; clear the stage at the top of
581
+ `renderAt`.
582
+ - `exercises` draws an eval or dev flow's hand-off to each production flow it drives as one box naming that flow's
583
+ video ("see video 8"). The production flow is never re-drawn.
584
+ - Exit 1 is an unknown flow, or `exercises` on a flow with no `exercises` hop.
585
+
586
+ **Secrets.** Leave a line that holds a credential out of `--lines`, so it shows only as an elision marker.
587
+
588
+ **Check, then look.** Resolve the beats, check the pacing, verify the panels, then render stills and look at
589
+ them:
590
+
591
+ ```sh
592
+ node <coursework>/tools/resolve-beats.mjs --work out/<name>/<NN> --script out/<name>/scripts/<NN>-*.json
593
+ node <coursework>/tools/pacing-check.mjs --scene out/<name>/<NN>/scenes/<key>.js \
594
+ --timeline out/<name>/<NN>/timeline.json --segment <key>
595
+ node <skill>/tools/excerpt-check.mjs --work out/<name>/<NN> --repo <repo> --script out/<name>/scripts/<NN>-*.json
596
+ node <coursework>/tools/render.mjs --shots <t1>,<t2>,<t3> --work out/<name>/<NN> --scene "src/scene-code.html?scene=<key>"
597
+ ```
598
+
599
+ `--shots` takes seconds of video time, and a code scene's acts are timed from its segment's window, so a still
600
+ outside that window shows nothing of the scene. Pick the shot times inside `timeline.json`'s `windows["<key>"]`: a
601
+ few seconds after its `start`, its middle, and just before its `end`.
602
+
603
+ `excerpt-check` is C6:
604
+
605
+ | Exit | Meaning |
606
+ |---|---|
607
+ | 0 | done |
608
+ | 1 | no manifest, scenes, `lines.json` or script |
609
+ | 2 | drift: a panel that is not byte-identical to the file as mined, was typed, or hides lines without a marker; re-excerpt it |
610
+ | 3 | a shown line outside every narration-synced focus window, a pause badge out of place, more than 3 pause segments, or a code scene that does not name its own segment |
611
+
612
+ The checkers catch timing and structure, never a visual fault. **Vision gate:** read the first still as an image.
613
+ If that fails, you are on a text-only model: ask the user whether to switch to a vision model or to skip the visual
614
+ check. Never "look" blind.
615
+
616
+ **Done when:** `resolve-beats`, `pacing-check` (each scene) and `excerpt-check` exit 0, and every still has been
617
+ looked at, or the user skipped the visual check.
618
+ <!-- /sync:code-scene -->
619
+
620
+ ```sh
621
+ node <coursework>/tools/stamp.mjs end --out out/<name> --video <NN> --stage 5
622
+ ```
623
+
624
+ ## Stages 6–8 — `/publish`, `/render`, `/assemble`
625
+
626
+ Run `craft-video-publish`, `craft-video-render` and `craft-video-assemble`, each with its code-flow block:
627
+
628
+ <!-- sync:codeflow-publish-render -->
629
+ **Code-flow videos only.** A code-flow video's plan, `plan.json` or `plan.enriched.json`, carries
630
+ `"kind": "codebase"`, and its `forms` are `overview`, `flow` and `core`. A coursework plan has no `kind`; nothing
631
+ in this block applies to its videos. A code-flow video adds these rules at stages 6–8:
632
+
633
+ - **Render.** A code segment renders through `src/scene-code.html?scene=<key>`. Pass `--work out/<name>/<NN>` so
634
+ the host reads that video's `timeline.js`; `assemble` passes it for every segment.
635
+ - **Chapters.** Write each chapter title from that chapter's narration, as for any video.
636
+ - **Upload title.** The `youtubeTitle` names the flow in the viewer's terms, such as what a request does or what
637
+ a command runs. Never use a module path.
638
+ <!-- /sync:codeflow-publish-render -->
639
+
640
+ **Done when:** each stage's own gate holds, ending in `assemble`'s `[done] …<output>.mp4` with `audio ok`, and
641
+ the assembled frames have been looked at.
642
+
643
+ ## Reference
644
+
645
+ Every artifact's fields are in [`FLOWS.md`](./FLOWS.md):
646
+
647
+ - `codemap.json`, `candidates.json`, `ranking.json`, `trace.json`;
648
+ - `flows.json`, `flows.validated.json`, `flows.approved.json`;
649
+ - `plan.json`;
650
+ - the panel fragment, `excerpt-check`'s rules, and the diagram skeletons.
651
+
652
+ The worked fixtures are synthetic code in `skills/explainer-video-from-codebase/fixtures/` of the source
653
+ repository, https://github.com/akshayDev17/ai-explainer-video, only; the installed package does not include them:
654
+
655
+ - `fixtures/flows/repo/`, a small lending library with tests and an eval harness;
656
+ - `fixtures/flows/flows.json`, its series proposal;
657
+ - `fixtures/polyglot/`, a Python backend with a TypeScript frontend, a Go service and a C library.
658
+
659
+ The enrichment, script and publish formats are in the coursework skill's reference sections.