explainer-video 0.0.0-stage → 0.4.1

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