Conversation
0d50ebf to
7320ccd
Compare
Narrated-video pipeline: HTML slides with staged entrance animations, TTS voiceover measured with ffprobe, burned-in subtitles, deterministic frame-stepping capture, ffmpeg assembly, and ASR verification. - 11 Node scripts, no build step; works with mcode connectors or mmx-cli - 13 themes, 17 layout recipes, image framing primitives - Subtitles confined to their own windows (no overlap); 16:9 and 9:16 canvases - Research phase documented: source grading, cross-verification rules, notes template - Discloses dependencies, accounts, network destinations and data handling (bilingual README) - Validation: node scripts/validate.mjs -> OK (exit 0)
7320ccd to
4104572
Compare
Add Plugin: html2video-for-mcode Narrated-video pipeline: HTML slides with staged entrance animations, TTS voiceover measured with ffprobe, burned-in subtitles, deterministic frame-stepping capture, ffmpeg assembly, and ASR verification. - 11 Node scripts, no build step; works with mcode connectors or mmx-cli - 13 themes, 17 layout recipes, image framing primitives - Subtitles confined to their own windows (no overlap); 16:9 and 9:16 canvases - Research phase documented: source grading, cross-verification rules, notes template, plus how to fetch official-site / press-release text (SPA rendering, PDF-first numbers) - Discloses dependencies, accounts, network destinations and data handling (bilingual README) - Validation: node scripts/validate.mjs -> OK (exit 0)
… them to the repo root) Add Plugin: html2video-for-mcode Narrated-video pipeline: HTML slides with staged entrance animations, TTS voiceover measured with ffprobe, burned-in subtitles, deterministic frame-stepping capture, ffmpeg assembly, and ASR verification. - 11 Node scripts, no build step; works with mcode connectors or mmx-cli - 13 themes, 17 layout recipes, image framing primitives - Subtitles confined to their own windows (no overlap); 16:9 and 9:16 canvases - Research phase documented: source grading, cross-verification rules, notes template, plus how to fetch official-site / press-release text (SPA rendering, PDF-first numbers) - Discloses dependencies, accounts, network destinations and data handling (bilingual README) - Validation: node scripts/validate.mjs -> OK (exit 0)
hetaoBackend
left a comment
There was a problem hiding this comment.
Request changes for exact current head 737ee96.
Blocking security and evidence issues:
- Input-derived IDs and paths are not contained.
skills/html2video-for-mcode/scripts/capture.mjs:75-77,228-230usess.htmlands.idto construct paths and recursively deletes the frame directory;build-video.mjs:55-58,191-197usest.idfor output/frame/ASR paths;plan-timings.mjs:47-50andbuild-video.mjs:102-105also consume script-provided paths. An Agent-editablescript.jsonvalue such as../../victimcan escape the intended build directory and trigger out-of-scope reads/writes/deletion. Add strict ID validation, resolve-and-containment checks, symlink checks, and malicious-ID tests. init-project.mjs:7-16,468-477accepts an existing directory and then overwrites project files without a non-empty check or explicit--force.fetch-official-images.mjs:31-32,102-115accepts arbitrary--out-dirand overwrites files;prep-image.mjs:64-87uses ffmpeg-yfor arbitrary output. This contradicts the README claim that writes stay inside the supplied project directory and creates destructive overwrite behavior. Default to refusing existing/non-empty targets and require explicit force, with output containment enforced.asr.mjs:29-32,65-74allows--base-url/MINIMAX_BASE_URLto replace the endpoint without validation while sending the MiniMax API key as a Bearer token. A misconfiguration or prompt-controlled environment can exfiltrate the credential to arbitrary HTTPS/HTTP endpoints. Default to an HTTPS allowlist for official hosts; make custom endpoints an explicit, separately disclosed dangerous opt-in.fetch-official-images.mjs:25-29,40-46,98-115accepts arbitraryhttp:,https:, andfile:URLs and downloads through a browser/request client without protocol, private-address, redirect, or response-size restrictions. This exposes SSRF and local-file-read/copy behavior. Restrict to validated HTTPS public targets, block loopback/private/link-local/metadata addresses and redirects, bound responses, and make local files explicit opt-in with containment.- The PR adds roughly 2,000 lines of executable scripts but no executable test suite;
evals/evals.jsonis prompt/expected-output data and is not run bynpm run check, while the repository validator does not execute these scripts or validate.claude-plugin/plugin.json. Add automated negative and smoke tests for containment, overwrite refusal, endpoint allowlist, SSRF/file rejection, and a minimal render/checker/build dry-run. The current[code]smithcheck is skipped and cannot substitute for this evidence.
Do not approve or merge until these security boundaries and executable test evidence are present on a new head.
|
Thanks for the review — all five blockers were reproduced against the exact head you flagged and are fixed on the new head (v1.1.0). Point-by-point: 1. Input-derived IDs and paths are not contained — fixed. 2. Destructive overwrite behaviour — fixed. 3. Endpoint replacement could exfiltrate the credential — fixed. 4. SSRF / local-file read in 5. No executable test suite — added. Also in this head (non-blocking, from user feedback while the review was open): pure-CSS/SVG chart recipes with entrance-and-growth animations, a one-switch The validator reports |
追加:放映页(可以先自己放一遍再渲染)+ 修一个"文档说能用、闸门说不能用"的类新提交 1. 新增 单文件、零依赖、 为什么不是"直接打开 2. 3. 修 4. 测试 +21 例(共 93,7 个文件):新增 5. 文档漂移修正:插件树 README 此前落后仓库树一轮(缺整个
|
追加:
|
| 情形 | 页面表现 |
|---|---|
有 clauses + 有 timings.json |
列出该张口播文案(P 可开 / 关) |
| 有 clauses、还没对时 | 只列文案,标题标「(未对时)」—— 口播还没做也能先看 HTML |
没有 clauses,或 --no-script |
面板与口播按钮完全不出现,画面占满整宽 |
顺带修掉一个误导标签:底部提示原写成「X 关配音画对比」(本意是「关动效 / 画面对照」),读起来像是在管音频。现在统一写作「X 动效 / 关动效 对照」,测试里加了断言:页面不得出现「配音」字样。
3. 响应式(此前只考虑了桌面)。 原来右侧固定 320px 面板 + 固定行高的顶/底栏,窄窗口和手机上挤成一团。现在:顶栏/底栏可换行、话题名过长省略号截断;窄窗口与手机上口播面板收成底部抽屉并默认收起(画面优先);手机给触摸条按钮 + 左右滑动翻页;总览网格按宽度自动列数;高度用 100dvh(免得被手机地址栏切掉)。
实测(Playwright,五档视口 + 触屏模拟):1600×900 / 1024×600 / 800×600 / 390×844 / 360×640 全部零横向溢出、零控制台报错,触摸条无标签截断;390×844 上左滑确实翻页;窄屏下点「口播」画面从 385→755px 高。
另外把「还没对时」这一档做实用:没有 timings.json 时,副本按 HTML 里实际用到的 stage 等间隔排(0.3/1.3/2.3s),页面顶部黄条如实标注「不是成片时序」—— 占位值会把动画全挤在 2 秒内,那才是真看不懂。
测试 99 例(+6:不做计时器 / 不得出现「配音」字样 / 响应式与触摸 / 口播三态 / 等间隔兜底)。
|
Seventh-round audit (1.7.3, commits Our own standards pass over the current head found that five containment tests could pass vacuously: on a filesystem where neither symlinks nor junctions can be created, they ended with a bare early Reproduced in both tool-less sandbox shapes after the fix — links creatable: 237 tests, 0 fail, 23 skipped; links not creatable: 209 pass, 0 fail, 28 skipped. The five formerly vacuous passes are exactly the five extra named skips; no shape reports a pass it did not earn. Development tree and both published trees: 237/237, 0 skip (with tools present). Small dedups from the same pass: Spec axis of the same round re-verified the standing public claims end to end — correction sweep (no standing "22 skipped" claim), 237/237 reproduction with tools and 0-fail reproduction without, description 977 chars, |
…) + 测试基建去 file:// 拼接/tmp 清理; PR body 沙盒句已同步双形态数字(237→238)
|
Eighth-round audit (1.7.4, commits
Evidence, reproduced today: development tree and both published trees (tools present): 238 tests / 238 pass / 0 fail / 0 skip (14 files). Tool-less sandboxes: links creatable → 215 pass / 0 fail / 23 skipped; links not creatable → 210 pass / 0 fail / 28 skipped, every skip naming its reason. |
hetaoBackend
left a comment
There was a problem hiding this comment.
Request changes for exact current head 50987c1f4e97e294713ff61c723435a3100e20f2.
The new path/network policy and tests close many earlier findings, but current-head blockers remain:
- Leaf symlink writes escape the project.
preview-page.mjs:498-500validates onlypreview/play; writes at:572,:576, and:612use plainpath.join/writeFileSync. Existing01.html,01.nofx.html, orindex.htmlsymlinks can therefore redirect writes outside the project. ApplysafeOutto every leaf and add a canary proving an external target is unchanged. safeRelstill accepts an ancestor symlink for an existing project root.tools.mjs:144permitsinside(real, realRoot), so a project-local link to the project's parent can be treated as valid. Allow that fallback only while the root itself does not exist; once it exists, require equality or containment beneath the canonical root. Add an ancestor-symlink regression.- Single-file ASR output bypasses containment/no-clobber.
asr.mjs:156-166writes arbitrary--outpaths directly and overwrites existing files. Bring this path under the documented project boundary and explicit force/no-clobber policy, with tests. prep-image --crophas no project containment contract.prep-image.mjs:59-63,83prevents overwrite unless--force, but still accepts arbitrary output paths. Either enforce project containment or explicitly document this command as a general-purpose exception; do not leave it inconsistent with the README's write-boundary promise.- No exact-head executable CI evidence yet. The fork runs exist but are gated as
action_requiredwith no jobs; the only visible PR check is skipped[code]smith. A maintainer must approve the fork workflows and obtain real smoke/negative-test results before merge.
Confirmed fixed on this head: ID/traversal checks, directory-symlink containment on the main render path, recursive-delete guards, most no-clobber behavior, official ASR endpoint allowlisting, and per-hop SSRF/private/file/redirect/size policy. The remaining findings above are specific to the current files, not carried over from the prior head.
…的写穿洞)/preview-page 三叶子收监/asr --out 收监+不覆盖/prep-image --crop 例外声明(238→241)
|
Thank you for the precise review — all four code findings are real, and all four are fixed in 1.7.5 ( 1. Leaf symlink writes in 2. 3. 4. 5. CI evidence — agreed, and this one is on the maintainer side. The fork's workflow runs sit at Evidence, reproduced today at Version is 1.7.5 in both |
… body 沙盒句改版本无关表述(241→242)
… body 沙盒句改版本无关表述(241→242)
… body 沙盒句改版本无关表述(241→242)
|
Ninth-round audit (1.7.6, skill repo
Evidence at the current tip: development tree and both published trees (tools present) 242 tests / 240 pass / 0 fail / 2 named skips (the two file-symlink canaries on stock Windows); tool-less sandboxes in both link-capability shapes 0 fail (25 and 31 named skips respectively); All four findings from your third review remain fixed and covered by the regression tests described in the previous comment. Requesting re-review at the current tip — and, when you get a chance, approving the fork's workflow runs so the scoped CI can execute on the PR. |
…此类清零"自证被独立清点推翻) + 明文更正(242→243)
|
Tenth-round audit (1.7.7, skill repo Our 1.7.6 changelog said a sweep found "no other member of this class" (derived-but-unjailed leaf writes). The spec axis of the tenth audit re-did the write-site inventory independently and found exactly one more: Also in 1.7.7: Evidence at the current tip: development tree and both published trees (tools present) 243 tests / 240 pass / 0 fail / 3 named skips (the three file-symlink canaries on stock Windows); tool-less sandbox shapes 0 fail with every skip named; |
… canary 不可满足断言 + body 计数改能力不变量表述
|
Eleventh-round audit (1.7.8, skill repo
Evidence at the current tip: development tree and both published trees 243 tests / 0 fail (240 pass + 3 named file-symlink-canary skips on stock Windows); tool-less sandboxes in both link-capability shapes 0 fail (26 and 32 named skips); |
|
Twelfth-round audit — convergence. Both axes report no serious findings, and this rounds off the loop that began with your third review.
Current state at All twelve of your review findings across three reviews are closed with named regression tests, and our own eleven additional audit rounds each closed what they found the same way. The remaining gate is yours: approving the fork's workflow runs will put the full suite's execution on the record for this PR. Requesting re-review at |
…双语安装段收束)+ 付费配额披露 + 重定向循环合一(--get 补环/0字节守卫)+ ffprobe 探测收进 tools + canary 扩展逐张叶子(243→244)
|
Thirteenth review round processed (1.8.0, skill repo Hard violations — fixed
Duplications — closed (the three most concrete of your items 4-8)
Judgement calls — disclosed, not changed (all from your items 3-5, 8, 9)
On your Spec notes — agreed on all: the four code fixes closed as you assessed them (canary scope now widened as above); the CI gate remains maintainer-side ( Evidence at |
…残留: 裸 cp -r 源未指明 + 通用 skills CLI 路线)
|
Linux execution of the full test suite — closing the open "CI evidence" item. The scoped https://github.com/Wzdhehe/html2video-for-mcode/actions/runs/35433539493 Frozen results of that run: 244 pass / 0 fail / 0 skipped / 0 cancelled — every test executed, no capability-based skips. In particular, the three file-symlink canaries (preview-page leaf The workflow re-runs on every push to the mirror, so this evidence stays current. Once the scoped smoke workflow is approved on this PR, the same suite runs on the PR itself. |
|
cc @hetaoBackend for a re-look when you have a moment — the Linux run linked above closes the open CI-evidence item, and the scoped smoke workflow on this PR needs one approval click to run here. |
Wzdhehe
left a comment
There was a problem hiding this comment.
Code Review · PR #41 (html2video-for-mcode · 1.8.1)
Verdict: ready to merge pending the maintainer actions listed at the bottom.
This is the largest single-plugin PR I've seen in this repo (53 files, ~5k lines of scripts + ~3.2k lines of tests, ~50 KB SKILL.md). The author has done ~13 rounds of self-audit on the published skill, and the only third-party review actions so far are from the requested reviewer hetaoBackend, who has not yet posted comments — so this is mostly a standards/spec pass.
What's in the PR
A complete skill for turning an outline into a narrated MP4: HTML slides with staged entrance animations, TTS voiceover, ffmpeg assembly with cover/transition/BGM handling, and ASR-based reverse verification. Ships as a standalone plugin tree at plugins/Wzdhehe/html2video-for-mcode/, plus a scoped smoke-CI workflow at .github/workflows/html2video-for-mcode-smoke.yml. 27 plugins now hosted in the repo after merge.
What I checked
I cloned the PR branch, ran the host's scripts/validate.mjs, ran the skill's tests locally (subset, ffmpeg available, chromium not), inspected every script, the security modules, the test canaries, the workflow, and the host's existing conventions.
$ node scripts/validate.mjs
OK plugin Wzdhehe/html2video-for-mcode
…
Validated 27 hosted Plugins and all examples.
Test runs in this sandbox (Node 22, ffmpeg present, chromium absent): safe-paths 22/22, fetch-policy 77/77, endpoint-allowlist 6/6, no-clobber 13/13, preview-page 30/31, review-round3 6/7, chart-kit 7/7, render-smoke 1/6 with 5 chromium-skips, cover-transition 3 skips (chromium). The chromium-skips are by design and name themselves.
What is genuinely good
1. The security model is unusually thorough for a skill plugin. Three layers worth calling out:
- Path containment (
scripts/tools.mjs):safeId/safeRel/safeOut/assertContaineduse lstat-not-existsSync traversal to catch dangling symlinks, and they fail-closed whenrealpathcannot resolve a link. The "1.7.5 ancestor-link regression" — a link inside the project pointing at the project's parent, which used to slip past the "root doesn't exist yet" fallback — is caught and pinned by a canary test (safe-paths.test.mjsline 99-120). I verified the canary actually creates the shape and asserts the rejection happens before any write. This is exactly the property tests should pin. - SSRF / DNS rebinding (
scripts/url-policy.mjs): end-to-end — block IPv4-mapped IPv6, NAT64, 6to4, fc00::/7, fe80::/10, the 13 RFC-reserved IPv4 ranges, trailing-dot FQDNs, and bare hostnames; andassertResolvedHostre-checks the resolved address right before each request so a hostname that flips DNS to 169.254.169.254 mid-flight is still rejected. Test coverage includes both the static and the post-DNS paths. - API-key exfiltration: ASR key only goes to two allowlisted domains;
--base-urlandMINIMAX_BASE_URLare validated against the same allowlist; the only escape is an explicit--allow-any-endpointthat warns loudly.MINIMAX_BASE_URL=https://evil.exampleis verified to exit 1 before any network call.
2. ffmpeg filter-injection surface is closed. width/height/fps/bgm.volume/bgm.fadeIn/bgm.fadeOut are all clampNum-validated in build-video.mjs (and the same check exists in capture.mjs for width/height, where the value reaches CSS/JS). Each one has a canary test that feeds an actual injection string like "0.5,amovie='C:/x',volume=0.5" and asserts the rejection names the field. I think this is the right place to be paranoid — ffmpeg -filter_complex accepts arbitrary expression syntax.
3. The "managed CSS region" design is novel and well-implemented. scripts/css-kit.mjs defines content-rev delimiters, and tokens-template.mjs exports a single source-of-truth body with a TOKENS_REV hash. check-slides, capture, build-video, preview-page, init-project --check-css, and init-project --upgrade-css all share the same expected content and the same status vocabulary. The reason this matters: a stale tokens.css will render successfully with the wrong styles (new classes get no rules, old rules win by cascade) — exactly the silent failure mode that motivated the design. Capturing the freshness check at every entry point is the right call.
4. Test design is honest. Tests aren't smoke tests; they're property tests that construct the threat model and assert the rejection. The "self-correcting changelog" (1.7.7/1.7.8 entries note that an earlier canary had an unsatisfiable assertion because the test planted the symlink it then measured) is rare transparency — most projects would silently delete such a test.
5. CI hygiene. Action versions are pinned to full 40-char SHAs (actions/checkout@3d3c42e…, actions/setup-node@82076278…) with # vX.Y.Z comments. permissions: contents: read is set. Trigger paths are scoped to this plugin's directory plus the workflow file. --test-timeout=300000 is set so the chromium-installed run doesn't get killed mid-encode. The host's own ci.yml has a typo (42-char SHA on actions/checkout); the author notes this in the workflow comment and correctly doesn't touch the host's file.
6. License + disclosure are correct. MIT, third-party notices for the adapted lewislulu/html-ppt-skill material with date + scope of adaptation, paid-account disclosure in both READMEs ("MiniMax account's paid, quota-metered balance") per the round-13 finding.
Concerns / questions
These are observations, not blockers — most have defensible answers in the PR body.
1. homepage and repository both point at this repo. The validator passed them, but homepage pointing at the hosted directory inside the host repo is a deliberate design choice (per round 13) to make the host repo the canonical install surface rather than the author's standalone mirror. This is a host-side policy question rather than a code defect — flagging it for the maintainer's awareness.
2. review-round2.test.mjs / review-round3.test.mjs are named by audit provenance, not topic. The author discloses this in CHANGELOG 1.8.0 ("renaming would orphan the suite list documented in both READMEs"). Defensible — I'd just want the topic-level describe blocks inside to be self-contained so a future contributor doesn't need to read the audit history to find what each one covers. They appear to be.
3. The scoped smoke CI is still action_required. The author worked around it by running the same suite in the Actions of the standalone mirror (run 35433539493, 244/0/0). That's fine evidence but it's not the host's CI. The maintainer should approve the workflow so it runs on this PR's check suite before merging — otherwise the only CI evidence is from the author's own infra.
4. Several tests take >30 s in this sandbox. safe-paths took 110 s (it constructs real symlinks via subprocess mklink /J fallback and runs four full canary paths). css-kit timed out at 30 s. These are all pass — just slow. Worth knowing for the maintainer's local sanity-check budget; not a defect.
5. Playwright Chromium is installed via npx playwright install --with-deps chromium in the workflow. On a sandbox runner this needs --no-sandbox in some configurations; the author doesn't set it. I didn't see a Chrome-launch failure in the tests I ran, so the default works on ubuntu-latest. If the maintainer hits a sandbox error on a fork or self-hosted runner, that's where to look.
6. The BGM loop-seam is not explicitly tested. When script.bgm is configured and the source clip is shorter than the video, build-video does -stream_loop -1 and afade. There's no test asserting that the seam at the loop point is inaudible or at the right phase. Probably fine (most BGM clips are written to loop cleanly) but worth a manual spot-check if the maintainer can spare 10 minutes.
7. scripts/preview-page.mjs is 637 lines and embeds the play page as one long template string. The author discloses this as "a structural trade-off, not a defect" in 1.8.0. Reasonable — splitting a single HTML artifact across many small strings would be worse. Just noting that any future contributor who touches this file will need to be careful not to break the embedded JS.
Maintainer actions before merging
- Approve the
.github/workflows/html2video-for-mcode-smoke.ymlfirst-run gate so the workflow actually executes on this PR. The author's external run is good evidence but the in-host CI tick is the contract. - Confirm the host's policy that
homepagemay point inside the host repo (it currently does — round-13 fix — and the validator accepts it). If that's the standing policy, no action; if not, this PR needs the URL reverted. - Optional but valuable: spot-check a 60-second sample render on each of the three real runtimes the README lists (Windows / macOS / Linux). The Chromium-skip test count drifts per-platform — that's by design — but a real cross-platform smoke is cheap.
Bottom line
The PR is unusually well-engineered for a skill plugin submission. The security model has the right threat model in mind (path traversal, DNS rebinding, ffmpeg filter injection, key exfiltration), every concern is pinned by a canary test, and the self-audit is transparent. The action items for the maintainer are mostly about CI gating and policy decisions, not code fixes.
…vider api REST fallback (1.9.0)
|
1.9.0 — ASR toolchain simplification:
|
…uoting, scrub mmx child env, assert full argv mapping
|
1.9.1 — sixteenth review round fixes (two-axis review over the 1.9.0 increment; every finding personally reproduced before fixing):
Verification: three trees locally 248 tests / 245 pass / 0 fail / 3 capability skips; real end-to-end against mmx 1.0.26 re-run keyless with word-level timestamps (exact transcript, |
…EY assertion regression-proof, disclose Windows-only quoting coverage
|
1.9.2 — seventeenth review round fixes. Both axes caught the same two defects in 1.9.1's own fix; each was personally reproduced before fixing:
Verification: three trees locally 248 tests / 245 pass / 0 fail / 3 capability skips; real keyless end-to-end re-run against mmx 1.0.26 (exact transcript); upstream validator OK; remote trees byte-verified after push (51 + 52 files, zero drift, zero |
What changes
Add Plugin:
html2video-for-mcodeatplugins/Wzdhehe/html2video-for-mcode.A Skill that turns a topic, outline, or script into a narrated MP4: HTML slides with staged
entrance animations, a TTS voiceover, burned-in subtitles, and an ASR pass that verifies the
voiceover says what the script says.
User value
After installing, a MiniMax Code user can ask in plain language:
and get:
What makes it more than a slide exporter:
derived from the TTS audio via ffprobe, so the picture can never lag behind the voiceover.
is just a headline.
screen-recording.
images, external resources, or entrance animations without an animation class: the silent
failure modes that otherwise ship a broken-looking video while every script reports success.
Plugin submission checklist
plugins/<github-owner>/<plugin-name>.plugin.jsonname matches the Plugin directory.README.mdincludes a real example prompt and expected result (bilingual:README.md+README.zh-CN.md).LICENSEandplugin.jsondeclare an open-source license (MIT).(Node 18+, ffmpeg/ffprobe, Playwright Chromium; MiniMax API key or Token Plan for voice and
ASR; Windows/macOS/Linux; PowerShell caveat documented).
(
api.minimaxi.com/api.minimax.iofor ASR only when invoked; voice via mcode connectorsor
mmx-cli; image fetching only from URLs the user passes; no telemetry).are included. The ASR script reads its key from an environment variable or CLI flag at
runtime and never writes it.
TODOhas been replaced.publish/validate-plugin.mjs(an authoring-side tool, deliberately not part of the shipped plugin tree): it stages the Plugin tree into the host checkout, verifies all 52 files are fingerprint-identical (sha256) to the source tree, then runs the upstream validator →OK plugin Wzdhehe/html2video-for-mcode, exit 0. (Validating the host'snpm run checkalone is not sufficient: it scans the staged copy under_official-plugins/plugins/**, so a stale staged copy yields a green result that proves nothing.)Evidence
The validator also prints a
Validated <N> hosted Pluginssummary line; N counts every plugin in the checkout and grows as the host merges unrelated plugins (26 → 27 → 28 upstream within the last week; 29 including this PR at the current main snapshot). That line is checkout state, not a property of this PR, so it is not quoted above.All 244 skill tests report 0 fail in every environment measured: the development tree, both published trees, and tool-less sandboxes in both link-capability shapes. Skips are capability-dependent and always named: 0 skips with tools on a symlink-capable system; on stock Windows without Developer Mode the three file-symlink canaries skip by name; in tool-less environments every tool-needing test skips with its reason. When ffmpeg / ffprobe / Chromium are absent (the monorepo's own root-level
node --testruns in exactly that environment), every test that needs one of them skips with its stated reason — verified in tool-less sandboxes in both link capability shapes: 0 fail, every remaining case skipping with its stated reason (nothing pretends to pass). Skip counts are environment-dependent and drift as each release adds tests — the invariant is stated here, per-version measurements live in the CHANGELOG — and the scoped workflow installs the tools and runs the whole suite for real. The host repository's owntest/hosted-plugins.test.mjscontains a symlink fixture that fails on a Windows checkout without Developer Mode (EPERM: operation not permitted, symlink …); that is a pre-existing host-side issue, it reproduces on a clean checkout without this Plugin, it passes on the CI'subuntu-latest, and this PR does not touch it. The part ofnpm run checkthat inspects Plugins passes with exit code 0 when the staged copy is current.Manual end-to-end test (Windows, Node 24, ffmpeg-static):
plan-timings.mjs→ every slide duration andstage entrance time derived from measured audio.
check-slides.mjscorrectly rejects slides with undefined CSS variables (--coral-a/--coral-b),missing images, external font links, and
data-stagewithout an animation class; clean slides pass.capture.mjs --mode motionproduced 143 frames for an 8.6s slide; frame-diff (PSNR) confirms thestaged entrance actually renders at its scheduled time (
infbefore the entrance, ~14 dB across it).build-video.mjs --asrproduced a 14.20s MP4 matching the expected duration exactly, full decodeclean,
out/subs.srtgenerated, and per-sentence ASR parts produced.#FFB84Don white)is rejected with a non-zero exit code, a compliant one (
#C2410C) passes.traditional characters (Cantonese voice) and mismatched numbers fail with exit code 1.
Full disclosure of dependencies, network access, and data handling is in the Plugin README.
Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.