2.1 story_spec.json 的字段全表
把《雾港夜航》的 story_spec.json 逐段摊开:顶层十个键里九段承载数据、conditions 只声明文法。给出字段名、类型、必填与消费方,再用 S 层脚本审计每个声明项是否真被引用、P 层类型声明是否与 JSON 逐项对齐,两边都不留未对齐字段。
tag
把《雾港夜航》的 story_spec.json 逐段摊开:顶层十个键里九段承载数据、conditions 只声明文法。给出字段名、类型、必填与消费方,再用 S 层脚本审计每个声明项是否真被引用、P 层类型声明是否与 JSON 逐项对齐,两边都不留未对齐字段。
卷一交付给卷二《叙事引擎与分支系统》的四类接口:规格段、内核函数、仿真产物、判据与容差。本节用一份自检脚本逐项核对磁盘上的段、函数、产物与阈值,给出交付清单表与验收口径的迁移表,并声明边界——卷二接住执行,不重写条件文法、不重算穷举、不另立真源。
装配检查表把前面十五节的判定收成十项可断言的检查:九段字段齐备、引用完整、写入域合法、整数不变量残差 0、规模对账、结局节点一致、死条件 0、覆盖率达目标、结局全部可达、卡死路径 0。S 层脚本逐项跑出 PASS/FAIL 表,十项全绿才算装配完成。
从改规格到产物核对是一条五环流水线:结构校验与语义校验、规格解析、跨语言摘要、内核门禁、产物核对。本节给出逐条本机真跑过的命令,用一个 S 层驱动把五环串成一次执行,再用一个 P 层门禁断言这份报告不是过期快照,并给出各环的时间账与取舍口径。
《雾港夜航》的工具链分成五层:config/ 是唯一真源,sim/ 用 Python 穷举与校验,core/ 是 TypeScript 确定性内核并被 sim 的产物交叉验证,web/ 把内核内联进单文件 HTML,out/ 收三层产物。本节给出实测目录清单、每层的读写边界,以及「改一个文件会影响谁」的影响面反查。
设定说「教会不承认灯塔之光」,规格里却可能允许玩家请执事去点灯。本节逐条检验 4 条 rule 与 6 条 lore 是否被行为兑现:lore 的 reveal_flag 是否真被选项置位并被条件读取,rule 是否有兑现它的门。S 层脚本输出对齐表,如实报告对不上的项——本案例 6 条 lore 全对齐,rule_curfew 未兑现。
「叙事失调」通常是主观说法:故事说一套、玩法做一套。本节把它形式化为可判定的失配——设定声明的规则、立场与秘密,与规格实际允许玩家做的事之间的差集,给出 R / S / K 三类判定口径,并在《雾港夜航》上实跑得到残差 6。这是代理指标,只做相对比较,不声称「更协调」。
一致性 = 声明域与使用域的差集。S 层脚本输出一张一致性报告表:14 项逐项给出声明数、使用数、两个方向的差集与判定。实测「使用未声明」为 0;真实发现是 lin/hai 声明了 affinity 却不在 state.vars、d_lt_2 有 line_when 无 line_else,以及 7 个 flag 与 fog_glass 被写却从未被读。
死条件 = 条件引用的状态量在到达该节点前不可能取到所需值。判定用对话图上的可达区间前向不动点:区间过近似、不按条件剪枝,故「过近似都说够不着」才是真死条件。S 层复跑显示当前 0 个死条件;正文如实交代曾经有过的 4 个、各自的成因与修法。
结构校验只问图的形状:孤儿(无人指向且非起点)、断链(goto 指向不存在的节点)、死路(无选项且无结局标记)。S 层脚本对对话图与场景图逐类检测,实测三类缺陷均为 0;P 层用人为注入的三个负例断言检测器真的会开火,而不是只会返回空列表,起点则是唯一的豁免。
把全部 48 条台词与 115 个选项渲染成一张 163 行的文本表,每行一个键,内联进单文件 HTML,再用 Playwright 无头打开,断言台词条数、选项条数、空文本行数与键指纹,截图到 out/shots/,并验两遍渲染确定性。末尾重申本章的做 / 不做边界:本套只做文本的结构,不评价文本质量。
同一条台词记录在不同状态下渲染出不同变体,这是条件文本的全部含义。本节枚举两个条件节点的整个定义域:d_tc_2 两分支两条文本;d_lt_2 声明了 line_when 却没写 line_else,101 个状态枚举下来只得到一条文本——结构分流了,文本没分流。变体退化因此成了一个可数的缺口,修法是在规格里补声明,而不是改台词。
台词里出现的角色名与地点名,在规格里其实是一次变量查找。本节给出文本模板的唯一文法(占位、转义与未知占位回落),由规格三段拼出 24 个绑定的命名实体表,并渲染全部 48 条 line 与 115 条选项文本统计占位出现次数:行内占位 0 处、字段级占位 48 处、可模板化点 33 处。只做文本的结构,不评价文本质量。
把 48 个对话节点当成一张有向图,环、不可达节点、死路三类缺陷就都成了可判定的性质。本节用 DFS 三色标记找环、BFS 从起点算可达性与层号,实测三类缺陷均为 0:115 条选项去重后只有 56 条唯一跳转,5 个无出口节点全是结局,与 na_00 的 BFS 状态空间穷举口径互证。
同一节点在不同状态下渲染出不同台词,靠的是 line_when 与 line_else 两个字段。本节把渲染规则写成四分支表,用 S 层脚本枚举全部 50 个渲染分支:48 个节点里只有 2 个带 line_when,其中 d_lt_2 声明了条件却没给 line_else,属规格层面的欠声明,回落到 line。
对话节点是字段化的记录:节点层八个字段、选项层五个字段,其中 goto 是选项的独立字段而非效果算子。本节给出两张字段表,用 S 层脚本校验 115 条跳转全部命中已声明节点并输出节点度数表,再用 P 层测试断言未知选项被拒绝、推进是纯函数。
阈值门就是条件里的 ['aff', 名, '>=', 整数]:它可能长在选项的 when、节点的 line_when 或结局的 when 上。S 层把全规格 15 条门限抽成一张表,在 20000 条采样路径上给出每条门限的跨越率、最高到达与缺口,基线 0 条死门;P 层断言门限未达时选项不可选、恰好达到时可选的边界行为。
本套的核心主张:台词文本不是规格的输入端,而是规格的一种渲染产物。同一条 dialogue 记录(line 加 line_when 加 line_else)在不同状态下渲染出不同台词。本节给出四分支渲染规则、用 d_tc_2 做最小演示、反向断言文本不进状态,并写死边界——只做文本的结构,不评价文本质量。
一份散文大纲要变成可执行的规格,中间隔着一层「骨架」:meta 头部加九段内容字段。本节给出散文语句到字段的逐句映射表,列出 world、characters、beats、scene_graph、dialogue、conditions、state、endings、metrics 九段各自承载什么,并用一份 S 层脚本按实际键序逐段打印规模摘要。
叙事可以被拆成四个最小构成:世界(派系、地点、设定、规则)、角色(目标、恐惧、秘密、弧光)、事件(幕、节拍、场景、对话节点)、选择(选项与跳转)。本节用《雾港夜航》的 story_spec.json 把四者逐项清点:4 个派系与 14 个地点、6 个角色、16 个节拍与 48 个对话节点、115 个选项,并跑通一份 S 层清点脚本。