把正文组成收成数据契约,程序投影与旁观维护不再靠模型临场猜。

有变量真值时固定开旁观填表,开场与游玩只填模型生成区。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-10-07 23:47:58 +08:00
co-authored by Cursor
parent bb4f934b3d
commit d0a48f9431
21 changed files with 3094 additions and 224 deletions
+33
View File
@@ -203,6 +203,39 @@ describe("protagonist overlay", () => {
expect(protagonistTagForOpeningSlot("设计.开场白")).toBe(PROTAGONIST_TAG);
});
it("binds present.v1 meta.用户角色 to the slotted card and follows opening rewrites", async () => {
const {
ensureProtagonistFromPersona,
personaBoundToOpening,
protagonistTagForOpeningSlot,
} = await import("../src/persona/protagonist-bind.js");
const { splitOpeningDocument } = await import("../src/skills/opening-seal.js");
const openingTag = "设计.开场白#debt-hall";
const card = (简介: string) =>
JSON.stringify({
schema: "present.v1",
blocks: { body: "清晨,@玩家推开堂屋门。" },
meta: { 用户角色: { 名字: "沈聿", 简介 } },
});
const board = new Blackboard();
const personaTag = protagonistTagForOpeningSlot(openingTag);
for (const 简介 of ["本地放贷人。", "持有这家债契的放贷人。"]) {
const content = card(简介);
const split = splitOpeningDocument(content);
expect(split.persona?.name).toBe("沈聿");
expect(split.text).toBe(content);
ensureProtagonistFromPersona(board, split.persona, personaTag, {
overwrite: true,
});
board.write({ tag: openingTag, content, source: "agent" });
expect(personaBoundToOpening(board, openingTag, content)?.description).toContain(
简介,
);
}
});
it("overlays opening meta 用户角色 when the user switches to that preset", async () => {
const { readSessionProtagonist, overlayPlayPersona } = await import(
"../src/persona/protagonist-bind.js"