- 注册表:docs/architecture/README.md §2,37 份 canonical(frontmatter topic+canonical:true)与表双向机器对账
- 门:.agents/tools/docs-gate.py 六检(品牌根/canonical唯一/死链/入口卫生/留痕隔离/设计档申报),挂 .githooks/pre-commit(本仓已激活)+ .gitea/workflows/docs-gate.yml(待runner)+ wave-close 第8步;旧 check-deadlinks.sh 退役并入 G3
- 清缩:删 54 项历史档(plans 16/agent-specs 设计与spike 20/brainstorms 5/memorys 3/goals+作战清单完成史归档/王蓝莓design/SAA现状html/add-game-template);channel-spike 564K 代码资产迁仓级 spikes/;六件删前蒸馏已迁(代码评审16条open项→进度总账§5、prefix-cache字段表→cheap-model skill、意图基线29条→需求清单附录、九门降级rationale→验收门、A11 TODO→tech-decisions、3layer边界→littlejs-game-dev 指针)
- 修口径约90处:六处SAA『现行主线』旧标、Nacos/RocketMQ『未部署』旧述(07-01反转)、gameDefinition残留、全部死链改 git show 定位;AGENTS.md 249→136行(决策史归tech-decisions);_index 改纯在飞板;对外演示版 md→html
- 依据:docs/agent-specs/2026-07-02-文档治理-{全量普查与裁决-report,SoT注册表与治理门-设计}.md(四路普查191份md+Codex/Opus双评审必修项已折入);恢复基线 8ea97234(单档 git checkout 8ea97234 -- <路径>)
11 KiB
shared 只读区 · 集成点文档与授权声明(W-CH-α 渠道引擎对比 spike · P0-4)
产物根:
docs/agent-specs/2026-06-12-channel-spike/shared/对应执行 spec:docs/agent-specs/2026-06-12-W-CH-α渠道引擎对比spike-execution.md§4 P0-4、§3 公平性与取证纪律。 配对判定:docs/agent-specs/2026-06-12-渠道适配判定-小游戏双渠道.md(HJ-CH-001)§2 Channel Runner v1 形态、广告与取证。
0. 只读区授权声明(生命线)
本目录三件核心产物为「只读共用基础设施」,任何 lane(lane-littlejs-adapter / lane-cocos)禁止修改:
| 文件 | 角色 | 禁改理由 |
|---|---|---|
channel-probe.js |
八锚点共用探针 | 双候选必须用同一份 probe 取证,改动即失去可比性(执行 spec §3-1) |
runner-shell.js |
Channel Runner 壳骨架 | 壳形状/锚点打点位置/白名单语义是双候选公平对比的公共契约;lane 只能注入 adapter 实现,不改骨架 |
test/probe.test.cjs test/shell.test.cjs |
字段级单测 | 锁住上面两者的行为契约 |
收编校验: 主会话收编时对 channel-probe.js、runner-shell.js 做 diff 校验;发现任一 lane 改动了只读区文件,判该 lane 取证作废(执行 spec §3-1「lane 禁止修改 probe;主会话收编时 diff 校验」)。
lane 正确用法(注入而非改源):
- adapter 把"真平台实现"通过构造参数注入壳:
new ChannelRunnerShell({ platform, fetcher, schemaValidate, probe }); - 真平台广告(
wx.createRewardedVideoAd/tt.*)、真网络 config 拉取(wx.request)、channel schema 扫描器,全部以注入形式接入,源文件保持字节级不变。
残余自报风险四重压制(执行 spec §3-2): 同一 probe + 字段级锚点定义 + FNV-1a 哈希链 + 录屏旁证。其中录屏与截图一律降为辅证,主证据 = 机器可校验文件(本探针产出的 JSONL + sha256 manifest)。
1. 产物清单与运行
| 文件 | 说明 |
|---|---|
channel-probe.js |
ChannelProbe(mark/wrapRaf/wrapInput/setConfigHash)+ verifyChain + fnv1a;CommonJS,小游戏/Node 通用,零依赖 |
runner-shell.js |
ChannelRunnerShell(boot/wireSdk/registerTemplate/loadConfig/instantiateTemplate)+ TemplateRegistry/AdStub/LifecycleStub/TelemetryStub;零依赖,复用 probe 的 fnv1a 统一 hash 口径 |
test/probe.test.cjs |
探针字段级单测(7 组),node test/probe.test.cjs |
test/shell.test.cjs |
壳骨架单测(7 组,含 async loadConfig),node test/shell.test.cjs;失败置 process.exitCode=1 |
test/RESULT.txt |
两个单测的执行记录(本次 OVERALL: PASS) |
同目录另有
game-config/、schema/两子目录,系 P0-3(GameConfig 渠道合规 schema)产物,由另一工位维护,不属本 REPORT 范围。
验证状态(已实跑,2026-06-12,node v24.16.0):
probe.test.cjs-> PASS(7/7)shell.test.cjs-> PASS(7/7)- 失败检测已验证:向两份单测各注入一处必败断言,均以非零退出码 + 明确报错失败(证明 PASS 非"静默掩盖")。
2. 八锚点字段级集成点(谁在何处调)
锚点枚举与顺序照搬执行 spec §3-2「锚点定义到字段级(渠道版八锚点)」。每个锚点的集成点说明也内联在 channel-probe.js 的 ANCHOR_DEFS 注释里(探针自带文档),此处汇总并补"取证字段"。
| 锚点 | 字段级定义(spec §3-2) | 集成点:谁在何处 mark | 关键取证字段 |
|---|---|---|---|
t_launch |
probe 首行求值 | 探针构造期自打(autoLaunch 默认 true);约定 = app.js 首行 require('./channel-probe.js') 并 new ChannelProbe(...) |
extra.auto=true、t_mono 起点 |
t_runner_boot |
壳初始化完成 | runner-shell.js boot() 末尾 -> probe.mark('t_runner_boot') |
seq、prevHash 衔接 t_launch |
t_canvas_ready |
createCanvas 返回 + 首次画布操作 | adapter 的 canvas 工厂(wx.createCanvas 包装)返回并完成首次 getContext/clear 后调 probe.mark('t_canvas_ready') |
由 adapter 接线(lane 实现),双端差异矩阵记 canvas 路径 |
t_sdk_ready |
SDK 直连绑定完成 | runner-shell.js wireSdk() -> probe.mark('t_sdk_ready');真测里此处把 ad/lifecycle/telemetry 接真 wx/tt API |
顺序应在 t_runner_boot 之后 |
t_config_loaded |
GameConfig 拉取 + schema 校验过(记 configHash + 网络状态) | runner-shell.js loadConfig(url, expectedHash, {net, fromCache}) hash+schema 双校验通过后调 probe.mark('t_config_loaded', {...}),并 probe.setConfigHash(actualHash) |
configHash、extra.net、extra.fromCache |
t_first_paint |
场景挂载后首帧(probe 包装 RAF 计数) | 候选渲染循环首次 RAF 回调经 probe.wrapRaf(cb) 包装,探针在 frame===1 自动 mark |
extra.frame=1 |
t_input_bound |
候选经 probe 包装面注册输入处理器;首次真触摸到达由 probe 独立记录 | 候选用 probe.wrapInput(handler) 注册触摸处理器:注册即 mark(extra.stage='registered');首次真触摸到达时包装回调内独立再 mark(extra.firstTouch=true) |
extra.stage、extra.firstTouch(G2b 真机真输入关键证据,候选代码无法伪造到达时刻) |
t_game_start |
玩法循环启动(canvas 哈希序列旁证) | 模板核心层玩法主循环真正开跑时调 probe.mark('t_game_start', { canvasHash }) |
extra.canvasHash(旁证) |
注: t_canvas_ready、t_first_paint、t_input_bound(到达)、t_game_start 的 mark 调用点落在 lane 的 adapter / 引擎渲染循环 / 模板核心层——探针只提供 mark/wrapRaf/wrapInput 接口与自动打点逻辑,lane 通过"调接口"接线,不改探针源。
3. JSONL 记录字段(每条 mark 落盘一行)
照搬执行 spec §3-1「日志 JSONL 含 seq/单调时钟/前条哈希(链式防篡改)/device/appVersion/devtoolsVersion/configHash」并补全:
| 字段 | 含义 | 取证用途 |
|---|---|---|
seq |
单调自增序号(0 起) | 检测记录缺失/重排(verifyChain 校验连续) |
anchor |
锚点枚举值 | 定位事件 |
t_mono |
单调时钟读数(ms,3位小数) | 仅算间隔,禁跨进程/设备比较绝对值 |
t_wall |
墙钟 ISO8601 | 仅供人读对齐,不参与判定 |
prevHash |
前一条记录的 FNV-1a 滚动哈希(首条=SEED_HASH) | 链式防篡改 |
hash |
本条滚动哈希 = fnv1a(prevHash + canonicalCore(本条)) |
改/删/重排任一条立即断链 |
device |
设备标识(机型/平台) | 真机门必填(RUNBOOK 注入) |
appVersion |
提审包版本(packageVersion) | 区分三开协议各次构建 |
devtoolsVersion |
微信/抖音开发者工具版本 | G2 模拟器门取证 |
configHash |
当前 GameConfig 的 FNV-1a hash | t_config_loaded 后有意义;与生成侧同口径 |
extra |
锚点附带上下文 | 网络/缓存/frame/canvasHash/firstTouch 等 |
warning |
乱序/重复/未知锚点告警 | 不抛异常,只记录(供收编方判采信) |
哈希链性质说明(重要): hash 用 FNV-1a 32 位非加密哈希。选它是为「任何事后悄改/删行/重排在 verifyChain 下立即暴露」的防篡改证据链目的,不是密码学安全——无法抵御有意构造碰撞的攻击者。这一点配合 §0 的四重压制使用,且 channel-probe.js 文件头与 fnv1a 注释均已注明。canonicalCore 只纳入"一经写入即不可变"的核心字段(seq/anchor/t_mono/configHash/device/appVersion/devtoolsVersion/extra),不含 hash/prevHash 自身,并用固定字段顺序 + 稳定 JSON(键排序)拼接,保证跨次/跨端规范化一致。
4. verifyChain(records) 校验语义(导出)
| 检查 | 失败 reason | 说明 |
|---|---|---|
| records 非数组 | records_not_array |
入参防御 |
| seq 连续(第 i 条 seq===i) | seq_discontinuous |
检出删行/重排 |
| prevHash 衔接(首条=SEED_HASH) | prev_hash_mismatch |
检出断链 |
| 逐条重算 hash 一致 | hash_mismatch |
检出字段被改/伪造 hash |
| 已知锚点偏序不回退 | anchor_out_of_order |
检出乱序(同锚点重复出现用 < 判,允许 t_input_bound 二次) |
返回 {ok:true} 或 {ok:false, reason, at}(at=出错记录下标)。
5. runner-shell.js 接口形状对齐 contracts/sdk-interface.d.ts
壳的 SDK 直连桩按契约 #3(contracts/sdk-interface.d.ts)语义命名,并叠加 HJ-CH-001 §2 渠道线 additive 字段:
| 壳接口 | 对齐契约 | 渠道补充 |
|---|---|---|
ad.showRewarded(slotId, cb) |
AdPlugin.showRewarded |
桩默认 {rewarded:false, reward_fallback:false}——不伪造收益(HJ-CH-001 §2:rewarded=true 只认平台完整观看回调;兜底奖励标 reward_fallback 且不进收益归因);真测五路径由 lane adapter 实现(G4) |
ad.showBanner(slotId, cb) / ad.hideBanner(cb) |
(契约未列) | HJ-CH-001 §2「契约 additive 增 showBanner/hideBanner」 |
lifecycle.emit(event, payload) / on / off |
WanxiangGameSDK.on/off + LifecycleEvent |
postMessage 传输层 -> 同上下文直连派发(HJ-CH-001 §2-4);合法事件集 = sdk_ready/game_loaded/game_start/game_end/game_error;未知事件记 warning 不抛 |
telemetry.report(event, props) / flush |
WanxiangGameSDK.track |
"本地队列 + 批量 flush"语义(契约 10条/5s,桩演示条数阈值);真测接 wx/tt 上报或后端日志 |
templates.register/instantiate |
— | HJ-CH-001 §2-2「templateId 只命中包内白名单」;未知 templateId 抛 template_not_whitelisted(渠道生死线) |
loadConfig(url, expectedHash, meta) |
— | HJ-CH-001 §2-3「按 configHash 远程拉取,schema+hash 校验后实例化」;hash 不符/schema 不过->拒绝,绝不实例化未校验 config |
骨架边界: 真平台调用(wx.createCanvas/createRewardedVideoAd/tt.*/真 wx.request/channel schema 扫描器)不在骨架内,由 lane 在 P1 真测段以注入形式接线。AdStub 注入 platform 后即委托真实实现(单测 T6 已验证委托路径)。
6. 假绿向量声明(执行 spec §4 P0-1 红线对齐)
本只读区单测全部跑在 Node(mock clock / 内存 sink / 注入 fetcher),其性质 = 授予"装载资格/骨架行为正确性",绝不等价任何真机门:
- probe/shell 单测绿 != G1 装载门 / G2 模拟器门 / G2b 真机真输入门 / G3 真机首屏门;
- 真机门必须在 Win/Mac + 微信/抖音开发者工具 + 真机上,按 RUNBOOK 真触摸取证(禁状态注入,执行 spec §3-6);
t_canvas_ready/t_first_paint/t_input_bound(到达)/t_game_start的真实打点依赖 lane adapter 在真环境接线,本骨架只验证"接口存在且语义正确"。
7. 后续(交接给 lane 与 P1)
- lane-littlejs-adapter / lane-cocos:
require本只读区的channel-probe.js与runner-shell.js,按 §2 表在 adapter/渲染循环/模板核心层接线锚点;真平台实现以注入方式接入,不改只读区源。 - P1 真测:注入真
fetcher(wx/tt.request)、真platform(广告)、真schemaValidate(P0-3 的 channel schema 扫描器);RUNBOOK 注入device/appVersion/devtoolsVersion。 - 收编:主会话对
channel-probe.js/runner-shell.js做 diff 校验;取证 JSONL 用verifyChain校验链完整,再纳入计分。