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.
- package/.claude-plugin/plugin.json +12 -0
- package/LICENSE +21 -0
- package/README.md +152 -2
- package/cordis.patch.yml +7 -0
- package/lib/index.js +35 -0
- package/package.json +56 -4
- package/plugin.json +13 -0
- package/skills/coursework-author/.synced-from +5 -0
- package/skills/coursework-author/SKILL.md +209 -0
- package/skills/craft-codeflow-flows/SKILL.md +116 -0
- package/skills/craft-codeflow-mine/SKILL.md +207 -0
- package/skills/craft-codeflow-plan/SKILL.md +65 -0
- package/skills/craft-codeflow-scene/SKILL.md +168 -0
- package/skills/craft-video-align/SKILL.md +109 -0
- package/skills/craft-video-assemble/SKILL.md +69 -0
- package/skills/craft-video-audio/SKILL.md +59 -0
- package/skills/craft-video-enrich/SKILL.md +46 -0
- package/skills/craft-video-plan/SKILL.md +57 -0
- package/skills/craft-video-publish/SKILL.md +79 -0
- package/skills/craft-video-render/SKILL.md +68 -0
- package/skills/craft-video-scene/SKILL.md +112 -0
- package/skills/craft-video-script/SKILL.md +80 -0
- package/skills/explainer-video-from-codebase/FLOWS.md +577 -0
- package/skills/explainer-video-from-codebase/SKILL.md +659 -0
- package/skills/explainer-video-from-codebase/tools/apply-flows.mjs +531 -0
- package/skills/explainer-video-from-codebase/tools/approve-flows.mjs +56 -0
- package/skills/explainer-video-from-codebase/tools/codemap-md.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/config/dotenv.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/json.mjs +14 -0
- package/skills/explainer-video-from-codebase/tools/config/redact.mjs +16 -0
- package/skills/explainer-video-from-codebase/tools/config/toml-lite.mjs +13 -0
- package/skills/explainer-video-from-codebase/tools/config/yaml-lite.mjs +18 -0
- package/skills/explainer-video-from-codebase/tools/excerpt-check.mjs +186 -0
- package/skills/explainer-video-from-codebase/tools/excerpt.mjs +116 -0
- package/skills/explainer-video-from-codebase/tools/flow-diagram.mjs +168 -0
- package/skills/explainer-video-from-codebase/tools/flow-plan.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/flows-md.mjs +87 -0
- package/skills/explainer-video-from-codebase/tools/lang/c.mjs +684 -0
- package/skills/explainer-video-from-codebase/tools/lang/go.mjs +673 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-config.mjs +143 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-deps.mjs +62 -0
- package/skills/explainer-video-from-codebase/tools/lang/python-stdlib.mjs +24 -0
- package/skills/explainer-video-from-codebase/tools/lang/python.mjs +609 -0
- package/skills/explainer-video-from-codebase/tools/lang/typescript.mjs +963 -0
- package/skills/explainer-video-from-codebase/tools/lib/decompose.mjs +102 -0
- package/skills/explainer-video-from-codebase/tools/lib/groups.mjs +77 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-coverage.mjs +68 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe-v8.mjs +232 -0
- package/skills/explainer-video-from-codebase/tools/lib/observe.mjs +162 -0
- package/skills/explainer-video-from-codebase/tools/lib/redact.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/sandbox.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/lib/test-links.mjs +28 -0
- package/skills/explainer-video-from-codebase/tools/lib/toml.mjs +129 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-c.mjs +95 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-go.mjs +101 -0
- package/skills/explainer-video-from-codebase/tools/lib/trace-js.mjs +136 -0
- package/skills/explainer-video-from-codebase/tools/lib/tree-sitter.mjs +19 -0
- package/skills/explainer-video-from-codebase/tools/lib/walk.mjs +138 -0
- package/skills/explainer-video-from-codebase/tools/mine.mjs +409 -0
- package/skills/explainer-video-from-codebase/tools/propose.mjs +127 -0
- package/skills/explainer-video-from-codebase/tools/rank.mjs +119 -0
- package/skills/explainer-video-from-codebase/tools/ranking-md.mjs +76 -0
- package/skills/explainer-video-from-codebase/tools/rules/c-handlers.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/celery-taskiq.mjs +48 -0
- package/skills/explainer-video-from-codebase/tools/rules/click-typer.mjs +6 -0
- package/skills/explainer-video-from-codebase/tools/rules/express.mjs +72 -0
- package/skills/explainer-video-from-codebase/tools/rules/fastapi.mjs +99 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-http.mjs +173 -0
- package/skills/explainer-video-from-codebase/tools/rules/go-test.mjs +108 -0
- package/skills/explainer-video-from-codebase/tools/rules/grpc.mjs +57 -0
- package/skills/explainer-video-from-codebase/tools/rules/gtest.mjs +97 -0
- package/skills/explainer-video-from-codebase/tools/rules/haystack.mjs +55 -0
- package/skills/explainer-video-from-codebase/tools/rules/jest-vitest.mjs +100 -0
- package/skills/explainer-video-from-codebase/tools/rules/js-http-client.mjs +111 -0
- package/skills/explainer-video-from-codebase/tools/rules/nextjs.mjs +53 -0
- package/skills/explainer-video-from-codebase/tools/rules/process-spawn.mjs +49 -0
- package/skills/explainer-video-from-codebase/tools/rules/py-http-client.mjs +26 -0
- package/skills/explainer-video-from-codebase/tools/rules/pytest.mjs +107 -0
- package/skills/explainer-video-from-codebase/tools/rules/react-router.mjs +17 -0
- package/skills/explainer-video-from-codebase/tools/rules/registry.mjs +31 -0
- package/skills/explainer-video-from-codebase/tools/trace/c/fmtrace-sh +14 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-net.cjs +33 -0
- package/skills/explainer-video-from-codebase/tools/trace/js/fmtrace-srcmap.cjs +41 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/fmtrace_plugin.py +87 -0
- package/skills/explainer-video-from-codebase/tools/trace/python/sitecustomize.py +330 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/linux-bwrap.sh +73 -0
- package/skills/explainer-video-from-codebase/tools/trace/sandbox/macos-nonet.sb +41 -0
- package/skills/explainer-video-from-codebase/tools/trace.mjs +577 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-c.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-cpp.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-go.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-javascript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-python.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/tree-sitter-typescript.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/LICENSES/web-tree-sitter.txt +21 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/MANIFEST.json +172 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-c.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-cpp.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-go.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-javascript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-python.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-tsx.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/tree-sitter-typescript.wasm +0 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.js +4081 -0
- package/skills/explainer-video-from-codebase/vendor/tree-sitter/web-tree-sitter.wasm +0 -0
- package/skills/explainer-video-from-coursework/ARCHITECTURE.md +59 -0
- package/skills/explainer-video-from-coursework/PIPELINE.md +343 -0
- package/skills/explainer-video-from-coursework/ROADMAP.md +725 -0
- package/skills/explainer-video-from-coursework/SERIES.md +232 -0
- package/skills/explainer-video-from-coursework/SETUP.md +105 -0
- package/skills/explainer-video-from-coursework/SKILL.md +858 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-chart.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-equation.js +10 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-hubspoke.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-loopflow.js +7 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-sequence.js +12 -0
- package/skills/explainer-video-from-coursework/scenes/_pt-timeline.js +11 -0
- package/skills/explainer-video-from-coursework/src/scene-code.html +127 -0
- package/skills/explainer-video-from-coursework/src/scene-code.js +379 -0
- package/skills/explainer-video-from-coursework/src/scene-core.js +1250 -0
- package/skills/explainer-video-from-coursework/src/scene-diagram.html +50 -0
- package/skills/explainer-video-from-coursework/src/scene-svg.html +50 -0
- package/skills/explainer-video-from-coursework/src/timeline.js +297 -0
- package/skills/explainer-video-from-coursework/src/timing-chart.js +160 -0
- package/skills/explainer-video-from-coursework/tools/align.mjs +419 -0
- package/skills/explainer-video-from-coursework/tools/apply-enrichment.mjs +206 -0
- package/skills/explainer-video-from-coursework/tools/assemble.mjs +223 -0
- package/skills/explainer-video-from-coursework/tools/build-audio.mjs +234 -0
- package/skills/explainer-video-from-coursework/tools/chromium.mjs +73 -0
- package/skills/explainer-video-from-coursework/tools/debug-page.mjs +75 -0
- package/skills/explainer-video-from-coursework/tools/env.mjs +30 -0
- package/skills/explainer-video-from-coursework/tools/frame-dedup.mjs +131 -0
- package/skills/explainer-video-from-coursework/tools/key-source.mjs +151 -0
- package/skills/explainer-video-from-coursework/tools/model.mjs +21 -0
- package/skills/explainer-video-from-coursework/tools/naming.mjs +38 -0
- package/skills/explainer-video-from-coursework/tools/onset-audit.mjs +51 -0
- package/skills/explainer-video-from-coursework/tools/pacing-check.mjs +350 -0
- package/skills/explainer-video-from-coursework/tools/planner.mjs +616 -0
- package/skills/explainer-video-from-coursework/tools/publish.mjs +230 -0
- package/skills/explainer-video-from-coursework/tools/render.mjs +205 -0
- package/skills/explainer-video-from-coursework/tools/resolve-beats.mjs +239 -0
- package/skills/explainer-video-from-coursework/tools/script-check.mjs +174 -0
- package/skills/explainer-video-from-coursework/tools/stamp.mjs +71 -0
- package/skills/explainer-video-from-coursework/tools/timing-report.mjs +299 -0
- package/skills/explainer-video-from-coursework/tools/transcribe-gemini.mjs +96 -0
- package/skills/explainer-video-from-coursework/tools/tts-gemini.mjs +394 -0
- package/skills/explainer-video-from-coursework/tools/verify-audio.mjs +88 -0
- package/skills/explainer-video-from-coursework/tools/workdir.mjs +124 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js +8 -0
- package/skills/explainer-video-from-coursework/vendor/plotly-basic.min.js.LICENSE.txt +16 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# SERIES.md — running a multi-video series
|
|
2
|
+
|
|
3
|
+
Stages 1–2 run once over the whole notes directory. Stages 3–8 run **per video**.
|
|
4
|
+
This file defines how a series run is organised, recorded, and resumed.
|
|
5
|
+
|
|
6
|
+
It exists because nothing else did. A nine-video run had no durable record of which
|
|
7
|
+
videos were selected, no per-video progress state, and no defined execution topology,
|
|
8
|
+
so the orchestrating agent hand-rolled a queue in `/tmp` and kept the remaining list in
|
|
9
|
+
its own context. When the session was interrupted, the pipeline's state was interrupted
|
|
10
|
+
with it, and the queue had to be re-derived three times. **A resumed session must be
|
|
11
|
+
able to continue from the filesystem alone.**
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Topology
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
assemble the run (once)
|
|
19
|
+
├─ read plan.enriched.json + the chosen indices → write series.json (once, never rewritten)
|
|
20
|
+
└─ loop, lazily:
|
|
21
|
+
acquire a free port → spawn ONE per-video agent → on settle, read its state.json
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
**Two roles, strictly separated.**
|
|
25
|
+
|
|
26
|
+
| Role | Does | Never does |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| **Orchestrator** (the main session) | Writes `series.json`. Dispatches one agent per free port. Reads `state.json` and the timing ledger. Reports. | Runs any skill stage. Renders anything. Polls anything. |
|
|
29
|
+
|
|
30
|
+
| Role | Does |
|
|
31
|
+
|---|---|
|
|
32
|
+
| **Per-video agent** (one subagent per video) | Stages 3→8 for its own video, end to end, including script authoring and the visual QA loop. Owns its work dir. Writes its own `state.json`. |
|
|
33
|
+
|
|
34
|
+
**The port is the admission token.** A port in the pool means "one more video may be in
|
|
35
|
+
flight". The orchestrator spawns only when a port is free, so an agent never queues and
|
|
36
|
+
never waits — which is the entire point of lazy dispatch, and why no lock can starve.
|
|
37
|
+
A per-video agent's lifetime is its render window bound: author → audio → align → stills
|
|
38
|
+
→ render → QA → assemble, typically 1.5–2.5 hours.
|
|
39
|
+
|
|
40
|
+
**Consequence, stated plainly:** total in-flight videos ≤ `parallel`. On an 8-core,
|
|
41
|
+
8 GB machine that is 2–3. Authoring capacity is therefore bounded by render capacity.
|
|
42
|
+
Whether that costs throughput depends on how well concurrent renders scale, which is
|
|
43
|
+
unmeasured — see *Open parameter* at the end.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Files
|
|
48
|
+
|
|
49
|
+
### `out/<name>/series.json` — written once by the orchestrator
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"plan": "out/<name>/plan.enriched.json",
|
|
54
|
+
"destination": "videos/",
|
|
55
|
+
"parallel": 2,
|
|
56
|
+
"portBase": 9333,
|
|
57
|
+
"videos": [
|
|
58
|
+
{ "index": 1, "work": "out/<name>/01", "script": "out/<name>/scripts/01-<slug>.json",
|
|
59
|
+
"slug": "1-the-instruction-layer-the-constitution-your-agent-runs-on",
|
|
60
|
+
"name": "the-instruction-layer" }
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`parallel` is the **one** concurrency number: it is simultaneously the pool size, the
|
|
66
|
+
batch size, and the maximum number of concurrent per-video agents — because the port *is*
|
|
67
|
+
the admission token, so one in-flight video means one port means one subagent. Ports are
|
|
68
|
+
derived, never stored: slot *i* renders on `portBase + i`. Storing a port list as well
|
|
69
|
+
would let the two drift, and a resumed session would then hand out a port a live agent
|
|
70
|
+
already holds.
|
|
71
|
+
|
|
72
|
+
It lives in this file rather than being a flag because a resumed session must not pick a
|
|
73
|
+
different value: `parallel` determines port allocation, and changing it mid-run
|
|
74
|
+
invalidates every port already claimed.
|
|
75
|
+
|
|
76
|
+
Titles are **not** duplicated here — they live in `plan.enriched.json`, which stays the
|
|
77
|
+
single source of truth for content. Two copies would drift.
|
|
78
|
+
|
|
79
|
+
### `out/<name>/<NN>/state.json` — written by that video's agent, and only by it
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"videoIndex": 1,
|
|
84
|
+
"status": "rendering",
|
|
85
|
+
"stages": {
|
|
86
|
+
"3": { "done": true, "attempts": 1, "ended_at": "…", "by": "agent-abc" },
|
|
87
|
+
"4": { "done": true, "attempts": 1, "ended_at": "…" },
|
|
88
|
+
"4b": { "done": true, "attempts": 1, "ended_at": "…" },
|
|
89
|
+
"5": { "done": true, "attempts": 1, "ended_at": "…" },
|
|
90
|
+
"6": { "done": true, "attempts": 1, "ended_at": "…" },
|
|
91
|
+
"7": { "done": true, "attempts": 1, "ended_at": "…" },
|
|
92
|
+
"8": { "done": false, "attempts": 1, "last_error": null }
|
|
93
|
+
},
|
|
94
|
+
"artefacts": {
|
|
95
|
+
"script": "…", "lines": "…", "beats": "…", "manifest": "…",
|
|
96
|
+
"mp4": "videos/<slug>/<name>.mp4",
|
|
97
|
+
"description": "videos/<slug>/description.md", "captions": "videos/<slug>/captions.srt"
|
|
98
|
+
},
|
|
99
|
+
"render": { "port": 9333, "attempts": 1 },
|
|
100
|
+
"qa": { "stills_checked": true, "beats_audited": 48, "verdict": "accept" }
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`status` ∈ `queued · authoring · audio · aligning · scenes · publishing · rendering ·
|
|
105
|
+
qa · done · failed · needs-fresh`.
|
|
106
|
+
|
|
107
|
+
**One writer per file** — that video's agent. Write atomically (temp + `renameSync`).
|
|
108
|
+
No shared file has nine writers, so there is nothing to merge and no lost updates.
|
|
109
|
+
|
|
110
|
+
`state.json` is a *claim about* progress, not proof of it: on resume, verify the
|
|
111
|
+
artefacts actually exist before trusting a `"done": true`.
|
|
112
|
+
|
|
113
|
+
### `out/<name>/<NN>/.video-identity` — written by the *tools*, not the agent
|
|
114
|
+
|
|
115
|
+
Stamped by `tools/workdir.mjs` on the first stage that touches the dir; refuses a
|
|
116
|
+
`videoIndex` mismatch with exit 3. Identity answers *whose dir is this*; `state.json`
|
|
117
|
+
answers *how far along*. Keep them separate: the guard must work for a human running a
|
|
118
|
+
single stage by hand, with no agent and no state file.
|
|
119
|
+
|
|
120
|
+
### `out/<name>/<NN>/intent.md` — written by the agent at stage 5
|
|
121
|
+
|
|
122
|
+
One line per beat: what should be on screen, and on which words.
|
|
123
|
+
|
|
124
|
+
```
|
|
125
|
+
beat 3 "gate" on "No authorization check" three red pills appear left
|
|
126
|
+
beat 4 "gate" on "No confirmation" fourth pill, right column
|
|
127
|
+
beat 9 "fix3" on "Only the tool…can guarantee" final line lands, amber
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Its purpose is QA fidelity — an agent judging frames against its *own recorded intent*
|
|
131
|
+
rather than against a memory of it — and anti-leniency, since a checklist resists
|
|
132
|
+
rubber-stamping far better than an impression does. Read it fresh at QA time; do not
|
|
133
|
+
recall it.
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## The per-video agent's duties
|
|
138
|
+
|
|
139
|
+
Each stage, in order, with its gate:
|
|
140
|
+
|
|
141
|
+
| Stage | Duty | Gate before moving on |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| **3** | Write `scripts/<NN>-<slug>.json`. **Read the plan's `sectionSpans` for this video and the source markdown itself** — a summary is not enough for a faithful script | `script-check` exits 0 |
|
|
144
|
+
| **4** | `build-audio --out out/<name>/<NN>` — measured durations, tiled windows | exits 0; every segment has a window |
|
|
145
|
+
| **4b** | `align` — word and line timings | exits 0; a line for every segment |
|
|
146
|
+
| **5** | Author `scenes/` + `manifest.json` + `intent.md`; `resolve-beats`; `pacing-check` per segment | pacing is sound for every segment |
|
|
147
|
+
| **5·QA** | **Stills gate** — `render --shots` at 3–5 mid-segment times on the assigned port, then **look at them** | no collision, no missing heading, nothing cropped |
|
|
148
|
+
| **6** | `publish.json` → `publish` | chapters and cues validate |
|
|
149
|
+
| **7–8** | `assemble --dest videos` | exits 0 with `audio ok` |
|
|
150
|
+
|
|
151
|
+
Then: sample the finished MP4, look, and either accept or fix-and-re-render.
|
|
152
|
+
|
|
153
|
+
**Fix loops are bounded.** Two render attempts, two QA rounds. On the second failure
|
|
154
|
+
write `status: "needs-fresh"` with the reason and stop — a drifting session that keeps
|
|
155
|
+
re-rendering is worse than a fresh one that resumes from `state.json`.
|
|
156
|
+
|
|
157
|
+
**Release the port between attempts**, not at the end of the agent's life, so a fix loop
|
|
158
|
+
cannot starve the other videos waiting to be admitted.
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
## Hard rules
|
|
163
|
+
|
|
164
|
+
1. **Never write into another video's work dir.** One dir per video; the guard enforces
|
|
165
|
+
it, and the refusal (exit 3) is not to be worked around by deleting the marker.
|
|
166
|
+
2. **One port per agent.** Never omit `--port`; a second render on 9333 does not fail
|
|
167
|
+
cleanly, it attaches to the running browser's page. `assemble.mjs --port <n>` forwards
|
|
168
|
+
the port to its per-segment renders and **refuses a port already in use**, so the
|
|
169
|
+
per-video agent can own stage 8 end to end on its own port — and the `parallel`
|
|
170
|
+
batch applies to stage 8, not just to the stage-7 QA window.
|
|
171
|
+
3. **Never run `build-audio --only` against a finished video.** Partial runs are isolated
|
|
172
|
+
now, but the intent of `--only` is a smoke test on a video you have not finished.
|
|
173
|
+
4. **Scope every per-video command at `out/<name>/<NN>`.** `stamp`, `planner` and
|
|
174
|
+
`timing-report` legitimately take the plan dir; nothing else does.
|
|
175
|
+
5. **`timing.jsonl` is append-only and already concurrency-safe** — `appendFileSync` of a
|
|
176
|
+
sub-4KB line is atomic. Do not rewrite it as read-modify-write.
|
|
177
|
+
6. **Scratch is disposable, deliverables are not.** `out/` is git-ignored; **`videos/`
|
|
178
|
+
currently is not** — a `git clean -fd` in the notes repo deletes the finished MP4s.
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Acceptance tests
|
|
183
|
+
|
|
184
|
+
Design against these; they are also how you would verify a re-implementation.
|
|
185
|
+
|
|
186
|
+
1. **Cold resume.** Kill every session mid-run. A fresh agent with no conversation
|
|
187
|
+
history reads `series.json` + the `state.json` files and continues correctly.
|
|
188
|
+
2. **No cross-contamination.** Two videos, one directory → exit 3, and the victim's
|
|
189
|
+
`timeline.json` byte-identical afterwards.
|
|
190
|
+
3. **Port reclamation.** An agent killed while holding a port does not block the series;
|
|
191
|
+
the port is reclaimable by pid-liveness or age.
|
|
192
|
+
4. **No silent partial.** A `--only` run leaves `timeline.json` and `narration.wav`
|
|
193
|
+
untouched and writes `timeline.<key>.json`.
|
|
194
|
+
5. **Untouched happy path.** A dir holding plan files with no marker is accepted — the
|
|
195
|
+
guard must not fire on legitimate layouts.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## Batch size — measured
|
|
200
|
+
|
|
201
|
+
Measured on this machine (8 cores, 8 GB), one fixed 612-frame window of one real scene,
|
|
202
|
+
distinct work-dir copies, distinct ports, nothing else running:
|
|
203
|
+
|
|
204
|
+
| trial | per-slot wall fps | aggregate | vs solo |
|
|
205
|
+
|---|---|---|---|
|
|
206
|
+
| N=1 | 12.21 | 12.21 f/s | 100% |
|
|
207
|
+
| N=1 (repeat) | 12.46 | 12.46 f/s | 102% — noise is ~2% |
|
|
208
|
+
| **N=2** | 12.05 | **24.10 f/s** | **197%** |
|
|
209
|
+
| **N=3** | 10.77 | **32.31 f/s** | **265%** |
|
|
210
|
+
|
|
211
|
+
**Set `parallel: 3`.** A second render costs almost nothing (1.3% per slot); a third
|
|
212
|
+
costs ~12% per slot and still nets +34% aggregate over N=2. Concurrency buys *throughput*
|
|
213
|
+
here, not just latency, so the batch is justified on its own merits. Cap at 3 on 8 GB —
|
|
214
|
+
each slot is a Chromium plus an ffmpeg.
|
|
215
|
+
|
|
216
|
+
### What the profile actually says
|
|
217
|
+
|
|
218
|
+
`render.mjs` reports 13.4 / 14.2 / 13.9 fps at N=1/2/3 while wall clock rises, so the
|
|
219
|
+
per-frame capture loop is **not** what degrades under contention — the extra time is
|
|
220
|
+
process startup and the ffmpeg encode. Overhead, computed per trial as wall minus
|
|
221
|
+
(612 ÷ reported fps): **N=1 ≈ 4.4 s, N=2 ≈ 7.7 s, N=3 ≈ 12.8 s**.
|
|
222
|
+
|
|
223
|
+
That overhead is paid **per segment, not per video**, because `assemble` invokes
|
|
224
|
+
`render.mjs` once per segment and each invocation launches a fresh Chromium and reloads
|
|
225
|
+
the page. On a 48-segment video that is ~48 × 4.4 s ≈ **3.5 minutes of pure relaunch**,
|
|
226
|
+
about 7% of a 50-minute render — a cheaper win than a codec or pipe change, and it needs
|
|
227
|
+
no change to how frames are captured. (An earlier draft of this section said ~5 minutes /
|
|
228
|
+
10%, from flatly assuming 14 fps; the per-trial figures above are the measured ones.)
|
|
229
|
+
|
|
230
|
+
**Consequence for this topology:** `frames/` and `preview/` are derived from `--work`, so
|
|
231
|
+
two concurrent renders can never share a work dir even on different ports. One dir per
|
|
232
|
+
video satisfies this by construction; a benchmark or smoke test must copy the dir per slot.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Setup — what this skill needs on the machine
|
|
2
|
+
|
|
3
|
+
One-time environment prerequisites. `SKILL.md` and the per-stage files point here; run
|
|
4
|
+
everything else from the **scratch root** (the directory holding `out/`, which Stage 0
|
|
5
|
+
fixes and which defaults beside your notes), not the skill install folder. Everything the
|
|
6
|
+
pipeline produces beyond this it makes itself under `out/`.
|
|
7
|
+
|
|
8
|
+
## Node ≥ 22
|
|
9
|
+
|
|
10
|
+
`render.mjs` and `debug-page.mjs` rely on Node's built-in global `WebSocket`. Nothing
|
|
11
|
+
declares it, so on older Node it fails cryptically. Check with `node --version`.
|
|
12
|
+
|
|
13
|
+
## ffmpeg + ffprobe
|
|
14
|
+
|
|
15
|
+
Frame → H.264 encoding, muxing, and the `ffprobe` duration measurement. Both must be on
|
|
16
|
+
`PATH`.
|
|
17
|
+
|
|
18
|
+
## Headless Chromium
|
|
19
|
+
|
|
20
|
+
The render stage captures frames through the headless shell. One command covers all three
|
|
21
|
+
OSes; Playwright puts the binary in the per-OS cache folder:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx playwright install --with-deps --only-shell
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
- `--only-shell` downloads just the headless shell, not the full Chromium browser.
|
|
28
|
+
- `--with-deps` also installs the OS system libraries — needed on a bare Linux box; on
|
|
29
|
+
macOS and Windows there are no extra dependencies to install.
|
|
30
|
+
|
|
31
|
+
`tools/chromium.mjs` finds it in the cache — `~/Library/Caches/ms-playwright` (macOS) ·
|
|
32
|
+
`~/.cache/ms-playwright` (Linux) · `%LOCALAPPDATA%\ms-playwright` (Windows) — or honours
|
|
33
|
+
`CHROME_BIN` / `PLAYWRIGHT_BROWSERS_PATH` if the browser lives somewhere else.
|
|
34
|
+
|
|
35
|
+
([source](https://playwright.dev/docs/browsers#chromium-headless-shell))
|
|
36
|
+
|
|
37
|
+
## Transcribe quota (stage 4b)
|
|
38
|
+
|
|
39
|
+
`tools/align.mjs` calls `gemini-3.5-transcribe`, which has a **hard per-day cap** that
|
|
40
|
+
is separate from the TTS model and cannot be batched around:
|
|
41
|
+
|
|
42
|
+
| Limit | Value | Source |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| Requests/day per project per model | **100** | API `QuotaFailure` → `GenerateRequestsPerDayPerProjectPerModel`, `quotaValue: "100"` |
|
|
45
|
+
| Audio per request | 1 hour — **30 minutes when word-level timestamps are enabled** | model card |
|
|
46
|
+
| Batch API | **not supported** | model card |
|
|
47
|
+
| Custom vocabulary + word timestamps | mutually exclusive | transcribe guide |
|
|
48
|
+
|
|
49
|
+
Exceeding the daily cap returns HTTP **429** with a `RetryInfo.retryDelay` (~18 h) and
|
|
50
|
+
nothing else in the pipeline is affected — TTS uses a different model with its own
|
|
51
|
+
quota.
|
|
52
|
+
|
|
53
|
+
Practical consequences:
|
|
54
|
+
|
|
55
|
+
- `align.mjs` transcribes the **whole narration track in one request** by default
|
|
56
|
+
(per-segment would be ~30 requests per video, i.e. ~3 videos/day). Above ~28 minutes
|
|
57
|
+
of track it cuts at segment boundaries into multiple passes, because 30 minutes is
|
|
58
|
+
the model ceiling with word timings.
|
|
59
|
+
- `--per-segment` remains available as a fallback; it is chosen automatically when
|
|
60
|
+
only a few segments are missing, where it is cheaper than a full pass.
|
|
61
|
+
- A quota failure **stops the run with exit 3** rather than falling back and burning
|
|
62
|
+
the rest of the budget on doomed requests.
|
|
63
|
+
- Alignment is a **one-time cost per video** — `narration/<key>.words.json` is cached
|
|
64
|
+
and keyed on the WAV plus the script text. **Commit those files** and alignment is
|
|
65
|
+
never paid for twice.
|
|
66
|
+
|
|
67
|
+
## Gemini API key
|
|
68
|
+
|
|
69
|
+
Read from the OS's native secret store — **never env, never a plaintext file**. Store it
|
|
70
|
+
once (a free key: <https://aistudio.google.com/apikey>).
|
|
71
|
+
|
|
72
|
+
**macOS — Keychain.** Either way stores the same item:
|
|
73
|
+
|
|
74
|
+
- GUI: **Keychain Access** (Applications → Utilities) → File → **New Password Item…** →
|
|
75
|
+
*Keychain Item Name* `gemini-tts` · *Account Name* your username · *Password* the key →
|
|
76
|
+
**Add**. ([source](https://its.uiowa.edu/services/macos/how-use-keychain-access-macos))
|
|
77
|
+
- CLI: `security add-generic-password -U -s gemini-tts -a "$USER" -w` (prompts for the key).
|
|
78
|
+
|
|
79
|
+
The script looks the item up by its **service** attribute, which the GUI dialog does not
|
|
80
|
+
expose. After creating it in the GUI, verify with `security find-generic-password -s
|
|
81
|
+
gemini-tts -w`; if that prints nothing, use the CLI command instead.
|
|
82
|
+
|
|
83
|
+
**Linux — Secret Service.** Either way:
|
|
84
|
+
|
|
85
|
+
- GUI: **Passwords and Keys** (Seahorse) → **+** → **Password** → pick a keyring, put
|
|
86
|
+
`gemini-tts` in *Description* and the key in *Password* → **Add**.
|
|
87
|
+
([source](https://help.gnome.org/seahorse/passwords-stored-create.html))
|
|
88
|
+
- CLI: `secret-tool store --label="Gemini TTS" service gemini-tts` (reads the key from
|
|
89
|
+
stdin — paste it, then Ctrl-D).
|
|
90
|
+
|
|
91
|
+
Same caveat: the GUI stores a *Description*, not the `service` attribute the script keys
|
|
92
|
+
on. Verify with `secret-tool lookup service gemini-tts`; if empty, use the CLI.
|
|
93
|
+
|
|
94
|
+
**Windows — Credential Manager** (GUI only — there is no command-line store, and the
|
|
95
|
+
pipeline reads the credential with the Windows CredMan API):
|
|
96
|
+
|
|
97
|
+
Control Panel → **User Accounts** → **Credential Manager** → **Windows Credentials** →
|
|
98
|
+
**Add a generic credential** → *Internet or network address* `gemini-tts` · *User name*
|
|
99
|
+
anything (e.g. your username) · *Password* the key → **OK**.
|
|
100
|
+
|
|
101
|
+
([Credential Manager in Windows](https://support.microsoft.com/en-us/windows/security/credential-manager-in-windows) ·
|
|
102
|
+
[CredRead — the retrieval API](https://learn.microsoft.com/en-us/windows/win32/api/wincred/nf-wincred-credreadw))
|
|
103
|
+
|
|
104
|
+
One-off override: `--key <value>` on any tool — visible in the process list, so not for
|
|
105
|
+
everyday use.
|