3.2 设定的一致性:冲突检测
设定冲突不是「感觉」,而是三种可判定的结构问题:立场失衡(带符号环的符号积为负)、地点解锁成环(互相依赖)、同一条规则被两个来源反证。本节用一个 S 层脚本各写一个检测器,在真规格上得到 0 死锁 / 0 失衡环 / 0 双源反证,再用三份注入式坏规格证明检测器真的会报错,而不是恒返回「无冲突」。
category
设定冲突不是「感觉」,而是三种可判定的结构问题:立场失衡(带符号环的符号积为负)、地点解锁成环(互相依赖)、同一条规则被两个来源反证。本节用一个 S 层脚本各写一个检测器,在真规格上得到 0 死锁 / 0 失衡环 / 0 双源反证,再用三份注入式坏规格证明检测器真的会报错,而不是恒返回「无冲突」。
世界观在规格里是三组可校验的字段:4×4 的派系 stance 矩阵表达谁与谁对立,地点 unlock_when 表达空间解锁,rules 表达可检查的约束。本节用一个 S 层脚本把三者都读成可判定的量,逐对检验 stance 矩阵的反对称性,校验 14 个地点的解锁条件与提供者,并把 4 条自然语言规则整理成待绑定的清单。
meta.spec_version 是迁移的判据。本节给出字段增删的兼容性规则,并用一个迁移脚本演示 v1 到 v2 的向前兼容:读 v1 规格、补默认值、产出 v2,再用 v1 投影摘要断言旧字段语义不变,且 Python 与 TS 两侧摘要一致。
模式校验挡「字段写错了」,语义校验挡「字段写对了却指向不存在的东西」。本节把这两类失效分开处理,给出六项校验的 Python 实现与真源 0 违规的实测结果,再对真源做三处定向破坏,确认对应校验会开火且无连带误报,TS 侧复用同一套判据求值。
把《雾港夜航》的 story_spec.json 逐段摊开:顶层十个键里九段承载数据、conditions 只声明文法。给出字段名、类型、必填与消费方,再用 S 层脚本审计每个声明项是否真被引用、P 层类型声明是否与 JSON 逐项对齐,两边都不留未对齐字段。
卷一交付给卷二《叙事引擎与分支系统》的四类接口:规格段、内核函数、仿真产物、判据与容差。本节用一份自检脚本逐项核对磁盘上的段、函数、产物与阈值,给出交付清单表与验收口径的迁移表,并声明边界——卷二接住执行,不重写条件文法、不重算穷举、不另立真源。
把卷一的三处消费——穷举、执行、度量——在同一份规格上串成一次预演:穷举给出覆盖率与结局可达性,执行把 seed 采样的第一条路径推进到底并算出整数 digest,度量给出张力 MAE 与结局分布熵。S 层与 P 层对同一路径复算,digest 逐位相同。
装配检查表把前面十五节的判定收成十项可断言的检查:九段字段齐备、引用完整、写入域合法、整数不变量残差 0、规模对账、结局节点一致、死条件 0、覆盖率达目标、结局全部可达、卡死路径 0。S 层脚本逐项跑出 PASS/FAIL 表,十项全绿才算装配完成。
叙事策划与程序在规格上分工:策划写结构与条件,程序写内核与门禁,规格是两者唯一的接口。本节给出规格变更的机器可读 diff 与影响面评审(本机可复现),并把真实团队的评审会、冻结窗口与排期逐处标注为需真实业务环境、命令行不可复现,把能算与不能算的分开。
从改规格到产物核对是一条五环流水线:结构校验与语义校验、规格解析、跨语言摘要、内核门禁、产物核对。本节给出逐条本机真跑过的命令,用一个 S 层驱动把五环串成一次执行,再用一个 P 层门禁断言这份报告不是过期快照,并给出各环的时间账与取舍口径。
《雾港夜航》的工具链分成五层:config/ 是唯一真源,sim/ 用 Python 穷举与校验,core/ 是 TypeScript 确定性内核并被 sim 的产物交叉验证,web/ 把内核内联进单文件 HTML,out/ 收三层产物。本节给出实测目录清单、每层的读写边界,以及「改一个文件会影响谁」的影响面反查。
把一致性写成一个整数:残差 = 规则失配 R + 立场失配 S + 秘密失配 K + 对齐缺口 A,本案例基线为 7。本节是 B 层:对门有效阈值、好感度初值、节拍密度做扰动,观察残差与覆盖率的相变——残差在阈值越过 60 时阶跃(7→9),覆盖率随初值连续下滑(−10 时 895‰),输出 3 条软告警但 exit 0。
设定说「教会不承认灯塔之光」,规格里却可能允许玩家请执事去点灯。本节逐条检验 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 处。只做文本的结构,不评价文本质量。