# 统一修复第3次定向复核（R4响应来源绑定修复波）

规格：FAIL。质量：FAIL。原R4的“响应旧正文配新分词”漏洞已解决，但本轮引入的来源绑定在正常跨库发布后的恢复尚未形成持续生命周期；仍有1项P2重要问题。R1/R2/R3/R5保持ADDRESSED。

本轮只复核verification/fix-wave2.patch、新增来源与恢复链及必要既有生产依赖；未执行Maven/业务DB/OSS/MQ，未重复执行已提供探针。上一轮报告保留在verification/REVIEW-wave1.md。

| 项 | 最终状态 | 事实 |
|---|---|---|
| R1 错误删除字段 | ADDRESSED | cc_new_question锁条件仍为del_flag，前轮修复未回退。 |
| R2 派生事务拖垮同步 | ADDRESSED | hook仅登记事件、提交后独立事务失信、异常后继续投递，前轮隔离未回退。 |
| R3 FAILED随主事务回滚 | ADDRESSED | compute代理回滚后再独立recordFailure，前轮事务边界未回退。 |
| R4 响应来源一致性 | 原错误序列漏洞ADDRESSED；恢复要求NOT_ADDRESSED | 本次已选输出凭据绑定正确，正常发布后的持续恢复仍有下述R4-R。 |
| R5 同秒人工草稿 | ADDRESSED | 显式auto_publish_eligible且人工保存清零，前轮保护未回退。 |

## R4已解决部分

1. QuestionServiceImpl.listGenStandQuestion在设置questionInfo.data的同一个contentConfigBo上capture SelectedSegmentOutput；局部map随本次RPC传入fillSegments。环境、配置ID和URL校验均在该处完成，不再独立读新DB给旧响应背书。
2. SelectedSegmentOutput捕获生成输入hash、FormData ID、配置ID及完整输出JSON canonical hash；SegmentSnapshot同样保存四项身份，delivery逐行比较且仍要求NORMAL。旧缓存缺元数据直接不下发segments。
3. GenerateOutPutDataService在schema转换前取输入hash，strJson同时用于OutputData和上传；缓存两个装配路径从同一OutputData取URL、output JSON及来源hash/formDataId；listSimple显式select新列已补。
4. canonical保留null/缺字段差异、数组顺序，消除对象键序差异。green-response日志8项覆盖先取v1后v2分词拒绝、同v2正常、旧meta拒绝及规范化边界。该探针不是完整RPC/OSS端到端测试。
5. DefaultSegmentTextExtractor已按ContentNormConfig.id定位真实FormData业务键，与refreshGenStandQuestion一致，不再以questionId冒充配置ID。没有可信来源身份时不会恢复点击。

## R4-R [P2] 正常跨库发布会再次改变输入身份，恢复Job却被定义为一次迁移

位置：`code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/DefaultSegmentTextExtractor.java:126-144`、`EnglishSegmentOutputIdentityRecoveryBizService.java:68-95`、`code/api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishSegmentOutputIdentityRecoveryJobHandler.java:13`；部署说明`FIX-LOGIC.md:717-719`。

确定生产链：

- FormDataBizServiceImpl.publishFormData先调用formDataTableRelation，把输入JSON中schema标为TABLE_RELATION的数值/数值数组执行decrementAndGet，再同步至PROD。
- 同方法随后调用OutputDataBizServiceImpl.publishOutputData；该服务仅读取JY输出并交SynchBizServiceImpl.publishOutPutData构建snapshot复制，未按映射后的PROD输入重算segment_source_payload_hash。
- 因此对于含跨库引用字段的表单，PROD当前输入hash为H_prod，复制过来的OutputData来源hash仍H_jy；即便正文文字未变，两者也不同。
- 新Extractor只接受inputHash==output.segmentSourcePayloadHash；不匹配时outputHash为null，库存不会写可靠身份，delivery继续fail-closed。
- 新恢复Job能够真实重生成并清缓存、force重算，这是有效恢复入口。但文档明确“完整一轮后可停迁移Job”，主发布与每日分词刷新路径并没有调用该输出恢复入口。

可达情形：上线执行完一次来源迁移后停止该Job；之后运营编辑并正常发布一个含TABLE_RELATION资源ID的标准题。主发布复制新的JY hash与映射后的PROD输入，造成身份不匹配。发布分词MQ和每日force均只重分词，不重生成输出，因此该题持续segments=null，必须再次人工开启来源恢复Job才能恢复。这不是仅旧数据迁移窗口；未来每次此类发布都可复现。

反证：无发生跨库数值变化的输入可正常匹配；保持恢复Job运行且生成成功时可恢复。因此本项不是“来源比较错误/所有题无法用”，而是把持续生产依赖误当一次迁移的生命周期缺口。输入hash包含全FormData，这一差异在FIX-LOGIC:717也已明确承认。

建议（二选一即可）：

- 把来源恢复者作为必须持续运行的调度，完整轮次后继续扫描；去掉“迁移后可停”，给出与恢复时限一致的调度频率和故障可观测约定。现有游标循环可复用。
- 或将缺失/跨库不匹配身份的真实输出生成可靠登记于发布后恢复链，失败持久重试，不仅依赖人工再开迁移任务。

必须补闭环用例：完成一次迁移后，再发布带资源ID映射的新表单；正式输入/输出凭据重新收敛，旧缓存失效后可点击。不能以“same selected v2与v2 identity相等”的纯比较探针替代跨库生成链。

## 本轮未发现需另报的重要问题与验证边界

- 未发现来源字段的悬空调用；回读投影、缓存两入口、RPC凭据传播及segments JSON落库均有实现。
- 旧缓存恢复不冒贴新hash到旧URL；恢复服务调用真实refreshGenStandQuestion，并经outbox清对应FormAndDataCacheKey后force重算。
- 新来源元数据占用segments原VARCHAR3000容量，代码已显式拒绝超长并记录失败，未悄悄截断；实际可容纳词数减少需在部署验收中覆盖。
- 71 Java AST仅语法；新增探针不证明全部类型/完整JUnit通过。三份DDL、持续调度配置、跨源ID映射、真实生成上传、MySQL锁事务、MQ及最终缓存消费仍需环境验收。
- 未创建临时CodeGraph索引，无需清理。
