fix(engine): pin entry-beat roster to the plan in Phase B
The Painter composites exactly plan.entryActiveCharacters into the entry frame (the same roster the Cinematographer framed). Phase B is told to reuse that roster, but only the entry beat's id was code-enforced — so an LLM slip could leave a character in the painted frame that the runtime entry beat says isn't there. Pin activeCharacters onto the plan's entry beat as a last line of defense, mirroring the existing id pin. Speaker is intentionally left to the prompt: it's coupled to line/TTS, so overwriting it could mis-attribute or orphan Phase B's dialogue. Addresses Copilot review feedback on PR #27. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -497,6 +497,15 @@ export async function runWriterBeats(
|
||||
beats = renameBeatId(beats, beats[0]!.id, plan.entryBeatId);
|
||||
}
|
||||
|
||||
// 把入场 beat 的 roster 钉成 plan 的:画师合成进帧的正是
|
||||
// plan.entryActiveCharacters,运行时入场 beat 必须显示同一批人(与上面钉
|
||||
// id 同理)。speaker 故意不钉——它和 line/TTS 耦合,强行覆盖会错配台词。
|
||||
const entryRoster =
|
||||
plan.entryActiveCharacters.length > 0 ? plan.entryActiveCharacters : undefined;
|
||||
beats = beats.map((b) =>
|
||||
b.id === plan.entryBeatId ? { ...b, activeCharacters: entryRoster } : b,
|
||||
);
|
||||
|
||||
return {
|
||||
beats,
|
||||
storyStatePatch: coerceStoryStatePatch(parsed.storyStatePatch),
|
||||
|
||||
Reference in New Issue
Block a user