docs(exec): 黄金闭环执行 spec(HJ-EXEC-006 v2) 定稿 — 双模型两轮评审 Go

- B0 产出:化解 autoplan 5 个 critical,主 agent Opus 子代理独立核验真实代码(file:line)
- Codex 首轮发现 11 项(4C+5H+2M)全采纳,推翻 v1 的 C5 幂等简化(恢复 eventId+uk_event_id)
- Codex 第二轮确认轮:Go,11 项 spec 层全闭合、无新设计矛盾,留 4 个 B1 实现守门
- D-PUB 已拍板 α(审核通过即自动发布);完成线=数据回路 A/B 可证

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
zizi 2026-06-09 13:35:26 +00:00
parent ddf88de420
commit 867c4523ae

View File

@ -0,0 +1,185 @@
<!-- B0 产出 v2:黄金闭环执行 spec。v1→v2 经 Codex Eng 独立复审硬化(4C+5H+2M 全采纳,其中 C5 幂等推翻 v1 简化)。上游 design v2(HJ-DESIGN-006)+路线 plan(HJ-PLAN-005)。5 个 critical 均经主 agent 独立核验真实代码(Opus 子代理 file:line)+Codex 二审。 -->
# 王蓝莓小卖部黄金闭环 — 执行 spec(HJ-EXEC-006 · v2 经 Codex 复审硬化)
> 文档号:HJ-EXEC-006 | 版本:v2 | 日期:2026-06-09 | 分支:dev/2.0.0 | 基线:dev/1.0.0
> 上游:设计 [`王蓝莓小卖部黄金闭环-design.md`](../superpowers/specs/2026-06-09-王蓝莓小卖部黄金闭环-design.md)(v2)、路线 [`下一阶段路线-plan.md`](./2026-06-09-下一阶段路线-plan.md)(B 轨)。
> 执行视图:[`MVP作战清单.md`](../mvp/MVP作战清单.md) 的 **B0→B1→B2→B3**。状态以 [`MVP进度总账.md`](../mvp/MVP进度总账.md) 为准。
> **双模型评审**:主 agent Opus 子代理独立核验真实代码(§3 file:line)+ Codex Eng 二审(4C+5H+2M 全采纳,对账见 §9)。**v1→v2 关键反转:C5 恢复 eventId(v1 简化被证不成立)。**
---
## 0. 目标与范围边界
**目标**:1 条垂直闭环做真,**完成线 = 数据回路 A/B 可证**(B 上报有效事件后排到 A 前 + 关 mock 仍通过 + 重放不重复计数)。
**范围内**:通用 clicker-plus 数据驱动 runtime + 确定性 PackageFactory;发布编排 `审核通过→PUBLISHED→写 feed`;最小数据回路(同步写 telemetry→算 quality_score→回灌 feed rank);小卖部皮肤 + ≥10 参数化种子。
**非目标(YAGNI)**:真实 Dify/LLM(用模板/兜底)、真实 OSS(package 存 DB,退场契约 §3.4)、MQ producer/consumer/DLQ(同步写替代 §3.5)、创作者自助/定时发布(auto-publish §1)、真实鉴权/多租户(mock token + tenant_id=1)、3D/Cocos、为单品类扩 runtime(经营=配置皮肤)。
---
## 1. 决策点 D-PUB(✅ 已拍板 α,2026-06-09):审核通过即自动发布
> Codex 二审指出 α 会改 APPROVED 常驻语义(admin UI 把 APPROVED 当可下架态,`game-admin/src/views/wanxiang/review/index.vue:88`)。**用户已拍板 α(2026-06-09)**;连带改动(本节下方)纳入 B1 实现。
**现状**:`submitPublish`(创作者)已声明发布意图→项目进 `REVIEWING(1)`;`reviewProject(APPROVE)` 止于 `APPROVED(2)`(`ProjectServiceImpl.java:291`);**APPROVED→PUBLISHED 无代码**。
**推荐 α(审核通过即发布)**:人工 `reviewProject(APPROVE)` 在同一本地事务内:记 APPROVE 审核记录 → 项目状态**直接 `REVIEWING(1)→PUBLISHED(4)`**(跳过常驻 APPROVED)→ 编排发布(§3.2)。
- **α 连带改动(Codex M10)**:① `ProjectStatusEnum` 注释标注 auto-publish 下 APPROVED(2) 仅作审核记录值、项目态不停留;② `contracts/api-schemas/project.yaml` 审核响应说明;③ admin `review/index.vue` 的状态筛选/「可下架」操作改挂 `PUBLISHED(4)`(UNLIST 作用于已发布);④ 单测更新。
- **compliance gate 与 α 的关系(Codex M10 澄清)**:`submitPublish` 的锁风门 verdict——block→不写库(admitted=false);review/pass→进 `REVIEWING(1)`。**compliance「pass」不自动发布,只是放进人工审核队列;只有人工 APPROVE 才触发 α 自动发布。**
**若选 β**(审核通过→APPROVED 常驻,再单独「发布」动作):§3.2 触发点从 `reviewProject(APPROVE)` 改为独立 `publishProject` 端点,§1 连带改动里 ③ 反向(APPROVED 保留可下架),其余 §3 不变。**localized。**
---
## 2. 前置条件(开工即查,不补则静默假成功 —— Eng 双审实证)
**后端/环境**:
1. 契约裁决:以 `contracts/*.yaml` / `-api` Java 接口为准;不破坏契约、不改已合入 Flyway V1-V9,**允许新增 V10 + 最小后端实现**。
2. staging:`BASE=http://100.64.0.7:48080`,单体在 mini-desktop 隔离容器。**重型构建/启动一律 mini-desktop**,勿本机。
**前端联调(Codex C3/C4/H8 —— 不补则试玩取包失败退 demo、like/share 漏采)**:
3. 环境变量真名 **`VITE_API_BASE`**(`game-studio/src/api/request.ts:35`);mock 中间件加门控(`VITE_API_BASE` 非空即关),启动打印 baseURL+mock 状态。
4. **host 取包统一走 `src/api/runtime.ts`(经 request.ts)**,删除/改造裸 axios 同源取包(`src/host/runtimeApi.ts:37`);**Play/Preview 显式注入 `fetchPackage`**(`Play.vue:196` 现未注入);request 拦截器补 `tenant-id:1` + token=`test1`。
5. **`fetchAndVerifyManifest` 的相对 `manifestUrl` resolve 到 API base**(`inject.ts:174` 现裸 fetch 同源)。
6. **`Play.vue` 的 `onTelemetry` 白名单原样转发契约事件名**(`Play.vue:112` 现把所有事件改写成 `game_impression`,吞掉 like/share);**Feed 互动补有效 traceId**(`Feed.vue:151,168`)。
7. **`sendBeacon` 写死同源 `/app-api`**(`telemetry/index.ts:24-25,142,204`)→ 改走 `VITE_API_BASE` 或常规 axios 通道;所有 staging 种子行统一 `tenant_id=1`。
8. **前端补 `eventId`(UUID)+ 真 `traceId`**(`buildEnvelope` 现无 eventId、`traceId` 默认 `''`,`telemetry/index.ts:101`)。
---
## 3. 5 个 critical 的确定性解法(现状 file:line → 解法 → 契约/文件 → AC)
### 3.1 C1 — 「可见态」精确定义 + feed 读路径 enforce(Codex H9)
**现状(已核验)**:`ProjectStatusEnum` DRAFT0/REVIEWING1/APPROVED2/REJECTED3/**PUBLISHED4**/UNLISTED5/BANNED6(`game-module-project-api/.../ProjectStatusEnum.java:21-27`);`game_project.current_version_id` 在(`V1.0.0:24`);`game_runtime_package.status` 0/**1已发布**/2(`PackageStatusEnum`);feed 默认查 `zone_id=0`(`FeedServiceImpl.java:47`)。**Codex H9:feed 读只过 `rank.zone/status/sort`(`FeedRankMapper.java:29`),发布门禁 `isGamePublished()` 默认 true(`FeedServiceImpl.java:285`)——写错一条 rank 就出流。**
**解法 —— 可见态四条件谓词 + 读写双侧 enforce**:
```
可见态 = project.status==PUBLISHED(4) AND project.current_version_id==versionId
AND game_runtime_package(versionId).status==PUBLISHED(1) AND game_feed_rank(game_id,zone_id).status==1
```
- 写侧:`FeedApi.upsertRank` 写 `status=1` 前**校验前三条件**(不满足拒写)。
- 读侧:去掉 `isGamePublished()` 默认放行;`buildStream` 对 rank 结果**批查 project 二次过滤**(或 join),漏发/降级游戏不出流。
**AC**:仅走完 §3.2 编排的游戏进 feed;手工写错的 rank 行不出流。
### 3.2 C2 — 发布编排(核心;本地事务硬约束 Codex H5/H6)
**现状(已核验)**:`reviewProject` APPROVE 落 `APPROVED(2)`、**全仓无 setStatus(PUBLISHED)**;`submitPublish` 止于 REVIEWING + compliance gate 已注入(`ProjectServiceImpl.java:140`);**feed-api 无 FeedApi**、`getZones()` 返空;**可复用孤儿端点** `RuntimePackageServiceImpl.publishPackage`(`:57-82`)翻包 0→1,但 version 回写是 TODO(`:77`)、不写 feed、无调用方。**Codex H5:跨模块 -api 是 @FeignClient 风格(`ProjectApi.java:22`),本地事务仅靠 `@Primary` 本地实现(`ProjectApiImpl.java:21`)。**
**解法 —— 新增 `PublishOrchestrationService`(project-server),α 在 `reviewProject(APPROVE)` 同一 `@Transactional` 内串联**:
1. 记 APPROVE 审核记录。
2. 校验 `game_runtime_package(currentVersionId).status==0预览就绪`,否则抛 `PROJECT_PUBLISH_PACKAGE_NOT_READY`(**新错误码**),事务回滚、审核不落。
3. `RuntimePackageApi.publish(currentVersionId)`:包 0→1(复用 `publishPackage` 逻辑)。
4. **`game_version(currentVersionId).status=3已发布` 由本编排显式写**(不依赖 runtime TODO,Codex H6)。
5. `project.status=PUBLISHED(4)`(**首个 PUBLISHED 写入点**)。
6. `FeedApi.upsertRank(...)`:写 `game_feed_rank(game_id, zone_id=launch_zone_id||0, version_id, quality_score=0, boost=0, sort_score=0, status=1, pinned=0)`(**发布基线 upsert,与 §3.5 算分 upsert 分流**)。
**事务原子硬约束(Codex H5,B1 必守)**:`FeedApiImpl`/`RuntimePackageApiImpl` 必须为 `@RestController @Primary` **本地委托** Service 的实现;`PublishOrchestrationService` 注入这些**本地 bean**;事务内**禁止** Feign/HTTP/`@Async`/`REQUIRES_NEW`。**加故障注入测试:feed upsert 抛错 → project/package/version 全回滚。**
**契约/文件**:新增 `game-module-feed-api`:`FeedApi`+`FeedRankUpsertReqDTO`(含 `mode`:PUBLISH_BASELINE / QUALITY_REFRESH 分流,Codex M11);`game-module-runtime-api`:`RuntimePackageApi.publish(versionId)`;project-server 加依赖 `feed-api`/`runtime-api`;新增 `FeedApiImpl`/`RuntimePackageApiImpl`(@Primary 本地);改 `ProjectServiceImpl.reviewProject` APPROVE 分支调编排。
**AC**:人工 APPROVE 后 `/app-api/feed/stream`(zone 0)含该游戏,顺序由 `sort_score` 真实驱动。
### 3.3 C3 — version 创建 + 完成回填闭环(Codex H7)
**现状(已核验)**:`game_version` 表在(`V1.0.0:44-61`),**全仓无 insert**;`AigcTaskDO.versionId` 字段在(`:43`)靠未实现 difyCallback 回填;**studio 轮询完成态直接取 `task.getVersionId()`(`StudioServiceImpl.java:181`)**——只在 PackageFactory 插 version 不够。
**解法 —— 落包步骤建版本 + 回填 aigc 任务,按 taskId 幂等**:PackageFactory 成功落包后:① `ProjectVersionApi.createForPackage(...)` 创建/复用 `game_version`(status=2预览就绪);② 写 `game_runtime_package`(status=0);③ `AigcTaskService.completeWithVersion(taskId, versionId)` 回填 `game_aigc_task.version_id` + status=succeeded → studio 轮询据此拿到 versionId。`current_version_id` 仍由创作者 `submitPublish(versionId)` 写入。
**契约/文件**:新增 project-server `GameVersionDO`/`GameVersionMapper`/`GameVersionService` + `ProjectVersionApi.createForPackage`(-api);aigc 新增 `AigcTaskService.completeWithVersion`(幂等)。**不改 V1 表结构**。
**AC**:每个 clicker-plus 包对应真实 `game_version` 行;studio 轮询能拿到 versionId;重复回填幂等。
### 3.4 C4 — package 落地 + manifest 原样服务(Codex C4)
**现状(已核验)**:`game_runtime_package` **无 package_json 列**,manifestUrl 由 package_url 纯派生(`RuntimeConvert.java:44`、`runtime.yaml:223`);前端 `fetchAndVerifyManifest` 对**原始字节**算 sha256、checksum 缺省则跳过(`inject.ts:169-186`);当前最大 Flyway=**V9**。
**解法(MVP 无 OSS,包存 DB,端点原样服务,抽象隔离)**:
- **Flyway V10**:`ALTER TABLE game_runtime_package ADD COLUMN package_json LONGTEXT NULL COMMENT 'MVP 临时整包;M3 接 OSS 后下线(退场契约)'`。三处同步(contracts/db-schemas、模块、yudao-server)。
- 新增 `PackageStore` 接口(runtime-server):`putManifest(versionId,json)`/`getManifest(versionId):String`;MVP impl=DB(读写 package_json),M3 换 OSS impl 调用方不变(退场契约)。
- 新增端点 `GET /app-api/runtime/package/{versionId}/manifest`:**返回原始 JSON 文本,不包 `CommonResult`、不 parse/re-serialize、不加换行**(Codex C4);`checksum=sha256(该响应文本)`,PackageFactory 写入时按相同字节计算并存 `game_runtime_package.checksum`。
- 前端:相对 `manifestUrl` resolve 到 API base(§2.5);取包统一走 request(§2.4)。
**AC**:`GamePlayer` 取真包→manifest 来自 DB→sha256 校验通过→runtime 加载试玩,**不退 demo 兜底**(staging 须真包)。
### 3.5 C5 — eventId 幂等(v1 简化被推翻,恢复 design v2)+ process_status(Codex C1/C2)
**现状(已核验)**:`game_telemetry_event` 唯一键 `uk_dedup(trace_id,event,ts)`(`V5.0.0:38`)、**无 eventId**;信封无 eventId、事件名 `game_play_start`/`game_play_end`(props `duration_ms`,`completed`);ingest 仅 `dispatchToMq()` 空壳**不落库**(`EventIngestServiceImpl.java:120-124`);`process_status` 语义 0待聚合/1已聚合/2失败(`V5.0.0:13,30`);前端无 eventId、traceId 默认 `''`。
**解法(Codex C1:恢复 eventId;C2:定义 process_status 事务)**:
- **恢复 `eventId`(每事件实例 UUID,幂等真身)**:`events.schema.json` + `telemetry.yaml` 信封加 `eventId`(必填);**Flyway V10** 给 `game_telemetry_event` 加 `event_id VARCHAR(64)` + **以 `uk_event_id(event_id)` 替换 `uk_dedup`**(UUID 全局唯一,彻底解决"同毫秒同名误吞 + 重放重计数");前端 `buildEnvelope` 生成 `eventId=crypto.randomUUID()` + 真 `traceId`(每 play 会话一个)。
- **同步写本地事务(替代 MQ)**:`ingestBatch` 在一个 `@Transactional` 内:逐信封 `INSERT`(uk_event_id 幂等)→**仅"真插入"(affected>0)才**累加 `game_telemetry_game_stat`(按 `(game_id,stat_date)` upsert)→ 算 `quality_score` → `FeedApi.upsertRank(mode=QUALITY_REFRESH)` 回灌 → 把该事件 `process_status=1已聚合`;任一步失败整体回滚(事件不计)。**MQ producer/consumer 只留契约+TODO。**
**quality_score 口径**:`clamp(60×完玩率+20×like率+20×share率,0,100)`,完玩率=completed_count/play_end_count,写 `game_telemetry_game_stat.quality_score`(DECIMAL5,2);回灌 `game_feed_rank.quality_score`(DECIMAL10,4)=当日 stat(**精度转换**),`sort_score=quality_score+boost`。
**AC**:B 上报 `game_play_start+game_play_end(duration_ms,completed=true)+like+share`(带 eventId+真 traceId)→ `game_telemetry_event` 有原始行 → stat.quality_score 变大 → rank.sort_score 同步变大;**重放同批次(同 eventId)不重复计数**。
---
## 4. 三批次执行(涉及模块/文件/步骤/失败路径)
### 4.1 批次①契约/状态机校正(B1)
落 §2 全部前置项(含 host 取包统一走 request、Play.onTelemetry 白名单、eventId/traceId);落 §3.1 可见态读写 enforce、§3.2 发布编排(@Primary 本地事务 + 故障注入测试)、§3.3 version 创建+回填、§3.4 V10+PackageStore+manifest 端点、§3.5 V10 eventId+同步写。
- **失败路径**:包未就绪→拒发布并回滚;compliance block→不写库;feed 本体回填查不到 project→空态;编排任一步失败→全回滚。
- **done**:前端真连 staging;submit→人工 APPROVE→项目 PUBLISHED(4)+包 1+版本 3+feed rank(status=1);feed/stream 读到它。
### 4.2 批次②最小数据闭环(B2)← 护城河种子,A/B AC 在此锁定
PackageFactory(aigc 桩→确定性产 clicker-plus 包)→ 落包(建 version+runtime_package+PackageStore 存 package_json,回填 aigc 任务)→ §4.1 发布编排入 feed → 通用 clicker-plus runtime 试玩。
- **runtime 必须真吐 `game_play_start`/`game_play_end(duration_ms,completed)`/`like`/`share`**(带 eventId),经 Play.onTelemetry 白名单原样上报 → §3.5 同步写 → 算分 → 回灌。
- **失败路径**:取包失败→**不静默退 demo 兜底掩盖**(demo 兜底仅 mock 链路)。
- **done = 数据回路 A/B 验收协议通过(§6)。**
### 4.3 批次③小卖部皮肤 + ≥10 种子(B3,可降级)
clicker-plus 配置出小卖部皮(点击售卖+极简库存)+ ≥10 参数化种子;撞 <15KB 预算先退纯 clicker-plus,**数据回路 AC 不动**。done:feed 有内容池(≥10)。
---
## 5. 接口/数据契约清单(contract-first,先改后写 —— 补全 Codex M11)
| 契约/产物 | 动作 | 位置 |
|---|---|---|
| `FeedApi` + `FeedRankUpsertReqDTO`(含 `mode`:PUBLISH_BASELINE/QUALITY_REFRESH) + **`FeedApiImpl`(@Primary 本地)** | 新增 | `game-module-feed-{api,server}` |
| `RuntimePackageApi.publish(versionId)` + **`RuntimePackageApiImpl`(@Primary 本地)** | 新增 | `game-module-runtime-{api,server}` |
| `ProjectVersionApi.createForPackage` + `GameVersionDO/Mapper/Service` | 新增 | `game-module-project-{api,server}` |
| `AigcTaskService.completeWithVersion(taskId,versionId)`(幂等) | 新增 | `game-module-aigc-server` |
| `PublishOrchestrationService` | 新增 | `game-module-project-server` |
| **pom 依赖**:project-server→feed-api/runtime-api;telemetry-server→feed-api | 新增 | 各 server `pom.xml` |
| 错误码 `PROJECT_PUBLISH_PACKAGE_NOT_READY`(1-100 段) | 新增 | project-api `ErrorCodeConstants` |
| **V10**:`game_runtime_package.package_json LONGTEXT` + `game_telemetry_event.event_id`+`uk_event_id`(替换 uk_dedup) | 新增(三处同步)| `contracts/db-schemas/V10.0.0__*.sql` + 模块 + yudao-server |
| `events.schema.json` / `telemetry.yaml` 信封加 `eventId`(必填) | 改 | `contracts/` |
| `runtime.yaml` 新增 `/manifest` 端点(原样 JSON)+ manifestUrl 语义说明 | 改 | `contracts/api-schemas/runtime.yaml` |
| Mapper 自定义 upsert SQL(stat、feed_rank 两套 mode) | 新增 | 各 server Mapper |
| `project.yaml` α 审核响应说明 + admin `review/index.vue` 状态筛选/操作 | 改 | contracts + game-admin |
| `game-package.schema.json` | 不改(clicker-plus gameConfig 子 schema 作 templates/ 后续批)| — |
> **B1 实现守门(第二轮 Codex 确认轮残留,必守)**:① `event_id VARCHAR(64) NOT NULL` 再加 `uk_event_id`(MySQL 唯一键允许多 NULL,必须非空,否则幂等失效);② **V10 只能有一个被 Flyway 实际执行的副本**(`yudao-server/.../db/migration/`),勿在多个 classpath jar 放同版本 V10(一个 V10 文件同时 `ALTER` runtime+telemetry 两表 OK);③ `FeedRankUpsertReqDTO.mode=QUALITY_REFRESH` 只更 quality_score/sort_score,**保留 boost/pinned/status**;④ 新增 `-api` 包**不得反向依赖 `-server` 或互嵌对方 DTO**(防 Maven 成环;已核实现有方向 server→对方 api 无环)。
---
## 6. 验证(A/B 协议 + 通用判定 + 负路径)
**A/B 协议(可执行)**:备同 zone 同 boost 的 A/B 两个已发布游戏(tenant_id=1)→记初始 feed 顺序→向 B 上报有效 `game_play_start+game_play_end(duration_ms,completed=true)+like+share`(带 eventId+真 traceId)→断言 `game_telemetry_event` 有原始行、`stat.quality_score` 变大、`feed_rank.sort_score` 同步变大→再请求 feed **B 排到 A 前**→**重放同批次(同 eventId)不重复计数**→**关 mock 仍通过**。
**通用判定**:不只看 200,须 `code==0`+结构符 yaml+非 mock 特征值;**主 agent 独立复跑,不采信子代理自报**。
**负路径**:错 token→401;停后端→不白屏;空数据→空态。**故障注入**:feed upsert 抛错→发布事务全回滚(§3.2)。
**数据清理**:固定 traceId/eventId 前缀 + 固定 gameId 清单 + cleanup SQL,验证前后表快照。
## 7. 回滚(4 独立单元,各自 commit)
① 前端联调(env/mock/token/tenant/beacon/取包统一/onTelemetry/eventId)|② 发布编排(PublishOrchestration+FeedApi/RuntimePackageApi @Primary+GameVersion+ProjectVersionApi+V10 package_json+PackageStore+manifest 端点)|③ telemetry 同步写+算分+回灌+V10 event_id |④ clicker-plus runtime+种子脚本。
## 8. 完成条件(B0→B3)
§6 A/B 协议通过(关 mock)= 本轮完成线;4 链路烟测登记总账;缺口 T+1 修复机制(标 owner/严重度/是否阻断/最晚日)。
## 9. 决策审计 + 双模型对账
| 项 | v1 | Codex 二审 | v2 处置 |
|---|---|---|---|
| C1 可见态 | 四条件谓词 | H9:读路径未 enforce | 读写双侧 enforce + 去 isGamePublished 默认放行 |
| C2 编排 | 复用孤儿端点 + @Transactional | H5:仅 @Primary 本地实现成立;H6:version 由 project 写 | @Primary 硬约束 + 故障注入测试 + version 显式写 |
| C3 version | 落包建版本 | H7:缺 aigc 回填闭环(studio 依赖) | + completeWithVersion 回填,taskId 幂等 |
| C4 package | V10 package_json + PackageStore | C4:manifest 端点须原样 JSON + 前端 baseURL resolve | 端点不包 CommonResult + 前端 resolve + 取包统一走 request |
| **C5 幂等** | **砍 eventId 复用 (trace_id,event,ts)** | **C1:不成立(误吞/重计);C2:process_status 未定义** | **恢复 eventId+uk_event_id(推翻 v1)+ 同步写本地事务定义 process_status** |
| 前端联调 | 6 前置项 | C3:host 取包绕 request;H8:Play 吞 like/share | + 取包统一走 request + onTelemetry 白名单 + Feed traceId |
| D-PUB | α 待点头 | M10:改 APPROVED 语义,admin 连带 | **已拍板 α**(2026-06-09)+ 连带改 admin/enum/project.yaml/单测 |
| 契约清单 | §5 | M11:缺实现类/错误码/pom/upsert 分流 | §5 补全 |
**评审轮次**:① 主 agent Opus 子代理独立核验真实代码(5 critical 全实,C2/C5 有简化空间);② Codex 首轮(11 项 4C+5H+2M,全采纳,推翻 v1 C5 简化);③ Codex 第二轮确认轮(2026-06-09):**Go,11 项 spec 层全闭合、无新设计矛盾**,残留 4 守门见 §5 末。