You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A host check for each speaker's voice pitch in dialogue groups: python3 code/voice_f0_check.py --project <slug> --ep <ep> --group <grp> (2026-10-22, #148). The video-generation role notes and WORKFLOW.md required a per-speaker acoustic check (voice_f0_check) on every dialogue group, but no such script ever shipped, so agents either wrote their own copy into each project's code/ (five different versions) or marked the check as not run. The new command splits the group clip by speaker (from the clip's boundary_map and the shot list's on-screen lines), compares each speaker's median pitch with that speaker's own voice sample from the group prompt's audio_refs, and reports PASS (within 20%), WARN (20–70%, noted in the hand-off only) or FAIL (over 70%, or two speakers each closer to the other's sample), with per-speaker and per-segment detail; --json gives machine-readable output. It only judges a group whose audio_plan is dialogue, whose project has 对白配音 = 视频原声 and whose prompt has audio_refs; otherwise it reports SKIP and exits 0. A FAIL sends the group to the re-roll flow, limited by the 重跑次数 setting. The single-clip form agents were used to, <clip> --seg <start>:<end>:<character-id>, also works. The FAIL line was planned at 35%; on 263 real dialogue groups that failed 32% of them, mostly because of steady gaps between a character's sample and the way the model voices that character, which a re-roll does not fix, so it was raised to 70%, roughly the gap between a male and a female voice (8% of those groups fail at that line, about 9% with the swapped-speakers rule).
A host check for the Bible skeleton: python3 code/check_bible_skeleton.py --project <slug> (2026-10-22). The Bible set-up task (p0-bible-init) was accepted on "skeleton schema complete", but nothing in the program checked that, so each project's memory-bible agent proved it with its own one-off check and the criteria differed from project to project. The new command reports PASS / FAIL / WARN item by item: the Bible files and folders listed in agents/WORKFLOW.md §2 exist and parse; the paths and top-level keys the program itself reads are present, with empty containers accepted (characters/index.json#characters, creatures/index.json#creatures, scenes/index.json#scenes, props.json#props, costumes.json#costumes, the top-level style_fragment_en / style_fragment_ui strings in style.json, and color_script.json#episodes, plus the alternative keys the program also reads); every file and folder name under bible/ is ASCII; and the change log bible/changelog.md exists. Spellings that only some parts of the program read give a warning instead of a failure. Any failure exits non-zero; --json gives machine-readable output. p0-bible-init in agents/workflow.yaml now lists bible_skeleton_ok and ascii_filename, and the memory-bible role notes and agents/WORKFLOW.md say what the skeleton must contain and name the command. Existing projects are not changed; projects started before the creature registry, the two style strings or the colour-script format was introduced report those parts as failing.
Whitebox layout check: a scene's aspect-ratio check can be waived with a defect the user signed off (2026-10-22).code/whitebox_layout_check.py fails a scene whose dimensions_m X:Z differs from the top-view image by more than 2%. When the top view is a generated sketch the user has accepted as out of proportion (the model is built to the set card instead), that check failed on every run with no way to record the waiver. A scene's whitebox.json can now carry layout_check_waiver: {"defect_id": "DEF-…", "items": ["aspect"]}. The check reads qa/defects/<defect_id>.json and counts the aspect item as waived only when that defect is waived (or waived_by_user), its waiver.signed_by is the user (an agent or full-auto does not count) and its scene_id is this scene. The report then lists the item under waived with the defect ID, and the scene passes unless something else fails. Only aspect can be waived; geometry outside the image still fails. When the conditions are not met, the aspect item still fails and says why the waiver was not accepted. The key is not compiled into the scene and does not count toward the reviewed-camera fingerprints, so adding it does not make reviewed shots or exported videos stale. The scene-modeling role notes and docs/whitebox.md describe it for scenes whose aspect deviation the user has signed off (#154).
全自动模式: a per-project checkbox on the 工作流预览 page that removes manual sign-off (2026-10-22). The summary row (the one with ✏️ 修改) gains a 全自动模式 checkbox on the right, off by default, with an i button explaining it. When checked (POST /api/v1/projects/<project>/full-auto {enabled}, stored per project in state.json#full_auto), every sign-off card of that project is answered 签字 by the service as soon as it is created, and every other confirmation card gets its default option, with no pop-up or desktop notification; the producer is then woken with a full-auto sign-off receipt and reviews the gate as usual (gate JSON signer full-auto). The existing pre-sign-off checks still apply (H3V missing prompts, H3W blocking whitebox issues, H3P dispatched prescriptions / post_ok FAIL): a blocked card stays open with the reason shown on it, dispatch.py --confirm … --sign prints 自动签字未通过:<reason> instead of waiting, and when no task is waiting on the card the producer is told to fix it and raise the sign-off again. Agent prompts get a 全自动模式 section only while the box is checked: wherever the spec says to ask or escalate to the user, the agent decides by the standard of getting the film finished (keep finalized settings and assets, safest option with least rework, report what it chose and why); the retry count setting still applies, and once retries run out the best existing version is kept and major/minor defects are waived (signed_by: full-auto); content-safety and copyright blockers are never waived, and the mode never changes settings, turns on paid options, races agents or switches engines. Checking the box immediately answers the cards already waiting, creates and signs any unlocked gate that has no card yet, and, if that did not wake the producer, asks it to check whether it is waiting on a user decision. Unchecking restores manual sign-off; automatic sign-offs already made stay. It does not replace 🤖 自动运行, which still decides whether an idle pipeline is resumed.
Ctrl+Enter adds a message to the task an agent is already running (2026-10-22). In the console, Enter still sends a new task (queued behind the agent's running one); Ctrl+Enter (⌘+Enter on macOS) sends the message into the running task instead (POST /api/v1/runs/steer), where it reaches the model after the current tool call finishes. This works on the Claude Code, Pi, Agentics and OpenCode 2 engines: Claude Code runs now take their prompt on stdin (--input-format stream-json --replay-user-messages), Pi and Agentics runs use --mode rpc (added messages are sent as steer prompts), and OpenCode 2 receives them through the session inbox (opencode api --standalone, delivery: "steer"). On the other engines (Codex, Kimi Code, Grok Build, Antigravity, OpenCode 1.x) the message is merged into the agent's queued task, or sent as a new task if none is queued. A message sent while the task is still queued or starting is added to its prompt, and one that was not delivered before the task ended is sent as a new task (a task stopped by hand only gets a note). The chat marks each such message with where it went, and the input placeholder mentions the shortcut in all interface languages.
Auto backup on sign-off, on the 版本管理 page (2026-10-22). A new 💾 自动备份 card (off by default) takes a backup folder; when on, every sign-off (answering 签字 on any sign-off card, from the console, a preview page or Feishu) zips the whole project folder into that folder in the background as <project>-<YYYYMMDD-HHMMSS>.zip. The zip wraps the project in a <project>/ folder, so it can be restored with › 导入项目…. The setting is global (state.json#auto_backup, API GET/POST /api/v1/config/auto-backup); the folder must be an absolute path outside data/projects and is test-written on save. The card shows the last backup (or its error) and how many backups of the current project the folder holds; a failed backup also raises a system notification. Already-compressed media (video, images, audio) is stored without recompression; a sign-off during a running backup queues one more backup; old backups are never deleted automatically. 📁 选择… next to the folder field opens the system folder picker and saves the chosen folder: the desktop app uses its own dialog (pickDirectory, after the desktop app is rebuilt); otherwise the service opens it on its own computer (POST /api/v1/config/auto-backup/pick-dir: macOS choose folder, Windows FolderBrowserDialog, Linux zenity / kdialog). The button only appears when the page is opened from the same computer (localhost / 127.0.0.1) and not on a desktop app connected to a remote service, so the dialog never pops up on another machine.
Changed
Whitebox: applying the pending decisions accepted at the H3W sign-off no longer asks for a second sign-off (2026-10-22). Signing H3W (白模确认) accepts the recommended option, or the default, for each pending whitebox decision the user has not answered (recorded by=sign:g6w). The producer's role notes and docs/whitebox.md sent the whitebox agent straight on to export after applying those, but the whitebox agent's own notes said to get H3W signed again first, so the two agents disagreed. All three now agree: decisions already on record at sign-off (accepted by signing, or answered before it) are applied and go straight to export. H3W is signed again only when the user makes a new decision after sign-off that changes the picture, or when the whitebox agent fixes a mistake in the whitebox itself. The H3W sign-off card's pending-decision summary now ends with "Signing accepts the recommended option for each unanswered item; applying them afterwards needs no re-sign." (in Chinese when the interface is Chinese) (#158).
Workflow DAG: skipped and waived dependencies now count as satisfied (2026-10-22, #149). The dispatch frontier (dagcheck.py --frontier, the idle watchdog, the host's sign-off card guard and the full-auto sweep) used to unlock a node only when every dependency was done / passed / passed_human_override. Anything downstream of a condition-skipped node never became dispatchable, e.g. cv3-engine after cv2-beatsync is skipped for code-composed music, contradicting WORKFLOW.md §10.3 rule 6 and the project-type DAGs. Skipped and waived dependencies now count as met; cancelled still does not. Sign-off gates (human nodes) are guarded: a gate whose dependencies are all skipped / waived, with none actually completed, is not treated as unlocked and gets no sign-off card. The workflow page marks gates as waiting for sign-off by the same rule. The main_dag_on_start.skip lists are completed so new projects don't leave main-flow nodes stuck: audio-to-video, digital human and demo video also skip p0-bible-init, p0-parse (p0-parse-* only matched the expanded batches), p0-merge, g0, g6s, g6w, g7p and g9p; mashup and the derivative-novel plugin add g6s, g6w, g7p and g9p (mashup still runs p0, g0 and p1 structure/events). In existing projects, nodes waiting only on skipped / waived dependencies now show up as dispatchable, and gates with at least one completed dependency get their sign-off card at the next service start or run end.
Demo videos: the per-section scene-coding tasks now run in parallel (2026-10-22). In a demo-video project, the producer sends one cv3-sec task per section to scene-coder. scene-coder keeps a conversation across its tasks, so it used to run them one at a time. All but the first sat queued, even though each section only touches its own sections/<section>.js. Section tasks now run side by side, up to the per-agent concurrency limit in Agent 高级设置. Each starts a fresh conversation and does not overwrite the one scene-coder resumes. Its other tasks (engine, full render, revisions) still run one at a time and continue that conversation. The task is recognised from the task id at the start of the dispatch message (执行工单 cv3-sec-grp0010…) or a task_id: line; if neither is there, the task runs one at a time as before. Concurrent codevideo.py render --section runs now take turns updating codevideo/render.json, so one section's render record no longer overwrites another's.
Fresh desktop installs default the music model to Agentics (2026-10-22). On a new desktop install, 生成模型 › 音乐 now starts on Agentics (signed-in account, default profile minimax-music-3) instead of 代码作曲, matching the image, video and TTS defaults; the browser version still defaults to 代码作曲. Installs that have already saved a music channel keep it, the 代码作曲 tab stays first, the sound-effects default (代码音效) is unchanged, and 演示视频 projects set to music_source=code still compose in code.
Smart Assignment for DeepSeek (opencode engine) now uses DeepSeek V4.1 Flash for the lighter tier (2026-10-22). The OpenCode Go model opencode-go/deepseek-v4-flash has been removed from the model catalog (OpenCode 2 rejects it as "Model unavailable"), so the smart_deepseek low tier moves to opencode-go/deepseek-v4.1-flash; the creative-core tier stays on opencode-go/deepseek-v4-pro. The opencode model picker's offline fallback list and the strategy's description (in all interface languages) are updated to match. Models picked explicitly per agent are not changed.
OpenCode engine: OpenCode 2 is now supported and is what one-click install sets up (2026-10-22). One-click install runs curl -fsSL https://opencode.ai/v2/install | bash (the old script only installs 1.x), or npm install -g @opencode/cli on Windows. The engine checks opencode --version and keeps the 1.x behaviour for 1.x installs. With 2.x: runs use --standalone (a private server tied to the run) instead of OpenCode's background service; the Thinking Effort level is passed as -m model#level only when that model offers the level (2.x rejects unknown levels, where 1.x ignored them); before resuming, the saved session is checked, because 2.x silently starts an empty session under a missing id, and a missing session falls back to a fresh one with the full role; stopping a run first sends SIGINT so OpenCode marks the session interrupted (a killed 2.x session would otherwise be resumed later by OpenCode's background service), and the process group is killed 3 s later; stdin is closed and PWD is set to the working directory (2.x waits for stdin to end and takes its working directory from PWD); the last step's token usage, often missing from 2.x's JSON events, is filled in from opencode session export; shell / subagent tool calls show as Bash / Task. Sign-in status and the OpenCode Go usage key come from opencode auth list / auth export (2.x keeps credentials in its database, not auth.json), the sign-in terminal uses auth login --standalone, the model list drops --refresh, and footage analysis also runs --standalone. Upgrading from 1.x keeps saved sessions. Not covered: on Windows a stopped 2.x run is still force-killed, so OpenCode may resume it later.
The built-in engine is now Agentics, a bundled Pi Coding Agent, replacing DeepAgents (2026-10-22). The engine formerly shown as deepagents appears as agentics in the top bar and agent model settings (its id stays deepagents, so saved choices, dispatch.py --engine and per-agent settings keep working). It runs Pi Coding Agent 1.3.0, which ships with the desktop app (pinned in vendor/pi, about 20 MB compressed) and runs on the app's own Electron Node, so nothing extra has to be installed; from source, run make install-pi (Node.js 22.19+). Its language model is the active channel of 🎨 Generation Models → Language Model/Agentics (renamed from "DeepAgents"): each run gets a private pi configuration with that endpoint and model, the API key is passed only through the environment, and the user's own ~/.pi/agent settings, extensions and logins are never used. The separately installed pi engine is unchanged. Sessions resume with --session; the thinking-effort setting maps to --thinking (not sent to local models). AI shot analysis in the footage library can now read the contact sheet when the chosen model accepts images. The DeepAgents runner and its Python stack (deepagents, langchain-openai, langgraph-checkpoint-sqlite and 40 transitive packages) are removed from the desktop Python runtime, pyproject.toml and the install-deepagents make target; the DEEPAGENTS_* environment variables no longer apply (AGENTICS_CONTEXT_WINDOW / AGENTICS_MAX_OUTPUT_TOKENS set the model limits instead).
New desktop installs start on the Agentics engine (2026-10-22). When neither the browser nor the service has a saved engine, the desktop app now selects agentics (the web version still picks Kimi Code for Chinese and Claude Code otherwise), and Language Model/Agentics defaults to the Agentics channel. If that channel is active but no account is signed in, the console shows "Sign in to get started" with a Sign in button that runs the desktop sign-in; the web version explains that the channel needs the desktop app or a local model. The Cloud model and OpenRouter tabs of Language Model/Agentics (and the matching top-bar channels) are hidden unless one of them is already the active channel.
ComfyUI image templates now share one naming scheme: -t2i for text-to-image, -i2i for image-to-image (2026-10-22). The 14 comfy/image-*-api.json templates (and their .md docs) are renamed: text-to-image templates, which had no marker, gain -t2i (e.g. image-flux2-dev-fp8-t2i-api.json), and the image-to-image ones replace -ref10 / -ref3 / -styleref / -img2img with -i2i (e.g. image-flux2-dev-fp8-i2i-api.json, image-qwen-image-edit-2511-i2i-api.json, image-z-image-turbo-bf16-i2i-api.json). Saved settings that point at the old names (genconfig.jsonworkflow / ref_workflow) are rewritten to the new names on load, and an old path still resolves if it reaches the runner directly; custom templates are untouched. The number of reference images a template takes is still read from its LoadImage nodes, not from the file name.
Agent role notes now write entity IDs one way, matching what the service reads (2026-10-22). The example IDs in the agents' role notes (SOUL.md) disagreed with each other and with the service, so files written by different agents could not reference each other and some IDs were silently missed: the screenplay example wrote scene:sc-cangjinge and char-linxiao (the service only picks up SCN- + digits and CHAR-…), directing / art / video examples wrote characters as c003, char-001 or chr_lin_feng and scenes as s012, the scene index wrote GEO-… where geography defines geo_…, creatures were cre_… in one note and CRE-* in another, events were evt_…, EV-0102 or ev_0042 instead of ev0042, props were prop_017 while the storyboard only picks up PROP-… (most projects already use PROP-0017), the economy example referenced an undefined cur_lingshi_low, and the political example used camp_… IDs with nowhere to define them. agents/WORKFLOW.md (Phase 2) gains an 实体 ID 写法 table — namespace, prefix / form, example, where it is defined and whether the service parses it (scenes SCN-0012, characters CHAR-0003, creatures CRE-0012, events ev0042, costumes CHAR-0003_battle_02, geo_ / nation_ / faction_ / pol_ / camp_ / cur_ / rank_ / mnt_) — and the examples in 26 role notes now follow it. A currency grade is referenced as <currency id>#<grade id> (e.g. cur_lingshi#low, grades get an id), and camps are defined in bible/politics.json#camps[]. The table applies to new projects only: existing projects keep the IDs already issued in their bible/*/index.json and project write rules, with no renaming or migration. A new test (tests/test_soul_id_examples.py) flags the old forms if they come back.
Moderation model: Agentics is back as a channel and is now the default (2026-10-22). The 🛡 审核模型 card on the 🎨 生成模型 page has two tabs again — Agentics (the signed-in desktop account, the model and 测试) and OpenRouter (API key, model and 测试) — and its 生效渠道 dropdown defaults to Agentics. The Agentics channel calls TypeSafe's Jev decision model (typesafe/jev-1.13 or typesafe/jev-1.14) through the account's OpenRouter wrapper at /api/alpha/decisions, with the same request and response as OpenRouter. A configuration saved by 1.0.40 moves to Agentics, keeping the chosen model, only when it has no OpenRouter key and OPENROUTER_API_KEY is not set; one with a key stays on OpenRouter, and once saved by this version the chosen channel is never switched again. With Agentics active and no account signed in, the refusal judge and 测试 report that the desktop account is not signed in. The NSFW mode dialog now names the active channel instead of always saying OpenRouter. Not exercised: a real call through the Agentics channel (no signed-in desktop account was available).
生成模型 page: the 🧩 语言模型/Agentics card is now the first card, above the image model (2026-10-22). Only the order changes; the card's contents and behaviour are the same.
New-project wizard: the 代码风格 step no longer links to online sample films (2026-10-22). Choosing a full style no longer adds 「在线看样片 ↗」, and choosing the light style no longer adds 「在线看 OPUSCAR 98 ↗」; the selected line keeps the style name, category and one-line description, and the explanation below drops the sentence pointing to the sample link.
Fixed
Windows: agent tasks no longer fail at start with "构建 Agent 提示词失败:No module named 'fcntl'" (2026-10-22). Building any agent's prompt imported modules/script_keypoints, which imported the Unix-only fcntl module at the top (since 2026-09-13), so on Windows every agent task failed before it started. File locking now goes through one shared module, modules/file_lock.py: fcntl.flock on macOS and Linux, and on Windows msvcrt.locking on the file's first byte, waiting for as long as it takes as flock does. Concurrent writes to the shot-plate library, Canvas, the storyboard, screenplay notes, keypoints, NPC staging, the footage ledger, the Ark asset ledger and the dialogue voice ledger are therefore serialized on Windows too instead of overwriting each other; only when neither lock is available does the write go ahead unlocked. Files the service writes and reads — change and sign-off records, project settings, the novel and brief, NOTES.md, the README, dag.json and others — are now read and written as UTF-8 explicitly, so running from source on a Chinese-locale (GBK) Windows no longer garbles them or fails to read them (an unreadable dag.json left the H3V sign-off card unbound to its gate); the desktop app already started Python with PYTHONUTF8=1. Reference-image paths saved by the shot-plate library now use forward slashes. Not exercised: the Windows lock path under real concurrency (covered by stubbed tests only) and a packaged Windows desktop build.
Skills added with "Load skill" are now installed in the data folder, so a desktop update no longer wipes them (2026-10-22). The skills dialog in the chat panel used to install an uploaded skill into the agent's own agents/<category>/<agent>/skills/ folder. In the desktop app that folder is inside the signed .app bundle (Resources/backend), which is replaced as a whole when a new version is installed, so every uploaded skill was lost on update; writing into the bundle also breaks its code signature (the same reason Python's __pycache__ is already redirected out of it). Uploaded skills now go to <data folder>/skills/<category>/<agent>/<skill>/SKILL.md (override with VIDEOAGENTS_SKILLS_DIR); the web version uses the same place and no longer writes into the repository's agents/ folder. The skill list reads both the built-in skills and this folder: skills you uploaded carry an "Uploaded" tag, insert their full SKILL.md path when clicked, and can be deleted from the list (click Delete, then Confirm Delete within 4 seconds); built-in skills cannot be deleted. If a later version ships a built-in skill with the same name as one you uploaded, the built-in one wins and your copy is shown greyed out as not in effect, ready to delete. Uploading a skill with the same name as a built-in one is refused. The prompt-skill check (code/prompt_skill_check.py) and run skill records find uploaded skills too. Skills uploaded into an older desktop build were stored inside the old app bundle and are already gone once a new version replaces it; save your own skills before updating and load them again afterwards.
Native lead-ins now split at sentence ends and dashes, and English lines are no longer cut mid-word (2026-10-22).modules/native_lead only treated commas, enumeration commas and semicolons as split points, so lines such as Wait. Come here. or 放回去。快走! got no auto-suggestion from sync_native_leads.py plan, and the proportional fallback cut at a character index (This w|ay — go!, 放回|去。快走!). Sentence-end punctuation now counts — Chinese 。!? directly (half-width ! ? too when followed by whitespace or a CJK character), ASCII . only before whitespace, with ellipses, decimals and common abbreviations (Mr. / Dr. / St. / vs. / etc. …) excluded — and so do the dashes — / --, still within the first-clause ≤60% limit. Without a usable split point, word-counted lines (time_cost.is_word_text, as in the #124 timing rules) snap to the nearest word boundary; the lead loses trailing whitespace. Auto-suggestions are offered only when a line can be split at punctuation within that limit. A line already synced into its group prompt keeps the split recorded in the prompt's native_leads[] while its text and s are unchanged, so check does not start failing and sync --write does not rewrite the prompt (or shift its H3V fingerprint) because of the new rules; the P8 trigger-word check now looks at the lead actually used.
render_whitebox.py --check-only now lists every shot whose camera contract went stale, not just the first one per group (2026-10-22). When several shots in one storyboard group had stale review fingerprints (for example after a wording change in each shot's composition.json), the compiler stopped at the first failing shot, so the group's error named only that shot. It now checks every shot of the group and reports them in the group's single error (one entry per shot, each starting with the shot ID); structural errors still stop the group at once. The first output line gains source_groups, the episode's real group count (groups still counts only groups that compiled), and the pending-decision summary now uses the same reading as the H3W sign-off gate, so groups that failed to compile are counted too. Groups that compile are unchanged (#157).
The machine-readable workflow no longer asks the voice agent for the retired per-group dialogue dry-voice tracks (2026-10-22). Since 2026-07-20 the voice agent produces one 3–5 s voiceprint sample per speaking character and age variant at project level (assets/audio/voice/refs/ + refs/manifest.json, registered in assets/audio/voice/casting.json), and the video model gets each speaker's own sample as a voice reference; the per-group assets/audio/voice/<ep>/lines/ tracks were dropped. The role notes and agents/WORKFLOW.md were updated then, but agents/workflow.yaml still listed lines/ as the output of each episode's p8-voiceprints task and checked it with four lines checks, so the voice agent could refuse the task or produce the retired tracks. p8-voiceprints now only checks that this episode's speaking characters (and variants) are covered by the project voiceprints and casting, adds what is missing, and is checked by voiceprint_coverage_main_100, sample_duration_3_5s, casting_bound and voice_collision — the same names as in agents/WORKFLOW.md and the voice agent's role notes. The p7-video note and the project tree in agents/WORKFLOW.md no longer mention dry-voice tracks. Existing lines/ folders are left as archives. Task graphs already built for existing projects (runs/dag.json) are not rewritten.
Agent commands written as python3 … keep working when that interpreter lacks a dependency or cannot load one (2026-10-22). Agents run host commands as python3 …, which resolves to whichever interpreter the engine's shell finds first — on the reporting Mac that was conda base, whose scipy 1.15.3 imports fine but fails at scipy.signal on macOS 27 (the OS loader rejects one of its compiled extensions), so 代码作曲 could not run and mixing would hit the same wall. When a host CLI (every code/*.py script, plus modules/genmedia.py, transcription.py, footage.py and footage_library.py) runs on an interpreter other than the service's own, it now test-imports numpy, scipy.signal / fft / ndimage / optimize, soundfile, Pillow, PyYAML and Playwright and, if any fails, reruns itself on the service's interpreter (on desktop, the bundled runtime); before, only a missing Playwright in seven scripts triggered this. The check runs before anything else, including on macOS's own Python 3.9, and its result is cached per interpreter until a package or the OS version changes, so it costs nothing after the first run. Dispatched agents also get a folder with python3 / python launchers for the service's interpreter at the front of PATH; engines whose shell re-runs login profiles (~/.zprofile, ~/.zshrc, conda init) can still push another python3 ahead of it, which the check above covers. The desktop runtime build and the 演示视频 doctor now import the scipy submodules actually used (the build also checks soundfile and soxr), so a package like this fails the check instead of passing a bare import scipy.
codevideo.py doctor now accepts --project, as the demo-video workflow docs write it (2026-10-22). The 演示视频 workflow (agents/workflows/codevideo.yaml, the film-director's role notes and the task templates copied from them) runs the toolchain check as python3 code/codevideo.py doctor --project <slug>, but doctor was the one subcommand without --project, so the documented command always stopped with a usage error (exit 2) and the host_toolchain_verified check (doctor exits 0) could only pass if the agent dropped the argument. doctor now takes --project like the other subcommands and ignores it (the check does not look at a project); --no-gl and --json are unchanged.
Transition design no longer attaches a scene's closing transition line to a cut inside that same scene (2026-10-22). When the shot list splits one screenplay scene into two scene_ids (same scene_no), the group boundary between them got the scene's closing transition line as diagnosis.screenplay_hint — in a one-scene episode that is the episode-close line (e.g. cut to black) — and propose then wrote it into the reason of the main design and every alternative ("剧本 转场:…"), so the transition card read as if the script asked for that cut; a time word in that line (e.g. 三日后) could also be taken as this boundary's time word. The line is now attached only where the scene number changes (last group of a scene → first group of the next), the same rule screenplay_link already used. Reasons already written into existing transition designs and shot lists are left as they are (designs you or the transition-design agent already decided keep their text on the next propose; only the diagnosis is refreshed).
Demo videos: Chinese in the storyboard contact sheet no longer shows as boxes (2026-10-22). The labels above each frame of board_sheet.jpg (written by codevideo.py board, and the sheet from still --sheet) were drawn with Pillow's built-in bitmap font, which has no Chinese glyphs, so a shot description written in Chinese came out as a row of boxes. The labels now use the same font lookup as the whitebox subtitles and the animatic (a font in data/fonts/, then a system Chinese font such as Hiragino Sans GB / PingFang / Noto Sans CJK / Microsoft YaHei, then Pillow's default), and a label too wide for its frame is cut to fit by measured width and ends with …, instead of at a fixed 70 (sheet) or 40 (shot description) characters. The sheet is not part of the render fingerprint, so no section is re-rendered.
The interpreter fallback no longer swaps a working interpreter for the service's own when that one is worse (2026-10-22). When an agent ran a host CLI on an interpreter other than the service's, a single failed test-import was enough to rerun the command on the service's interpreter, without checking that interpreter. On the reporting Mac the service ran on conda base (whose scipy.signal cannot load on macOS 27); an agent that switched to a healthy environment missing only Playwright to run 代码作曲 through genmedia.py music --provider code was sent back to conda base, where the composer script failed. The fallback now test-imports on the service's interpreter too (cached the same way) and reruns only when the service's interpreter fails strictly fewer of the same imports, so an older desktop runtime that only lacks soundfile is still preferred over an interpreter that lacks everything; otherwise the command stays where it is and prints what each side failed to load. genmedia.py no longer counts a missing Playwright, since it never opens a browser; the code/*.py scripts still do.
Scene plates: Chinese entries in a location's architecture negatives no longer go into the English negative prompt (2026-10-22). The reverse-view plate and the lighting-variant plates (code/render_scene_plates.py) appended bible/scenes/<scene>/architecture.json#negative to the image model's negative prompt as-is, so a location whose negatives were written in Chinese mixed them into an otherwise English negative. The entries are now split: English (plain ASCII) entries stay in the negative prompt, and any other entry is written word for word into the prompt body as a Must not include: … sentence (before the empty-plate line). Lists and comma / semicolon / 、-separated strings are both split per entry. The prompt template and the style fragment (style_fragment_en) are unchanged.
Upscaling with the ffmpeg method can now overwrite the source clip in place (2026-10-22). The upscale role notes archive the original and then upscale onto the same path, but genmedia.py upscale --input <clip> --output <same clip> handed one file to ffmpeg as both input and output: written identically, ffmpeg refused (same as Input #0 - exiting) so no final version could be produced; written differently (a relative input and an absolute output, say), ffmpeg emptied the source before reading it, leaving a one-frame clip while reporting success. ffmpeg now writes to a hidden temporary file next to the output and it replaces the output only after ffmpeg succeeds with a non-empty file; on failure the original is left untouched and the temporary file is removed. The step that rescales other methods' results to the delivery size uses the same temporary-file handling, so two clips of one group no longer share a fixed temporary name. --dry-run notes when the output is the input file. The other upscale methods download the full result before saving and were not affected.
The 剧本预览 page no longer fails when an episode's chapter_range is written as an array or a string (2026-10-22). With a story/story_graph.json that has acts, an episode_plan.json entry whose chapter_range was a chapter-ID array (["ch001"]) or a string ("ch001-ch003") made the derived view crash ('list' object has no attribute 'get'), and because the derived view is built first, the preview request failed even when a valid script_breakdown.json existed (the storyboard and shot-list previews' script links went through the same code). The episode's chapter range is now read from {start, end} (the documented form), {from, to}, an array (first and last ID) or a string, falling back to the episode's chapters[], so the current act is also shown for plans that only list chapters[]. If building the derived view still fails, the preview shows the formal breakdown (or an empty derived view) with the error instead of failing; the page shows the array form as ch001 – ch003 instead of an empty –. Voice-form resolution and the episode plan's roadmap scope also accept the array form; the episode-planner's notes now spell out chapter_range: {start, end} as the form to write.
A ticked project skill whose files are gone no longer leaves the agent told it is both enabled and not selected (2026-10-22). When a skill ticked in 项目技能 was no longer installed (for example high-density-fight, removed on 2026-09-24, or a skill uploaded to an older desktop build and lost on upgrade), the console silently left it out of the list, so it could not be unticked, while the project setting still had it on: the agent found no SKILL.md, and skill_report.py answered skill is not selected for this agent/run. Such skills now show greyed out in 项目技能, marked 未安装; unticking one and saving removes it from the project settings (ticking a skill that is not installed is still refused). Only when an agent has such a skill, its skill contract gains one line telling it to work without the skill, not to look for its SKILL.md or report it with skill_report.py, and to say so in its receipt; skill_report.py now answers that the skill is not installed. The leftover high-density-fight registration and the fight-choreography skill's reference to it are removed.
Narration entries whose header carries extra fields are no longer reported as missing (2026-10-22). The generation-group check (check_generation_groups.py, narration_anchors_cover_all), the white-model reel's narration subtitles and the storyboard/shot preview's narration list each read narration.md with a pattern that required anchor: right after the ID and est_duration_s: right after a one-part anchor. Headers such as [N-01 | scene: S01 | scene_id: SCN-0001 | anchor: … | est_duration_s: 4.4 | …], or the four-part anchor the storyboard notes prescribe (anchor: S01 | SCN-… | ev… | …), were not read at all, so the check reported "narration.md 解析不到任何条目" for a complete file and the reel and preview showed no narration. All three now use the same parser as the script breakdown: fields after the ID in any order, extra metadata ignored, multi-part anchors kept whole; a field value may contain square brackets. Entries without est_duration_s are still not counted, and the check now names them ("条目头缺 est_duration_s") instead of reporting the whole file as unreadable. Entries that were already read come out unchanged; white-model reels of episodes that gain narration subtitles will show as needing a re-export.