{
  "baseline": "0baa9ebe8b7f9ecc46f675acbf1f56ac65378957",
  "formal_issues": 17,
  "conditional_boundaries": 2,
  "supplementary_scenarios": 7,
  "matrix_rows": 26,
  "final_review": "规格/代码质量审查通过，详见REVIEW-FINAL.md",
  "business_execution": "未执行真实业务环境验收",
  "headers": [
    "改动ID",
    "关联问题/类型",
    "业务改动",
    "改动前行为",
    "修复代码逻辑",
    "完整代码位置",
    "验证前置与操作",
    "预期结果/观察点",
    "已执行证据",
    "待验证项",
    "影响与回归范围",
    "DDL/配置/上线依赖",
    "交付状态"
  ],
  "rows": [
    [
      "CHG-001",
      "AUDIO-N01 / P1正式问题",
      "音频补发布把曾发布对象的人工待发布草稿一并发布",
      "编辑保留hasPublished=1并置status=0；completed发现人工音频仍返回true；queryInFlight只检查hasPublished而非当前草稿状态，调用完整publishWord/Phrase，锁内重读当前主表、翻译、全部变体发布。",
      "音频自动发布使用独立入口，在与人工保存/发布相同的实体锁内重读status和hasPublished；只有当前PUBLISHED且曾发布才复用完整发布。草稿返回未发布结果，PROD旧版本保留。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:108 public PublishResultBo publishAudioWord\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:119 public PublishResultBo publishAudioPhrase\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:218 private void publishReleased",
      "已发布无音频词有在途TTS；人工上传音频、修改释义仅保存；恢复TTS完成。",
      "人工草稿保持待发布；PROD名称/翻译/变体不变；无草稿且当前已发布的对照可补发布。",
      "新增EnglishAudioPublishGuardRegressionTest（未执行）；锁外旧快照/锁内草稿路径已静态核对。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "词条保存、补录、音频、双库发布",
      "两库先建outbox；注册englishDictOutboxReplay。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-002",
      "AUDIO-N02 / P2正式问题",
      "completed下载永久失败与查询异常均绕过超时收口",
      "elapsed超时只位于null/running分支；query异常直接return，completed保存null也return并保留任务。每轮ORDER BY taskTime,id LIMIT batch重复拿最老行。",
      "总超时判定前置到queryTask之前，覆盖query异常、completed下载失败、running/null全部分支；cancel异常被单独接住，仍计失败并清在途任务。预算内下载失败保持任务，下轮重试。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:262 private boolean handleInFlight",
      "分别持续query异常、completed下载失败、running；提交时间超过1800秒，再测预算内下载失败。",
      "所有超时分支都结束当前尝试并记录失败；cancel异常不阻清理；预算内失败保留任务。",
      "verification/red-audio.txt -> green-audio.txt；4个超时分支及预算内失败保留实跑。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "TTS状态、下载上传、超时与批次",
      "沿用音频回填Job与超时配置。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-003",
      "AUDIO-N03 / P2正式问题",
      "失败上限只统计提交抛错，平台终态失败与超时会无限重新提交",
      "submit成功立即clearFailCount；终态失败和超时只清任务号、不增加失败计数；下轮pending重新提交；永远达不到maxRetry=3。",
      "submit成功不再清计数；终态failed/cancelled与超时每个已结束尝试计一次，只有成功回填或检测到人工已补齐时清计数。提交API抛错仍计数，7天冷却保持现有契约。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:161 private void submit(\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:262 private boolean handleInFlight",
      "submit一直成功而每次终态failed/cancelled或超时；连续三轮；成功/人工补齐另做对照。",
      "失败跨尝试累计，到上限停止提交；只有成功完成才清计数。",
      "verification/green-audio.txt：终态累计、成功清零；submit计数路径静态核对。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "重试预算、Redis计数、平台配额",
      "失败计数7天冷却；验证真实Redis。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-004",
      "AUDIO-N04 / P2正式问题",
      "达到提交失败上限的最早ID持续占满候选批次，后续词无法补音频",
      "SQL先按ID升序LIMIT batch；maxRetry仅业务循环内判断后return，未推进游标/排除冷却行；7天计数TTL内每轮仍选择同一批。",
      "三张表增加严格id>afterId的DAO查询入口。每张表用独立Redis游标跨调度推进；页满推进到最后候选，页不足归零，达到失败上限/非自动出身变形也不能永久占住最早一页。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java:115 private List<EnglishWordDo> pendingWords\ncode/infrastructure/repository/src/main/java/cn/jojo/cc/core/service/impl/english/EnglishWordCoreServiceImpl.java:260 listAudioPendingSubmitAfterId",
      "batch=1，最小ID连续失败达到上限，恢复TTS后还有第二个健康ID；变体前页均人工来源另验。",
      "游标继续前进，后ID能被提交；完整一圈回到起点，不被最早失败页永久占满。",
      "接口/实现/调用三处均已给出；未实跑Redis游标和真实数据库分页。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "三表分页、游标、来源过滤",
      "新增afterId查询及Redis游标，三表独立。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-005",
      "AUDIO-N05 / P2正式问题",
      "异步回填只更新音频列，完整性及教研缓存保持旧值",
      "markAudioCompleted只set音频和清任务两列，不重算completeness；回填服务不调用教学缓存失效；补发布复制JY完整性0到PROD而不重算。",
      "单词条件更新音频时同一SQL重算完整性：word、phonetic非空，存在活翻译且无空词性/译文。音频回填和TEACHING缓存失效、受控补发布事件同JY事务提交；变形按phraseId失效聚合详情。",
      "code/infrastructure/repository/src/main/java/cn/jojo/cc/core/service/impl/english/EnglishWordCoreServiceImpl.java:307 public boolean markAudioCompleted\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishAudioCompletionTxService.java:25 public boolean complete",
      "唯一缺音频的词回填成功；先预热JY详情缓存；缺音标的词作为对照。",
      "字段齐全词completeness=1且RPC complete=true；教研暖缓存通过outbox失效；仍缺字段词保持0。",
      "SQL/事务及outbox调用静态核对；完整性数据库UPDATE、缓存实际消费未执行。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "音频回填事务、完整性、JY/PROD缓存",
      "两库outbox DDL及持续重放Job。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-006",
      "SEG01 / P2正式问题",
      "首次发布早于异步分词完成时，正式库缺行不能由全量自行恢复",
      "buildPublishableSegments空/非成功即返回空，ensureProdSegmentSkeletons直接return；发布MQ随后refreshQuestion见PROD无行直接false；每日force也共用该缺行门。JY次日成功不自动再次发布。",
      "首次发布不要求JY已有NORMAL结果。按正式题型先在JY保留唯一身份，再递减映射PROD骨架；发布及全量刷新缺行路径都调用同一成对建行服务。PROD禁止自行reserveIdentity/自增。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentSkeletonBizService.java:19 public void ensurePair\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentStateTxService.java:29 public QuestionSegmentDo reserveTeachingIdentity\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/SegmentRefreshBizServiceImpl.java:191 private boolean refreshQuestion",
      "首次同步后暂停compute消费者立即发布；随后恢复消费者，再跑全量；对照已有成功行发布。",
      "JY/PROD INIT身份成对，不依赖首次NORMAL才建；随后可算成NORMAL，正式库不自由自增。",
      "JY身份->PROD映射->INIT->刷新接线静态核对；跨库ID实值与调度未跑。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "首次发布、历史缺行、ID映射、全量",
      "部署全部分词修复；来源元数据恢复Job持续调度。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-007",
      "SEG02 / P2正式问题",
      "正文更新后到异步执行前旧NORMAL序列仍可下发",
      "AFTER_COMMIT只发ID消息，提交时未把旧行置INIT/FAILED；delivery只验NORMAL而不比题目正文/revision。worker尚未到引擎时，旧成功行被视为可信。正式库重新发布也保留已有骨架行，不降级。",
      "保存事务内仅登记事件，提交后独立失信，派生失败不回滚主体。输出生成前记录稳定canonical原FormData输入hash；分词在同一次原输入读取中取得文本、输入hash、本库formDataId/configId，并只绑定确由该输入生成的完整输出JSON hash。缓存URL/输入hash/formId及scalar完整输出hash来自同一OutputData行；输出hash在loadDB解析原输出后生成并存为scalar，Redis默认序列化丢嵌套null时不重算。RPC选URL时固定不可变凭据随本次请求传给delivery。库存四项身份与环境都相同才下发，v1响应不能被后读v2分词替换。缺身份安全省略；恢复Job必须每分钟持续调度，真实重生成、驱逐缓存、force重算，不能完整一轮后停用，绝不给旧URL现场贴新hash。总开关/执行急停在Job取锁前阻断，服务及重生成前复检。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentSaveHookService.java:61 public void hookSave\ncode/application/src/main/java/cn/jojo/cc/biz/service/impl/schema/process/oss/GenerateOutPutDataService.java:84 String segmentSourceHash\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/SegmentPayloadIdentity.java:9 public record SegmentPayloadIdentity\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/DefaultSegmentTextExtractor.java:103 public RichTextSource readRichTextSource\ncode/api/api-server/src/main/java/cn/jojo/cc/server/service/impl/QuestionServiceImpl.java:1847 SelectedSegmentOutput.capture\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentDeliveryBizService.java:76 Map<String, SelectedSegmentOutput> selectedOutputs\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/EnglishSegmentOutputIdentityRecoveryBizService.java:76 private void recover",
      "已有成功分词，正文v1改v2；暂停消费者。另先选择v1输出，再发布/重算v2，最后装配响应。",
      "实际所选输出来源与分词不一致则segments=null；同v2正常仍下发；题目主体不因派生失败回滚。",
      "verification/red-response-binding.txt -> green-response-binding.txt：完整生产Delivery/Identity/SelectedOutput+逐字RPC fill方法，外围替身。8项包括先选v1再DB/分词v2必须null、同v2正常、旧缓存拒绝、canonical null/数组/对象序。真实生成器/OSS/双库Job未跑。 verification/green-cache-roundtrip.txt使用真实BO/default serializer验证Redis往返null丢失下scalar仍一致，旧缺scalar拒绝；green-job-pause.txt执行完整Job，暂停0锁/0服务调用，正常对照1/1。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "同步/发布、缓存、输出生成、RPC",
      "输出来源DDL、元数据cache字段、持续恢复Job。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-008",
      "SEG03 / P2正式问题",
      "迟到旧任务删除新revision另一内容位成功行",
      "upsert/isStale仅保护同(question_id,content_type)；旧type1写入可成功；dropObsoleteContentTypes重读当前type2后按ID软删，无revision条件",
      "外层先检查executionEnabled，再经独立代理compute事务SELECT真实cc_new_question WHERE del_flag=0 FOR UPDATE，拒绝旧revision，改用锁内重读题目。跨内容位写入/空集合删除同题锁事务。异常先退出代理回滚释放锁，再由外层调用另一个REQUIRES_NEW事务重锁、核当前revision及可用正文指纹，登记FAILED/retry_count/last_error，绝不让失败登记随原事务回滚或持原锁再自锁。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentTxService.java:22 public <T> T compute\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentTxService.java:29 public void recordFailure\ncode/infrastructure/repository/src/main/java/cn/jojo/cc/core/dao/english/QuestionSegmentDao.java:20 lockQuestion(",
      "旧rev1/MATERIAL任务停在写锁前；新正文rev2/ANSWER_AREA成功后恢复；另用旧空结果试删除。",
      "锁内重读真实修订号，拒绝整个旧写删；不插旧类型、不删新类型；失败诊断单独提交。",
      "verification/red-segment-wave.txt -> green-segment-wave.txt，真实Spring事务拦截器的事件序列为begin/lock/rollback/begin/lock/failed/commit；内存事务manager及DAO替身，非真实MySQL锁/持久化验证。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "跨内容位并发、事务、失败收口",
      "真实MySQL题行锁及双源事务需联调。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-009",
      "SEG04 / P2正式问题",
      "数字夹英文仍作为整词查询",
      "isWordChar包含数字，nextToken连续吞入，isWordToken只要求至少一个英文字母",
      "数字统一按分隔符；nextToken、isWordToken、边界判定共用ASCII字母规则，避免cat2dog、3look/up4进入词典。撇号和hyphen仍仅两侧字母时保留。",
      "code/common/src/main/java/cn/jojo/cc/common/utils/EnglishWords.java:31 public static boolean isWordChar",
      "正文含cat2dog、3look up4、1972、low-temperature、it's。",
      "数字作为分隔，cat/dog/look/up独立；纯数字忽略；hyphen和撇号保留合法词内连接。",
      "verification/red-words.txt -> green-words.txt（实际编译完整生产EnglishWords）。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "词边界、短语首尾、补录输入",
      "共享EnglishWords同步改动。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-010",
      "01 / P2正式问题",
      "CSV未闭合引号仍吞后续短语，且修文件重导不能纠正已写错误译文",
      "readRecords不检查EOF的inQuotes，直接addRecord；readPhrases将后续物理行作为允许换行的translation，rows有效且invalid为空。importOnePhrase正常创建后，重导因主词已存在直接skip。",
      "CSV在EOF仍处于引号内时整文件拒绝，并报记录起始物理行；只允许字段开头开引号，闭引号后只允许逗号/换行/EOF。有效引号换行/双引号转义保留；记录行号改为起始物理行。文件在解析完成后才进入写入。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java:324 private List<Record> readRecords",
      "CSV译文开启引号未闭合，后面跟合法记录；对照引号内合法换行和双引号转义。",
      "EOF拒绝畸形文件，不能吞后行后正常写库；合法CSV解析不回归。",
      "verification/red-import.txt -> green-import.txt；完整Reader、Normalizer实际隔离javac。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "文件解析、整批导入、重导",
      "无新增基础设施。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-011",
      "02 / P2正式问题",
      "非法变形不应部分入库，避免幂等跳过堵死修文件重导",
      "非法项只continue内层循环；主短语和got up仍进入rows并写库。修成got up|gotten up重导时，importOnePhrase先getByNormalized命中已建主词而直接false。",
      "一个非空variant非法就break且整行不进入rows；撤销该无效行的文件内firstSeen占用，后续同key修正行仍能导入。合法去重及与原形同串的变体丢弃保持。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java:163 boolean invalidVariant",
      "get up带got up|起床混合变体；修正为got up|gotten up后重导。",
      "非法变体使该短语整行零写；修好后可首次完整入库，避免残缺行占幂等键。",
      "verification/green-import.txt实际拒整行；新增JUnit覆盖同文件修正同key，未运行。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "变体原子接受、幂等重导",
      "无新增基础设施。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-012",
      "03 / P2正式问题",
      "导入显式非法limit仍退化为全量真写",
      "Param.parse直接getInteger，无正数校验；execute只在limit>0截断，dryRun默认false。显式无效小批参数变成整文件执行。",
      "下载前校验显式limit必须为JSON整数类型，严格>0且<=Integer.MAX_VALUE；0、负数、小数、数字字符串、null、溢出拒绝。只有省略limit表示全文件。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictImportBizServiceImpl.java:353 Integer limit = null",
      "import开关开启，显式limit=0/-1/null/小数/溢出/字符串，再测省略及合法正数。",
      "非法值在下载/百度/写库前拒绝；省略才表示全量；正数只处理前N合法候选。",
      "Param原嵌套类隔离javac，green-import.txt；JUnit另列小数/溢出/null/string未运行。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "导入参数、真写范围、dryRun",
      "保持原XxlJob入口。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-013",
      "06 / P2正式问题",
      "缓存失效异常被吞成发布成功，下一次批量也不再扫描该词",
      "PROD写成后先把JY status置PUBLISHED；缓存失效异常仅catch日志，无补偿身份。批量按status=UNPUBLISHED扫描，因此下轮不再拿到该行。clearRedisCommonCache内部异步丢投也不一定抛回。",
      "发布PROD主从数据与失效事件同一PROD事务提交，JY改为PUBLISHED不会丢补偿身份。Job按retry_after扫描JY和PROD两库，使用真实sendN同步等broker确认；失败留待办并退避，短语版本推进用可抛错可靠入口。delivered_time保留确认记录。内部PublishResultBo保留pending，BatchPublishResultBo增加pendingCacheCount（包含于PROD successCount），Job显式记录待失效数；HTTP Boolean保持既有契约，仅表示PROD提交及失效待办登记，不表示跨实例消费完成。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictPublishProServiceHelper.java:141 englishDictOutboxCoreService.enqueue(\"WORD_CACHE\"\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictOutboxBizService.java:28 public int replay\ncode/api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishDictOutboxJobHandler.java:15 @XxlJob",
      "PROD提交后让broker/Redis失效失败，或退出进程；恢复后跑outbox Job，模拟重复投递。",
      "待办与业务事务同提交；失败不丢，确认后记回执；重放幂等。Boolean只确认PROD提交，不冒称消费可见。",
      "完整DDL/DO/DAO/Core/Biz/Job/真实调用已接线，未部署DDL/注册Job/运行broker。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "PROD事务、MQ、缓存、重放",
      "两库outbox DDL；englishDictOutboxReplay持续注册。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-014",
      "10 / P2正式问题",
      "补录重复消费和重试仍先查百度，再幂等复用已有词",
      "autoFillOne首步queryBaiduSafely，随后文本锁内saveWordForAutoFill才查已有（含删除行）。TTS已移到异步回填，不再逐次生成或上传。",
      "文本锁内先查活词，再查含删除行；存在则直接复用，不查百度。仅确实不存在才查询百度并保存。文本锁释放后进入实体锁内验证自动补录、从未发布且auto_publish_eligible=1，才发布。新建补录置1，任何人工saveWord清0；不再用createTime==updateTime推断未编辑。新列在JY/PROD部署，存量默认0且禁止用旧审计时间回填资格。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/SegmentAutoFillBizServiceImpl.java:143 private void autoFillOne\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java:132 public PublishResultBo publishAutoFillWord",
      "对已发布、人工草稿、导入草稿、已删同词重复消费；新词竞争另验。",
      "先幂等预读，已有词不再调百度；锁内再查封并发；人工接管词不自动发布。",
      "verification/red-eligibility.txt -> green-eligibility.txt：同秒flag0/null旧代码误发布、修复后拒绝；原方法逐字提取，外围替身。百度先幂等路径与人工清flag静态核对；真实百度/MQ未跑。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "补录配额、幂等、人工发布闸门",
      "auto_publish_eligible迁移先行，历史默认0。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-015",
      "13 / P2正式问题",
      "复合词性验证归一化但落库仍写超长原串",
      "NotBlank无Size；isSupportedPos临时归一化n.并接受；buildTranslationWrites仍存item.getPos，超过VARCHAR96。",
      "翻译实际落库值改为PartOfSpeechEnum的规范串，与原校验使用同一函数；重复n.等输入不再以超长原串写VARCHAR96。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictWriteTxService.java:533 target.setPos(PartOfSpeechEnum.normalizeKnownCodes",
      "pos为n./重复40次；再用全部20种合法词性与未知词性。",
      "合法串规范化后实际落库，规范结果不超96；未知值拒绝；不能只校验后写原串。",
      "静态确认写入点已用规范值；未执行真实MySQL严格模式。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "保存、词性回显、列宽",
      "不扩VARCHAR96，不改枚举业务口径。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-016",
      "14 / P2正式问题",
      "管理搜索未按保存规则压缩连续空白",
      "保存归一化单空格，搜索只trim，再按normalized列eq。",
      "单词与短语管理精确搜索都使用保存时相同TextNormalizer：小写、trim、连续空白压单空格。",
      "code/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPhraseBizServiceImpl.java:129 TextNormalizer.normalize(bo.getPhrase()), bo.getStatus()\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishWordBizServiceImpl.java:116 TextNormalizer.normalize(bo.getWord()), bo.getStatus()",
      "保存take off，搜索 take  OFF 等连续内部空白；再测空条件、部分take与原词。",
      "搜索与保存共用normalize，归一化后精确匹配；不扩大为模糊或变体搜索。",
      "TextNormalizer复用和两个调用点静态核对；未运行管理API。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "单词/短语分页搜索、查重",
      "无新增基础设施。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-017",
      "23 / P2正式问题",
      "新批量发布Job接受limit=0和空参数段，意外发布全部待发布词",
      "isNumeric接受0，parse返回limit0，nextBatchSize把<=0解释为无限；只含逗号的raw.split结果无有效键，也返回type=all/limit0。本类注释明确配错抛错，但未区分“省略limit”与“显式0”。",
      "纯数字/键值limit都严格正整数，非空参数必须至少解析一个有效键值；显式0和纯逗号参数在任何publish调用前失败。空参数及未给limit仍按原规则全量。",
      "code/api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishBatchPublishTask.java:190 private static int positiveLimit",
      "批量发布250词，传limit=0/纯0/仅逗号/负数/非法type；对照150词和2短语。",
      "所有非法显式参数零发布；省略才全量；合法150/2严格限量。",
      "verification/red-batch.txt -> green-batch.txt；完整Task实际隔离javac，非法参数零写，150/2限量正确。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "批量发布参数、跨库真写",
      "保留原Job与HTTP契约。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-018",
      "COND-IPA / 条件边界",
      "COND-IPA",
      "见原分析条件风险；不把策略歧义冒充已发生故障。",
      "识别Unicode IPA字符块及有边界的/ASCII音标/；跳过括号中可识别IPA，不把and/or中的斜杠当音标起点；[ordinary words]保留。无法可靠区分无标记纯ASCII音标，明确保留。 DefaultSegmentTextExtractor保留原IPA token交统一扫描器处理，避免先剔IPA字母留下ASCII碎片；旧fixture已相应迁移。",
      "code/common/src/main/java/cn/jojo/cc/common/utils/EnglishWords.java:195 private static int phoneticSpanEnd",
      "正文含ˈfəʊnəm、and/or、普通[cat]，另构造有明确标记的纯ASCII音标。",
      "可识别音标整token排除，无f/n/m残片；and/or及普通正文保留；无标记纯ASCII歧义明确保留边界。",
      "verification/green-words.txt覆盖/kæt/、[dɒɡ]、/kat/、and/or与普通方括号；未标记纯ASCII仍是条件边界。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "富文本抽取、词边界、补录",
      "输入若为纯ASCII音标需明确语义标记。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-019",
      "COND-INDEX / 条件边界",
      "COND-INDEX",
      "见原分析条件风险；不把策略歧义冒充已发生故障。",
      "保留read readySnapshot的旧索引降级；持久化分词专用freshSnapshotForWrite强制版本比对，再确认句柄version等于实际远端，Redis不可读/重装失败/unknown版本返回null跳过落库及补录。",
      "code/application/src/main/java/cn/jojo/cc/biz/cache/english/EnglishPhraseIndexCacheService.java:190 public PhraseIndexSnapshot freshSnapshotForWrite\ncode/application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentBizServiceImpl.java:269 freshSnapshotForWrite(segmentSource)",
      "远端短语版本变更，重装DB失败/Redis版本不可读；对照查询读与落库写。",
      "读可降级旧表；写侧无法证明新鲜度则不持久化旧块；恢复后重新计算。",
      "读写入口分离和校验静态核对；Redis失败与多实例重装未实跑。",
      "JUnit/真实MySQL、Redis、MQ、TTS/OSS、RPC及C端联调未完成；具体探针只证明列明分支。",
      "索引、保存/发布/全量写入口",
      "版本检查配置与重装退避；真实故障注入待验。",
      "已给完整代码；业务验收待验证"
    ],
    [
      "CHG-020",
      "REV-R1 / 复审补充回归",
      "题表锁字段契约",
      "旧题表错用delete_time",
      "使用cc_new_question.del_flag，保持新分词表delete_time。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "题表锁字段契约：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "使用cc_new_question.del_flag，保持新分词表delete_time。",
      "verification/green-sql-contract.txt（文件已存在）",
      "SQLite仅验证列/筛选契约，不证明MySQL FOR UPDATE锁。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ],
    [
      "CHG-021",
      "REV-R2 / 复审补充回归",
      "派生失败不回滚题目同步",
      "同步事务内派生写失败污染rollback-only",
      "hook事务内只发事件；提交后独立失信，失败仍投计算；响应来源门保护读侧。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "派生失败不回滚题目同步：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "hook事务内只发事件；提交后独立失信，失败仍投计算；响应来源门保护读侧。",
      "verification/green-segment-wave.txt（文件已存在）",
      "题目同步提交、派生表不可用、消费者暂停需业务联调。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ],
    [
      "CHG-022",
      "REV-R3 / 复审补充回归",
      "失败登记在原事务退出后提交",
      "FAILED写后throw导致一起回滚",
      "计算Tx回滚释放题锁后，新Tx重锁核revision/指纹再登记FAILED。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "失败登记在原事务退出后提交：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "计算Tx回滚释放题锁后，新Tx重锁核revision/指纹再登记FAILED。",
      "verification/green-segment-wave.txt（文件已存在）",
      "真实Spring+内存事务manager不等于真实JTA/MySQL并发。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ],
    [
      "CHG-023",
      "REV-R5 / 复审补充回归",
      "同秒人工接管保护",
      "createTime==updateTime无法证明未编辑",
      "显式auto_publish_eligible：历史0、新补录1、人工保存清0；锁内读资格。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "同秒人工接管保护：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "显式auto_publish_eligible：历史0、新补录1、人工保存清0；锁内读资格。",
      "verification/green-eligibility.txt（文件已存在）",
      "两库新列与历史迁移；完整人工保存链需IDE和业务验证。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ],
    [
      "CHG-024",
      "SOURCE-01 / 复审补充回归",
      "响应选择后发生更新",
      "仅核新DB正文仍会给旧响应配新序列",
      "生成来源、输出scalar hash、本次选定URL/来源和分词来源绑定；v1/v2不同即省略。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "响应选择后发生更新：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "生成来源、输出scalar hash、本次选定URL/来源和分词来源绑定；v1/v2不同即省略。",
      "verification/green-response-binding.txt（文件已存在）",
      "真实输出生成/资源跨库/缓存失效链尚未联调。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ],
    [
      "CHG-025",
      "SOURCE-02 / 复审补充回归",
      "缓存JSON往返保留身份",
      "nested null丢失可能使同一正常输出hash失配",
      "从同一OutputData生成scalar输出hash并缓存，capture只复制scalar。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "缓存JSON往返保留身份：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "从同一OutputData生成scalar输出hash并缓存，capture只复制scalar。",
      "verification/green-cache-roundtrip.txt（文件已存在）",
      "旧无scalar缓存安全失信；须真实生成和驱逐恢复，不贴新DBhash。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ],
    [
      "CHG-026",
      "SOURCE-03 / 复审补充回归",
      "持续恢复与急停",
      "把一次迁移误当未来发布的永久恢复者",
      "恢复Job持续调度；每库50滚动扫描；执行前和每题重生成前检查总开关/急停。",
      "见FIX-LOGIC.md、REVIEW-FINAL.md及source-manifest.json",
      "持续恢复与急停：按对应验证脚本/VERIFICATION所列前置执行，并覆盖正常对照。",
      "恢复Job持续调度；每库50滚动扫描；执行前和每题重生成前检查总开关/急停。",
      "VERIFICATION.md（文件已存在）",
      "正常扫描周期按ceil(N/50)+1个成功调度轮估算；锁跳过/暂停/失败不保证固定墙钟上界。",
      "来源一致性、事务隔离、旧数据滚动升级",
      "见VERIFICATION.md；持续注册englishSegmentOutputIdentityRecover、englishDictOutboxReplay。",
      "代码与复审结论见最终记录；不等于业务通过"
    ]
  ]
}