lili 87777975c7 feat(w-axis): R1 生成可信闸代码收口(actor/judge闭合+W-GOLD-LIVE+full_gate+三批闸门)
actor/judge prompt eval 闭合: actor 3.0.5 正文强化+baseline(7轮); judge 3.0.14 正文强化+anyTerms同义词补全+baseline(judge-a 13轮/judge-b 33轮); registry/checker 版本对齐+顺序锚点 fail-closed 双保险
W-GOLD-LIVE live_prompt: play-loop 契约开口(request/3+provenance/3+ReferenceAssetRecord/1+Registry/1+迁移清单15条); cheap_verify 切v3+消费对账六闸(只消费active)+生成prompt注入接线(0 active不注入); Node runner playtest-v3.cdp.cjs 升/3+绊线恢复; 真模型回归验向后兼容
full_gate+三批基线: full_gate.py 集成runner串六子门+降级+22测; baseline_gates.py fresh25阈值+historical11预期表(2 needs_human交创始人定标)+shadow20框架+38测
注: registry cheap-system 登记 1.8.0→1.8.1 同步此前在途 cheap-system.md frontmatter 升版(修 pre-commit 版本漂移); cheap_verify/cheap_studio/validate/test_acceptance_v3/test_cheap_service_driver 为在途M含本会话叠加+此前W-AXIS在途(同一线无法hunk分离); 3红测试根因与raw审计未入本提交
2026-07-25 18:47:47 -07:00
..

contracts/play-loop/ —— 判分闭环两契约PlaySpec 考卷 · VerdictFeedback 判卷反馈)

这一目录登记生成引擎判分闭环上的两份契约,它们是 2026-07-03 一次性裁决Δ1从散在代码里的隐形约定钉出来的显式契约。闭环是这样一条线一局游戏生成出来后harness 按一份考卷PlaySpec真玩它、九门判分没过门就把一份结构化判卷反馈VerdictFeedback喂回续修 agent 让它接着修;修完再玩再判,直到门绿才放行。两份契约分别钉住这条线的两端——考卷说清「怎么玩我、以便据此判我」,反馈说清「哪道门没过、卡在哪、往哪修」。

契约文件 设计代号 是什么 消费方 状态
play-spec.schema.json C5 PlaySpec 考卷:游戏声明起局仪式、驱动器族、输入指令、赢/输可观测量、与源工程的派生绑定 W-S1 修复三单(接 startRitual / derivedFrom 进 harness 与 ensure_play_spec· tier2 F-1 对齐 schema 已立、校验器与负样本齐;生产接线在途
verdict-feedback.schema.json C6 VerdictFeedback 判卷反馈:每次未过门回喂续修 agent 的结构化载荷(哪道门 / 卡在哪个 phase / 判卷驱动器 / 证据指针 / 疑似失败面 / 修复方向) W-S1 修复三单gate_judge 产结构化反馈、middleware 注入续修)· tier2 F-1 对齐 schema 已立、校验器与负样本齐;生产接线在途

为什么现在钉这两份

考卷C5至今是隐形的。驱动器怎么选,靠的是 cheap_run.py:241-242 那句「state 里有没有 targets 键」——有就用点目标的 tap-targets 族、没有就用循环按键的 key-cycle 族。这条约定只活在一个函数里,读代码才知道。更要命的是考卷和源工程之间没有任何绑定cheap_run.py:278 用「play-spec.json 已存在就不覆盖」来兜底可源工程一旦改过旧考卷还在就会拿上一版的考卷去判新工程——要么假绿、要么错判。C5 把驱动器族、起局仪式、赢/输可观测量都升为显式字段,并加一段 derivedFrom.sourceHash 把考卷绑到源工程内容上hash 不一致即算陈旧、必须重生。陈旧从此可判定,而不是靠「不覆盖」听天由命。

**反馈C6**至今是一整段拼出来的中文字符串(gate_judge.py:109_cheap_verdict_feedbackrun.py:1070verdict_feedback)。字符串拼装里丢字段是无声的,最典型的一处就是 gate_judge.py:100-102:它拼 H 门的 latch 细节时只读了 latch.after,没读 latch.phaseNow。可 latch 有两条失败支——驻留校验支带 afterplay.cdp.cjs:1089),而 advisory 失败支只带 phaseNow、不带 afterplay.cdp.cjs:1083)。于是当游戏卡在 playing 玩不到终局、走 advisory 支失败时,回喂里就成了 after=None,续修 agent 根本不知道游戏卡在哪个阶段只能盲修。C6 把「卡在哪个阶段」升为一等字段 phaseNow,并规定「只要报 latch 失败就必须带 phaseNow」——这类丢字段从结构上不再可能发生。

不编码 tier用能力字段与扩展段表达档位差异Δ9③

两份 schema 都要同时承载便宜档与 tier2 富游戏档,但任何字段里都不会出现 tier: cheap | tier2 这样的枚举。做法沿用 contracts/trace/ 已立的「接口对称、内容不对称」纪律:核心字段两档同名同义(考卷的 driver/observables/derivedFrom反馈的 gate/driverType/failedGates档位差异一律靠两种方式表达——

  • 能力字段的「在不在」:考卷里出现 economy 段=这局有经济胜负语义,出现 controlCheck=这局有可跟手的控制体,出现 firstPlay=要判首玩核心闭环时限。它们表达的是「这局具备什么能力」而不是「这局属于哪档」。便宜档轻游戏不写这些段tier2 富游戏档写,同一份 schema 都校验得过。
  • ext 扩展段:某一档独有、不进对称核心的东西塞 ext各写各的、没有的字段绝不编造。tier2 的经济三个数、findings 严重度就落在反馈的 ext 里;便宜档一般不写 ext

样本 play-spec/valid/03-tier2-paddle-economy.jsonverdict-feedback/valid/03-tier2-economy-ext.json 就是用来证明这一点的tier2 的富能力字段和 ext 富证据全部通过同一份 schema全程没有 tier 枚举。

和另外两处容易混的东西划清界限

  • C6 反馈 ≠ contracts/trace/ 的 trace 事件。 trace 记录是生成轨迹里一步的观测事件写进台账供成本对账和排障回看是旁路的、给人和监控看的。C6 反馈是判分没过时喂回 agent 让它接着修的载荷,是闭环里的一环、给模型读的。两者都带 traceId(反馈的 evidence.traceId 正是用来 join 到 trace 台账但用途和生命周期完全不同trace 每步都产,反馈只在未过门时产。
  • 这两份 ≠ contracts/agent-loop/ 里的 v1 verdict。 contracts/agent-loop/verdict.schema.json 是 Tier0/1 clicker 世代的终判记录designId / playReport / decision 三值),是终判本身且是上一代模板闭环的产物。C5/C6 属于当前引擎无关九门这一代,且 C6 是终判之后产的反馈、不是终判。别把两代、也别把「终判」和「终判后的反馈」混为一谈。

校验器与样本

校验器 validate.py 零依赖(只用 Python 标准库),自带一个 JSON-Schema Draft-2020-12 子集实现——本仓没装 jsonschema、也没 vendor ajv而红线要求零新依赖两份契约的消费方cheap-worker、tier2本就是 Python落一份 stdlib 校验器与消费侧同栈。正负样本套件即校验器自身的回归证明:某关键字实现错了,正样本会被误拦或负样本会漏过,跑套件即暴露。

# 套件模式:正样本应全过、负样本应全拦,打印每契约小计与总计
python3 validate.py --suite

# 单点模式:拿任意样本对某 schema 校验(可多文件)
python3 validate.py play-spec.schema.json samples/play-spec/valid/01-t2048-key-cycle.json

样本目录 samples/<契约>/{valid,invalid}/,目录名对应同名 schema。每个负样本文件头部的 _note 写明它复现的是哪条真实失败模式、对应生产代码哪一行、预期被哪条规则拦下——负样本都是「真实会发生的错法」,不是假想错法:

  • C5 负样本:①无 sourceHash 绑定(复现 cheap_run.py:278 缺绑定的陈旧兜底②无任何驱动方式driver/inputs 皆缺③驱动器族缺必填参数key-cycle 无 keys④进展断言用了 checkAssertion 不认的算子(会无声判否)。
  • C6 负样本:①报 latch 失败却丢 phaseNow复现 gate_judge.py:100-102)②逐门反馈缺 driverType ③声称未过门却给空 items复现 gate_judge.py:125-126 的盲修缺口④门全绿却仍产反馈passed=true 矛盾态)。

取样来源

正样本的 gameplay/verdict 字段取自现网真实产物,derivedFrom 等 C5 新增绑定段按契约目标形态补齐(现网 play-spec.json 尚无此段,接线在 W-S1 在途),每个样本 _note 如实记录:

  • play-spec/valid/01game-runtime/games/_wg1-gen/t2048/play-spec.jsonkey-cycle
  • play-spec/valid/02cheap-worker/fixtures/golden-specs/shop-serve.play-spec.jsontap-targets
  • play-spec/valid/03game-cloud/.../saa-golden/play-spec.jsonpaddle-intercept + controlCheck+ run.py:1017-1024 的 economy 形态
  • play-spec/valid/04game-runtime/games/_wg1-gen/_goldenpath-ref/play-spec.jsoninputs 固定序列)
  • verdict-feedback/valid/01game-runtime/games/_wg1-gen/t2048/evidence/verdict.jsonH_progress latch.phaseNow='playing' 真实失败)
  • verdict-feedback/valid/02game-runtime/games/_wg1-gen/_goldenpath-ref/evidence/verdict.jsonF_wiring callCount=0 真实失败)
  • verdict-feedback/valid/03run.py:1000-1070 verdict_feedback/_economy_evidence + gate_judge.py:42-53 前缀口径 + tier2-verdict.schema.json richGameGates/findings 形态tier2 富证据示例值)