# 验证记录与明确边界

结论：统一代码已交付并接线；隔离生产方法的 RED/GREEN 与 Java 语法检查已经执行。完整工程编译、JUnit、真实数据库/MQ/Redis/TTS和业务验收未执行。没有执行 Maven。

## 已执行

| 验证 | 实际范围 | 结果/证据 |
| --- | --- | --- |
| 导入探针 | 完整生产Reader、Normalizer；生产Param嵌套类逐字提取；仅异常基础设施替身 | `verification/red-import.txt` 退出1复现未闭引号吞行；`green-import.txt` 退出0。非法variant整行拒绝，0/-1拒绝，合法limit保留。 |
| 批量发布探针 | 完整生产EnglishBatchPublishTask；内存发布服务与任务框架替身 | `red-batch.txt` 退出1复现limit=0真写250；`green-batch.txt` 退出0，显式0/纯0/逗号拒绝且零调用，150/2正确限量。 |
| 音频生命周期探针 | 生产handleInFlight方法逐字提取；TTS/音频保存/DAO/Redis为替身 | `red-audio.txt` 退出1；`green-audio.txt` 退出0。7项：completed/query异常/cancel异常/running总超时，终态计数，成功清计数，预算内下载失败保留任务。 |
| 词边界/IPA探针 | 完整生产EnglishWords及Normalizer | `red-words.txt` 退出1；`green-words.txt` 退出0。7项：数字切词、撇号/hyphen、纯数字、and/or、Unicode IPA、标记ASCII IPA、普通方括号。 |
| Java AST语法 | code树全部Java文件，JDK Compiler API只parse不analyze | `verification/java-parse.txt`。只证明语法，无完整类型解析/链接/注入验证。 |
| 统一补丁可应用 | 按root的assemble.py，以固定快照对应文件组成基线副本 | `patch-check.json`。仅证明补丁可应用，不代表运行正确。 |

探针脚本保留在verification/，可用Python3+本机JDK重复运行。`--baseline`选项用于RED。RED探针退出1是预期回归失败，不属于环境错误。SLF4J无provider警告来自隔离classpath，不影响断言；日志基础设施未在探针里验证。

## 已交付但未执行的JUnit

- `code/application/src/test/java/cn/jojo/cc/biz/service/english/dict/EnglishImportBoundaryRegressionTest.java`
- `code/common/src/test/java/cn/jojo/cc/common/utils/EnglishWordsBoundaryRegressionTest.java`
- `code/application/src/test/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioLifecycleRegressionTest.java`
- `code/application/src/test/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentRevisionRegressionTest.java`
- `code/application/src/test/java/cn/jojo/cc/biz/service/english/dict/EnglishAudioPublishGuardRegressionTest.java`

按项目规则应在IDE使用Java21/JUnit5/Mockito执行。未计算80%覆盖率，也没有宣称全部新增分支都有RED/GREEN。其他事项主要是完整源码静态核对及新增测试交付，不能外推为运行验收。

## 必须在集成环境补验

1. JY/PROD两库先执行outbox及auto_publish_eligible DDL；必须配置`englishDictOutboxReplay` Job，每分钟按现有租户上下文运行。新代码缺表会回滚发布或音频完成事务，不能先发代码后补表。
2. 草稿隔离：TTS在途时上传人工音频并修改翻译保存；恢复TTS，确认JY回填或清任务但PROD翻译、名称、变体和status保持旧正式版本；手动发布后才变化。
3. 生命周期：连续failed/cancelled/超时三次停提交；最低ID一页全失败后，后续ID可随游标轮转继续提交；失败计数7天过期后的再尝试符合现有策略。
4. 音频回填：唯一缺音频的单词从completeness0变1；仍缺音标或任一翻译字段为空则保持0；TEACHING旧详情缓存实际被清，RPC complete随之收敛。
5. 首次发布：暂停计算worker先发布，仍有成对JY/PROD INIT身份；恢复单题/全量后PROD NORMAL，ID严格映射。测试历史PROD完全缺行路径，禁止自由自增建行。
6. 跨内容位：旧MATERIAL任务停在锁前，新ANSWER_AREA正文保存并算成功，再放旧任务；旧任务必须整次拒绝，不得新插旧type、删新type或清新集合。新增空正文与任务失败/事务回滚并发场景。
7. 保存事务：暂停MQ/派生表不可用，题目和表单仍能提交；后置失信可失败，但下发按本次已选输出的生成输入hash/完整输出hash/本库form与config身份拒绝旧序列，不用后读DB代替响应来源。验证新增DB行锁等待、计算事务回滚后独立失败登记。
8. outbox：PROD成功后进程立即退出、Redis失败、broker失败、Job重入、登记回执失败；待办不丢，失败退避不挡健康事件，重投幂等；broker确认后的消费者重试/DLQ另行确认，不以delivered_time冒充实际缓存完成。
9. 索引：Redis远端版本更新且重装失败，读路径可服务旧快照，写路径不能持久化旧索引结果；远端不可读/UNKNOWN同样不能写。恢复后重算收敛。
10. 导入：非法CSV全文件不写；非法variant该行零写，修文件重导可完成；小数、null、字符串、溢出等limit在下载前拒绝；原正常格式及去重不回归。

## 原Mockito测试的实际迁移

已将以下7份原测试文件完整输出到code/，提供了具体fixture迁移，不仅是适配建议：

- EnglishAudioBackfillBizServiceImplTest：completionTx注入；游标计数器与失败计数器分离；三参游标查询；受控音频发布入口断言。
- SegmentAutoFillBizServiceTest：预查询CoreService注入；新补录fixture显式资格1；发布入口改为锁内资格门。
- QuestionSegmentBizServiceTest：真实共享正文服务和真实事务协调器fixture；题helper设置lockQuestion返回待测题；写路径freshSnapshotForWrite。
- QuestionSegmentSaveHookTest：提交前不接触派生服务；新增提交后失信失败仍投消息用例。
- EnglishSegmentDeliveryBizServiceTest：批量当前题目和共享正文源fixture；行携带实际contentType/MD5；新增本次所选v1输出与库存v2不匹配用例。
- EnglishPublishBizServiceTest：失效不再在发布线程异步发送，断言内部pending；批量另计pendingCacheCount，PROD successCount与缓存完成分开。
- EnglishDictWriteTxServiceTest：人工保存清除资格且保持source断言。

本包新增QuestionSegmentRevisionRegressionTest也已迁移到真实事务协调器fixture。新增QuestionSegmentTransactionRegressionTest使用真实Spring事务拦截器+内存manager检查回滚/登记顺序；QuestionSegmentLegacySchemaRegressionTest核旧题表模型和SQL删除列契约。这些JUnit未执行，不能据此声称旧suite全绿。第二轮另迁移DefaultSegmentTextExtractorTest的完整IPA token策略，以及QuestionServiceImplSegmentsTest请求局部凭据map接线。

## fix-round1：独立复审R1–R5统一修复波

初轮REVIEW.md保留。review-baseline/是修复前完整修改文件树；fix-wave.patch由根代理统一生成。新增3组RED/GREEN：

| 验证 | 实际执行 | 结果与限制 |
| --- | --- | --- |
| R1旧表列契约 | 从生产DAO逐字抽取SQL，在SQLite真实建有del_flag而无delete_time的最小表上执行原查询过滤；只移除SQLite不支持的FOR UPDATE并替换参数占位 | red-sql-contract退出1明确no such column；green-sql-contract退出0，活行命中/已删行拒绝。这不是MySQL锁或迁移部署验证。 |
| R2/R3/R4与急停 | 完整生产engine、TxService、StateTxService、shared content、delivery、save hook共6个类实际javac；使用真实Spring事务注解拦截器，内存transaction manager和DAO/TTS/配置等外围替身 | red-segment-wave退出1，失败登记在rollback前丢失；green-segment-wave退出0，事件顺序begin/lock/rollback/begin/lock/failed/commit。还实际断言同审计时间新正文拒旧NORMAL、相同正文可下发、正文抽取异常不给segments、主hook无派生DB调用、后置失信失败继续投消息、暂停不查锁。不是MySQL事务或多线程锁实跑。 |
| R5同秒接管 | publishAutoFillWord原方法逐字提取，实体/锁/PROD写入为替身；createTime与updateTime固定相同 | red-eligibility退出1，flag0/null也发布；green-eligibility退出0，只有flag1放行。人工save清flag及DDL接线另有静态/JUnit。 |

分词波探针本机classpath采用Spring核心6.2.0、spring-tx6.2.1及已有本地依赖；未冒称与部署依赖完全一致。初次探针编译发现本地无spring-tx6.2.0，已改用实际存在的6.2.1后重新编译/执行成功。探针生成脚本早期引号错误已修正；只有最终green文件被列为通过证据。

新资格列迁移存量全部默认0：历史时间戳无法证明未人工编辑，历史补录草稿应人工确认发布，不根据create_time==update_time回填1。source字段业务含义保持不变。

本次未建立CodeGraph临时索引，无需清理索引；未触碰原分析源码快照。

## fix-round2：绑定本次实际响应输出

第二轮REVIEW-FINAL.md要求的R4来源错位已按真实生成链实现，等待定向复核：

- 生成器在schema转换前记录canonical原FormData输入hash；对象key递归排序，显式null和数组顺序保留，生产/消费用同一个SegmentPayloadIdentity函数。
- OutputData新增VARCHAR(40)来源hash；缓存URL/输出JSON/输入hash/formDataId及scalar输出hash从同一OutputData行装配，listSimple显式SELECT已补新列；旧六参BO构造兼容。
- 分词JSON保存输入hash、完整输出hash和本库form/config ID；仍受3000长度门。RPC选择URL时复制不可变凭据并传给fillSegments，delivery完全依此凭据核验，不再以当前DB代表已选响应。
- `verification/red-response-binding.txt`退出1，先选v1再库存/DBv2误配以及缺元数据旧缓存误放行；`green-response-binding.txt`退出0，共8个断言含同v2正常对照、null/缺字段、数组顺序和配置ID拒绝。实际编译生产Delivery/Identity/SelectedOutput/Snapshot，RPC fill方法逐字提取；模型/DAO/URI基础设施替身。未运行整个RPC服务或真实缓存/输出生成。
- 最终`run-segment-wave.py`已适配真实双参SelectedSegmentOutput调用，保留真实Spring事务拦截器的回滚后FAILED登记、急停先于锁、提交后隔离等断言，最新green-segment-wave退出0。
- 10组probe已针对最终冻结code全部重跑；完整命令、退出码、证据SHA及code SHA见`verification/final-probe-runs.json`。当前AST为72个Java文件，只证明语法。

### 部署与滚动恢复（尚未执行）

1. JY/PROD都先执行三份DDL，包括`code/database/597084-output-source-identity.sql`。只有OutputData加一个40字符列；库存来源元数据在原segments JSON内，没有存完整payload。
2. 生成器、API及worker都升级后，旧缓存/库存没有可信身份时只省略segments，题目主体继续返回。旧缓存的URL绝不现场贴当前DB的新hash。
3. 必须配置`englishSegmentOutputIdentityRecover`每分钟持续运行的正式来源恢复作业；每轮两库各50候选题、持久ID游标，共用既有分词刷新锁。它真实调用现有`refreshGenStandQuestion`生成/上传输出，不是只回填hash；该服务没有强制切库注解，继承当前Job数据源，生成器在本库事务写入。
4. 新输出确认和当前原输入匹配后，作业登记`FORM_OUTPUT_CACHE` outbox事件驱逐对应FormAndDataCacheKey，再force重算。已有`englishDictOutboxReplay`必须继续每分钟执行。失败记日志且游标继续，下一完整轮可重试；两库finished=true仅表示本轮扫描完成，游标归零后仍须持续调度；后续正常发布也可能再次产生跨库输入身份差异，不能停用。
5. JY/PROD来源ID分别取本库，OutputData.formDataId沿原@TableRelation映射；若发布映射改变原FormData内容hash，必须在PROD真实重生成再恢复，不能复制JY身份ID去冒认PROD。
6. 未执行真实schema生成、OSS上传、缓存驱逐消费、Job两库运行、并发RPC或完整JUnit。新增9份JUnit和9份原fixture迁移代码不等于套件已通过。
7. 多内容配置下现行单行库存只保存实际计算配置的身份；选择其它配置时安全省略，不混合序列。多个配置同时可点击需要原单行模型之外的明确设计；正常单配置同版本已给实际探针对照。


## fix-round3最终新增验证

- `run-cache-roundtrip.py --baseline`对review-round2-baseline真实BO/SelectedOutput执行默认JSON.toJSONString + parseObject，退出1：显式嵌套null丢失导致正常缓存身份失配。
- `run-cache-roundtrip.py`对当前真实BO/SelectedOutput退出0：DB直读正常、Redis往返scalar仍匹配、旧缺scalar拒绝。输出日志保留实际序列化字符串，不使用只在内存相等的替代证明。
- `run-job-pause.py`编译完整生产Job，框架/Redis/业务服务替身；总开关关闭、execution关闭均0锁/0服务调用，正常对照1锁/1调用，退出0。服务内部复检静态核对，真实OSS/DB仍未运行。
- 最终原8组+新增2组共10组对冻结code全部重跑exit0；72 Java AST通过。最终命令、证据SHA、code SHA见final-probe-runs.json。
- `englishSegmentOutputIdentityRecover`必须每分钟持续运行，每库每轮50。保守完整周期为ceil(N/50)+1个成功调度轮加执行时间；整页额外空页归零，锁竞争跳过、暂停、故障/失败重试不含在无条件时间承诺内。不能扫描完就停用。
