{ "$schema": "https://json-schema.org/draft/2020-12/schema", "$id": "https://wanxiang.ai/contracts/play-loop/verdict-feedback.schema.json", "title": "VerdictFeedback", "description": "VerdictFeedback 判卷反馈契约(生成引擎 agentic 架构设计代号 C6,2026-07-03 一次性裁决 Δ1)。每次真玩判分没过门时,回喂给续修 agent 的那份结构化反馈:说清哪道门没过、卡在哪个阶段、这局是用哪种驱动器判的、证据在哪、疑似哪段代码坏了、往哪个方向修。它不是终判本身(终判是 tier2-verdict / 便宜档九门 verdict),也不是观测事件(那是 contracts/trace 的 trace 记录,是给台账看的旁路轨迹);它是判分→反馈→续修这条闭环里、专门喂回 agent 让它接着修的那份载荷。为什么要钉成结构:现在这份反馈是被拼成一整段中文字符串产出的(tier2 run.py:1070 verdict_feedback、便宜档 gate_judge.py:109 _cheap_verdict_feedback),而字符串里丢字段是无声的——最典型的是 gate_judge.py:100-102 拼 H 门 latch 细节时只读了 latch.after、没读 latch.phaseNow,于是当 latch 走 advisory 分支失败(play.cdp.cjs:1083,那支只带 phaseNow、不带 after)时,回喂里就说成 after=None,agent 根本不知道游戏卡在哪个 phase(本该是 playing),只能盲修。把反馈钉成结构、把『卡在哪个阶段』升为一等字段并在报 latch 失败时强制携带,这类丢字段就从结构上不可能发生。它要同时承载便宜档与 tier2 两档:核心字段两档同名同义(接口对称),tier2 独有的富游戏证据放 ext(内容不对称、Δ9③)。红线:字段不编码 tier 枚举,档位差异靠能力字段与 ext 表达,绝不出现 tier:cheap|tier2。消费方=W-S1 修复三单(gate_judge 产结构化反馈、middleware 注入续修)与 tier2 F-1 对齐。", "type": "object", "required": ["gameId", "passed", "failedGates", "items"], "properties": { "schemaVersion": { "description": "契约版本标签(可选,建议 'verdict-feedback/1')。", "type": "string", "minLength": 1 }, "gameId": { "description": "这份反馈对应哪局游戏(工程 id),供续修 agent 与台账定位。非空。", "type": "string", "minLength": 1 }, "passed": { "description": "恒为 false:VerdictFeedback 是『没过门时才产』的反馈载荷,门全绿就不该有它(那时 agent 该去 finish 交付、而不是收反馈)。锁死 false 是为了从契约层杜绝『门绿了却还拼一份反馈踹回去续修』这种矛盾态——这与归一判定 GateJudgment(passed, failed_gates, feedback) 的口径一致:passed=true 时 feedback 不作回喂用。", "const": false }, "failedGates": { "description": "未过门名清单(与 GateJudgment.failed_gates 同口径):便宜档=九门里 pass=false 的门名(A_boot..I_control);tier2 另带前缀——richGame:<门>(三联动/经济/latch 富游戏三门)与 finding:(P0/P1 对抗发现)。至少一项:没有未过门就不该产反馈。前缀差异是内容层扩展,不是 tier 枚举。", "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/gateId" } }, "items": { "description": "逐门结构化反馈(至少一项,与 failedGates 呼应):每个未过门一条,带它卡在哪、用什么驱动器判的、证据、疑似失败面、修复方向。这是本契约的主体——现行代码把这些拼成一段字符串(_cheap_verdict_feedback 逐门拼 hint+detail、verdict_feedback 逐门拼 detail+why),本数组把逐门信息结构化,字符串可由它派生(见 humanReadable),从而不再丢字段。", "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/feedbackItem" } }, "evidence": { "description": "信封级证据指针(可选):整份判分留下的证据根,供 agent 与排障回看。", "type": "object", "properties": { "verdictPath": { "description": "本次九门/三层 verdict.json 落盘路径。", "type": "string" }, "tracePath": { "description": "本次生成轨迹 trace.jsonl 路径(对接 contracts/trace)。", "type": "string" }, "traceId": { "description": "贯穿生成任务链路的 traceId(既是反查键也是成本关联键,与 contracts/trace 的 traceId 同一个)。", "type": "string" }, "screenshotDir": { "description": "本局截图目录(first-paint.png / after-play.png 等取证图所在)。", "type": "string" } }, "additionalProperties": true }, "humanReadable": { "description": "由 items 派生的人读中文回喂全文(可选,向后兼容现行字符串消费者):即 verdict_feedback / _cheap_verdict_feedback 现在产的那段话。留它是为了平滑过渡——续修 middleware 现在注入的是字符串,接线期可先并存;但它必须是 items 的派生投影、不得携带 items 里没有的信息(否则又回到字符串丢字段/夹带的老路)。", "type": "string" }, "ext": { "description": "信封级扩展段(Δ9③):tier2 富游戏档独有、不进对称核心的整份反馈级证据放这里(如整局经济三个数汇总、findings 严重度分布、三层校验摘要),便宜档一般不写。接口对称(上面核心字段两档同名同义)、内容不对称(ext 各写各的、没有的绝不编造)。", "type": "object", "additionalProperties": true } }, "patternProperties": { "^_": { "description": "下划线开头的作者元数据(如 _note / _source),供人读与取样标注,消费方不依赖。放行任意值。" } }, "additionalProperties": false, "$defs": { "gateId": { "description": "门标识:九门名(A_boot/B_uncaught/C_frame/D_render/E_live/F_wiring/G_input/H_progress/I_control),或 tier2 富游戏门 richGame:,或对抗发现 finding:。", "type": "string", "pattern": "^([A-I]_[a-z]+|richGame:[a-zA-Z]+|finding:P[0-2])$" }, "feedbackItem": { "type": "object", "required": ["gate", "driverType"], "properties": { "gate": { "description": "这条反馈对应哪道未过门(取值见 $defs/gateId)。", "$ref": "#/$defs/gateId" }, "driverType": { "description": "判这道门时用的驱动器族(PlaySpec.driver.type,如 key-cycle/tap-targets/paddle-intercept;用固定 inputs 序列判的填 input-sequence;A_boot 这类与驱动无关的门填 none)。为什么每条都必须带:续修 agent 要知道这局是怎么被玩的,才能判断『是玩法真坏了,还是判分的驱动方式没覆盖到』——现行代码知道 driver 却没把它随反馈带出去,本字段强制带上、避免归一重写时把它丢了。非空。", "type": "string", "minLength": 1 }, "phaseNow": { "description": "卡在哪个阶段(游戏此刻的 phase,如 'playing'):真玩没驱动到终局时游戏停留的 phase。当本条报 latch 失败(下面 latch.pass=false)时强制携带——这正是修 gate_judge.py:100-102 丢 phaseNow 缺陷的一等字段:latch advisory 失败支(play.cdp.cjs:1083)只带 phaseNow、不带 after,旧代码只读 after 就把『卡在 playing』丢成了 after=None。非 latch 失败可省略。", "type": "string" }, "latch": { "description": "终局 latch 判定细节(可选,仅 H_progress 类门带):真玩到没到终局态、到了有没有驻留。它出现且 pass=false 时,本条 item 必须带 phaseNow(见上)。", "type": "object", "required": ["pass"], "properties": { "pass": { "description": "是否玩到终局并驻留。", "type": "boolean" }, "advisory": { "description": "本次 latch 失败是否降为 advisory 不致命(有真进展的限时/无快速失败态类)。", "type": "boolean" }, "reason": { "description": "latch 判定原因(人读)。", "type": "string" }, "after": { "description": "驻留校验分支读到的终态 phase(play.cdp.cjs:1089 该支带 after 不带 phaseNow;与 phaseNow 互为两支,故两者都建模)。", "type": "string" } }, "additionalProperties": true }, "detail": { "description": "这道门的人读细节(可选):如 F_wiring 的『期望前缀=… 实际 callCount=0』、H 门逐条断言 before/after、E_live 去重态数等(对应 _cheap_gate_detail / verdict_feedback 逐门 detail)。", "type": "string" }, "fixDirection": { "description": "修复方向(可选,人读一句):这道门没过时该往哪修(对应 _CHEAP_GATE_HINTS 的门提示 / _RICH_CHECK_WHY)。", "type": "string" }, "evidence": { "description": "本条门的证据指针(可选):console 日志、游戏运行日志、截图、状态快照等的路径或摘要,让 agent 据实修而非猜。", "type": "object", "properties": { "console": { "description": "console 错误/日志(路径或摘要文本)。", "type": "string" }, "gameLog": { "description": "游戏运行日志(路径或摘要)。", "type": "string" }, "screenshots": { "description": "相关截图路径清单。", "type": "array", "items": { "type": "string" } }, "statePath": { "description": "玩前/玩后可观测状态快照(路径或内联)。", "type": "string" } }, "additionalProperties": true }, "suspectedSurface": { "description": "疑似失败面(可选):文件/函数级的怀疑点清单,把『大概哪儿坏了』结构化给 agent,缩小续修搜索面。", "type": "array", "items": { "$ref": "#/$defs/surfaceHint" } }, "ext": { "description": "本条门的扩展段(Δ9③):tier2 富游戏门独有的逐门证据放这里(如经济门的三个数 measured/coinsTarget/winThreshold、finding 的 severity/issue/suggestion),便宜档九门一般不写。", "type": "object", "additionalProperties": true } }, "additionalProperties": false, "allOf": [ { "if": { "properties": { "latch": { "properties": { "pass": { "const": false } }, "required": ["pass"] } }, "required": ["latch"] }, "then": { "required": ["phaseNow"] } } ] }, "surfaceHint": { "type": "object", "properties": { "file": { "description": "疑似出问题的文件(源工程相对路径,如 'src/systems/score.js')。", "type": "string" }, "symbol": { "description": "疑似出问题的函数/符号名。", "type": "string" }, "why": { "description": "为什么怀疑这里(人读一句)。", "type": "string" } }, "additionalProperties": false } } }