{
  "head": "0baa9ebe8b7f9ecc46f675acbf1f56ac65378957",
  "master": "873c4f2d7c63a947216ae92bdc393b73408b6b6e",
  "formal": [
    {
      "id": "AUDIO-N01",
      "p": 1,
      "title": "音频补发布把曾发布对象的人工待发布草稿一并发布",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java",
      "start": 168,
      "end": 176,
      "trigger": "导入/补录无音频词已发布且有在途TTS；运营上传人工音频、改翻译并保存，未点发布；TTS随后completed。",
      "mechanism": "编辑保留hasPublished=1并置status=0；completed发现人工音频仍返回true；queryInFlight只检查hasPublished而非当前草稿状态，调用完整publishWord/Phrase，锁内重读当前主表、翻译、全部变体发布。",
      "impact": "未审核翻译、改名或变体配置进入PROD，待发布状态被系统改为已发布，违反Spec6只改不发布正式库完全不变。",
      "counter_evidence": "人工音频不会被TTS覆盖；从未发布hasPublished=0确实不自动发布。Spec17自身写hasPublished=1补发布，与草稿隔离冲突，需设计裁决而不能说仅实现漏判。",
      "kind": "相对e9f2fb1b新增异步链路",
      "evidence_status": "当前源码确定机制，未跑业务环境",
      "validation": "当前原方法逐字抽取隔离javac探针已执行，见audio-probe/results.txt；外围服务/DAO替身，未连业务环境",
      "evidence": "audio-probe/results.txt"
    },
    {
      "id": "AUDIO-N02",
      "p": 2,
      "title": "completed下载永久失败与查询异常均绕过超时收口",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java",
      "start": 248,
      "end": 292,
      "trigger": "状态持续completed，但下载404/过期/OSS保存失败；或状态查询持续报错；提交时间超过1800秒。",
      "mechanism": "elapsed超时只位于null/running分支；query异常直接return，completed保存null也return并保留任务。每轮ORDER BY taskTime,id LIMIT batch重复拿最老行。",
      "impact": "该词永久无音频且不再补提交；当坏任务达到batch还会饿死后续在途任务。",
      "counter_evidence": "null/running超时确实cancel并清理，普通一过性下载失败下轮能恢复；问题限定持续失败。",
      "kind": "相对e9f2fb1b新增异步链路",
      "evidence_status": "当前源码确定机制，未跑业务环境",
      "validation": "当前原方法逐字抽取隔离javac探针已执行，见audio-probe/results.txt；外围服务/DAO替身，未连业务环境",
      "evidence": "audio-probe/results.txt"
    },
    {
      "id": "AUDIO-N03",
      "p": 2,
      "title": "失败上限只统计提交抛错，平台终态失败与超时会无限重新提交",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java",
      "start": 151,
      "end": 157,
      "trigger": "平台submit一直成功，但每次合成都failed/cancelled或running超时。",
      "mechanism": "submit成功立即clearFailCount；终态失败和超时只清任务号、不增加失败计数；下轮pending重新提交；永远达不到maxRetry=3。",
      "impact": "同一无效词反复消耗合成配额，违反Spec17失败3次停止和配置同一行最多提交次数。",
      "counter_evidence": "提交API连续抛错确实计数并停手7天；这不是所有失败均无上限。",
      "kind": "相对e9f2fb1b新增异步链路",
      "evidence_status": "当前源码确定机制，未跑业务环境",
      "validation": "静态顺向和反向调用核验；未执行Maven、未访问业务DB/TTS",
      "evidence": "audio-analysis.md"
    },
    {
      "id": "AUDIO-N04",
      "p": 2,
      "title": "达到提交失败上限的最早ID持续占满候选批次，后续词无法补音频",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/impl/EnglishAudioBackfillBizServiceImpl.java",
      "start": 143,
      "end": 146,
      "trigger": "最小ID的batch条IMPORT/AUTOFILL词提交连续失败达到上限，随后TTS恢复且还有更大ID待生成词。",
      "mechanism": "SQL先按ID升序LIMIT batch；maxRetry仅业务循环内判断后return，未推进游标/排除冷却行；7天计数TTL内每轮仍选择同一批。",
      "impact": "后续健康词最多7天也无合成任务；批次条数配置变小放大此影响。",
      "counter_evidence": "不是数据库全量饥饿，限某表前batch被失败上限占满；7天过期或人工补齐能释放。",
      "kind": "相对e9f2fb1b新增异步链路",
      "evidence_status": "当前源码确定机制，未跑业务环境",
      "validation": "静态顺向和反向调用核验；未执行Maven、未访问业务DB/TTS",
      "evidence": "audio-analysis.md"
    },
    {
      "id": "AUDIO-N05",
      "p": 2,
      "title": "异步回填只更新音频列，完整性及教研缓存保持旧值",
      "file": "infrastructure/repository/src/main/java/cn/jojo/cc/core/service/impl/english/EnglishWordCoreServiceImpl.java",
      "start": 301,
      "end": 316,
      "trigger": "导入词已有音标和翻译，唯一缺音频completeness=0；TTS成功回填。",
      "mechanism": "markAudioCompleted只set音频和清任务两列，不重算completeness；回填服务不调用教学缓存失效；补发布复制JY完整性0到PROD而不重算。",
      "impact": "即使最终音频齐全，管理筛选仍有缺失、RPC complete=false；已缓存TEACHING详情仍可无音频直到常规过期。",
      "counter_evidence": "人工保存走mergeForCompleteness会重算；缺音标或翻译词仍应不完整，不能用它们验证本问题。",
      "kind": "相对e9f2fb1b新增异步链路",
      "evidence_status": "当前源码确定机制，未跑业务环境",
      "validation": "静态顺向和反向调用核验；未执行Maven、未访问业务DB/TTS",
      "evidence": "audio-analysis.md"
    },
    {
      "id": "SEG01",
      "p": 2,
      "title": "首次发布早于异步分词完成时，正式库缺行不能由全量自行恢复",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/impl/question/library/QuestionBizServiceImpl.java",
      "start": 2099,
      "end": 2102,
      "trigger": "首次同步后立即发布；worker尚未完成或首次计算失败，JY无NORMAL行",
      "mechanism": "buildPublishableSegments空/非成功即返回空，ensureProdSegmentSkeletons直接return；发布MQ随后refreshQuestion见PROD无行直接false；每日force也共用该缺行门。JY次日成功不自动再次发布。",
      "impact": "学生库永久segments=null直到人工再次发布；不是承诺的次日全量收敛",
      "counter_evidence": "JY正常行已就绪后发布可建INIT骨架并计算成功；再次发布可恢复",
      "kind": "新异步窗口与骨架策略扩大可达；历史发布缺行亦无全量补建",
      "validation_boundary": "静态完整闭环，未实跑MQ/DB",
      "evidence": "segment-analysis.md"
    },
    {
      "id": "SEG02",
      "p": 2,
      "title": "正文更新后到异步执行前旧NORMAL序列仍可下发",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentSaveHookService.java",
      "start": 85,
      "end": 94,
      "trigger": "已有成功分词；LABS同步正文增加/删除英文；暂停计算消费者或投递失败",
      "mechanism": "AFTER_COMMIT只发ID消息，提交时未把旧行置INIT/FAILED；delivery只验NORMAL而不比题目正文/revision。worker尚未到引擎时，旧成功行被视为可信。正式库重新发布也保留已有骨架行，不降级。",
      "impact": "新正文搭配旧序列，按顺序定位可漏高亮/错位置；消息丢失持续至下次实际重算",
      "counter_evidence": "worker成功会覆盖，已进入引擎失败会markFailed；不能把正常秒级受理当同步完成",
      "kind": "同步计算改MQ后新增明显窗口；发布保留现存行同类窗口",
      "validation_boundary": "静态控制流证明；没有声称真实客户端已错误高亮",
      "evidence": "segment-analysis.md"
    },
    {
      "id": "SEG03",
      "p": 2,
      "title": "迟到旧任务删除新revision另一内容位成功行",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/segment/QuestionSegmentBizServiceImpl.java",
      "start": 406,
      "end": 415,
      "trigger": "历史兜底MATERIAL→新表单ANSWER_AREA；旧刷新读旧正文后暂停，新同步rev2成功，再恢复旧rev1",
      "mechanism": "upsert/isStale仅保护同(question_id,content_type)；旧type1写入可成功；dropObsoleteContentTypes重读当前type2后按ID软删，无revision条件",
      "impact": "新type2成功结果被旧type1替代；下发拿到旧内容序列",
      "counter_evidence": "稳定题型固定同内容位时旧revision SQL门有效；需内容位集合变化，不是所有乱序均触发",
      "kind": "旧21保留，当前代码未修",
      "validation_boundary": "静态SQL与方法完整核对；未本轮DB并发实跑",
      "evidence": "segment-analysis.md"
    },
    {
      "id": "SEG04",
      "p": 2,
      "title": "数字夹英文仍作为整词查询",
      "file": "common/src/main/java/cn/jojo/cc/common/utils/EnglishWords.java",
      "start": 31,
      "end": 32,
      "trigger": "cat2dog、3look up4",
      "mechanism": "isWordChar包含数字，nextToken连续吞入，isWordToken只要求至少一个英文字母",
      "impact": "与Spec7.3数字分隔冲突，漏cat/dog且可能补录cat2dog等混合词",
      "counter_evidence": "纯1972过滤正确，low-temperature整词正确",
      "kind": "旧15保留",
      "validation_boundary": "本轮原EnglishWords+TextNormalizer隔离javac通过并执行，cat2dog/3look/up4输出已确认",
      "evidence": "segment-analysis.md"
    },
    {
      "id": "01",
      "p": 2,
      "title": "CSV未闭合引号仍吞后续短语，且修文件重导不能纠正已写错误译文",
      "scope": "05 / 07 / 08 import-publish",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java",
      "start": 363,
      "end": 365,
      "trigger": "短语文件 translation 开引号后直到EOF未闭合，后面还有正常记录。",
      "mechanism": "readRecords不检查EOF的inQuotes，直接addRecord；readPhrases将后续物理行作为允许换行的translation，rows有效且invalid为空。importOnePhrase正常创建后，重导因主词已存在直接skip。",
      "impact": "look up译文变成“查找\\nget up,起床”，get up整条丢失；修文件重导无法恢复look up已落的错误译文。",
      "fix": "EOF拒绝未闭合引号并记录起始物理行；同时约束引号只在字段边界出现。",
      "validation": "本轮原完整Reader/Normalizer与原Param javac隔离执行，见 evidence/import/results.txt",
      "evidence": "evidence/import/results.txt",
      "kind": "旧问题仍存（当前源码重新核实）",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "previous_id": "01",
      "reviewer_id": "IP01"
    },
    {
      "id": "02",
      "p": 2,
      "title": "非法变形不应部分入库，避免幂等跳过堵死修文件重导",
      "scope": "05 / 07 / 08 import-publish",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishImportFileReader.java",
      "start": 171,
      "end": 173,
      "trigger": "同一短语variants有合法及非法文本：get up / got up|起床。",
      "mechanism": "非法项只continue内层循环；主短语和got up仍进入rows并写库。修成got up|gotten up重导时，importOnePhrase先getByNormalized命中已建主词而直接false。",
      "impact": "报告“变形非法”同时建成残缺语义；修文件重导仍无法加入gotten up，后续题面该变形漏短语命中。",
      "fix": "任一非空variant非法时整条短语不进入有效rows，保留行错误待修文件重导。",
      "validation": "本轮原完整Reader/Normalizer与原Param javac隔离执行，见 evidence/import/results.txt",
      "evidence": "evidence/import/results.txt",
      "kind": "旧问题仍存（当前源码重新核实）",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "previous_id": "02",
      "reviewer_id": "IP02"
    },
    {
      "id": "03",
      "p": 2,
      "title": "导入显式非法limit仍退化为全量真写",
      "scope": "05 / 07 / 08 import-publish",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictImportBizServiceImpl.java",
      "start": 353,
      "end": 355,
      "trigger": "english.import.enabled已开，JSON显式limit=0或-1，省略dryRun。",
      "mechanism": "Param.parse直接getInteger，无正数校验；execute只在limit>0截断，dryRun默认false。显式无效小批参数变成整文件执行。",
      "impact": "整文件调用百度并建待发布词条，音频由后续回填Job统一处理；显式无效小批参数扩大到全量写入。",
      "fix": "显式提供limit时在下载前拒绝<=0以及越界/非整数字段；只允许省略表达全量。",
      "validation": "本轮原完整Reader/Normalizer与原Param javac隔离执行，见 evidence/import/results.txt",
      "evidence": "evidence/import/results.txt",
      "kind": "旧问题仍存（当前源码重新核实）",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "previous_id": "03",
      "reviewer_id": "IP03"
    },
    {
      "id": "06",
      "p": 2,
      "title": "缓存失效异常被吞成发布成功，下一次批量也不再扫描该词",
      "scope": "05 / 07 / 08 import-publish",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPublishBizServiceImpl.java",
      "start": 257,
      "end": 259,
      "trigger": "PROD提交且JY已标PUBLISHED后，clearRedisCommonCache同步抛错；例如有界队列拒绝后fallback MQ发送失败。",
      "mechanism": "PROD写成后先把JY status置PUBLISHED；缓存失效异常仅catch日志，无补偿身份。批量按status=UNPUBLISHED扫描，因此下轮不再拿到该行。clearRedisCommonCache内部异步丢投也不一定抛回。",
      "impact": "PROD已新而缓存仍旧，直到实际TTL/手工重发/另一次有效失效；批量报告成功无法证明学生已看到新内容。",
      "fix": "隔离后续失效动作，失败返回诚实的部分成功/重试身份，并保证可重放；不能声称重跑pending会自动覆盖status1。",
      "validation": "当前源码静态核验",
      "evidence": "import-analysis.md",
      "kind": "旧问题仍存（当前源码重新核实）",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "previous_id": "06",
      "reviewer_id": "IP05"
    },
    {
      "id": "10",
      "p": 2,
      "title": "补录重复消费和重试仍先查百度，再幂等复用已有词",
      "scope": "05 / 07 / 08 import-publish",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/SegmentAutoFillBizServiceImpl.java",
      "start": 142,
      "end": 144,
      "trigger": "同词多题的延迟MQ或消费重放；库中已经有已发布词、人工草稿、导入草稿或已删词。",
      "mechanism": "autoFillOne首步queryBaiduSafely，随后文本锁内saveWordForAutoFill才查已有（含删除行）。TTS已移到异步回填，不再逐次生成或上传。",
      "impact": "重复投递、发布失败重试、已有人工草稿或删除词都仍消耗百度配额；不会重复建词；本轮不再声称重复TTS/OSS。",
      "fix": "先按词条状态判补录阶段并锁内重检；旧补录待发布只重试发布，不重新做外部生成。",
      "validation": "当前源码静态核验",
      "evidence": "import-analysis.md",
      "kind": "旧问题仍存（当前源码重新核实）",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "previous_id": "10",
      "reviewer_id": "IP06"
    },
    {
      "id": "13",
      "p": 2,
      "title": "复合词性验证归一化但落库仍写超长原串",
      "scope": "词典维护/发布/补录/批量任务/缓存",
      "kind": "旧问题仍存（当前源码重新核实）",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishDictWriteTxService.java",
      "start": 531,
      "end": 533,
      "trigger": "HTTP pos传重复有效词性组成122字符原串",
      "mechanism": "NotBlank无Size；isSupportedPos临时归一化n.并接受；buildTranslationWrites仍存item.getPos，超过VARCHAR96。",
      "impact": "严格MySQL模式DataTooLong导致整个词条保存失败；宽松模式可能截断。",
      "fix": "见相应专项报告中的修复边界与拷打问题。",
      "validation": "当前源码静态核验",
      "evidence": "import-analysis.md",
      "previous_id": "13",
      "reviewer_id": "EXISTING-POS",
      "extraEvidence": [],
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。"
    },
    {
      "id": "14",
      "p": 2,
      "title": "管理搜索未按保存规则压缩连续空白",
      "scope": "05 后台单词/短语精确搜索",
      "kind": "旧问题仍存（当前源码重新核实）",
      "file": "application/src/main/java/cn/jojo/cc/biz/service/english/dict/EnglishPhraseBizServiceImpl.java",
      "start": 129,
      "end": 131,
      "trigger": "已存take off，搜索take  off",
      "mechanism": "保存归一化单空格，搜索只trim，再按normalized列eq。",
      "impact": "允许的归一化精确搜索无法命中。大小写是否出错取决于DB collation，不用其做确定反例。",
      "fix": "见相应专项报告中的修复边界与拷打问题。",
      "validation": "当前源码静态核验",
      "evidence": "import-analysis.md",
      "reviewer_id": "EXISTING-SEARCH",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "previous_id": "14"
    },
    {
      "id": "23",
      "p": 2,
      "title": "新批量发布Job接受limit=0和空参数段，意外发布全部待发布词",
      "scope": "05 / 07 / 08 import-publish",
      "file": "api/admin-worker/src/main/java/cn/jojo/cc/worker/task/EnglishBatchPublishTask.java",
      "start": 148,
      "end": 150,
      "trigger": "控制台显式填limit=0（或纯0）；或误填只含逗号的非空参数。",
      "mechanism": "isNumeric接受0，parse返回limit0，nextBatchSize把<=0解释为无限；只含逗号的raw.split结果无有效键，也返回type=all/limit0。本类注释明确配错抛错，但未区分“省略limit”与“显式0”。",
      "impact": "试跑配置错误会直接跨库发布所有待发布内容，可能把未完成审核的人工草稿送至学生端；没有dryRun与任务撤回可兜。",
      "fix": "只有空参数或省略limit表达全量；显式limit校验>0；非空参数必须至少解析一个有效键值，不接受分隔符空集。",
      "validation": "本轮完整原Task javac，内存发布服务替身，显式0/逗号全量，150/2正常限量；见 evidence/import/results-batch.txt",
      "evidence": "evidence/import/results-batch.txt",
      "kind": "旧问题仍存（当前源码重新核实）",
      "counter_evidence": "无真实DB/MQ/外部调用；除明确标为本轮javac的项目，均为当前源码链路核对，不复用旧实跑作为当前实跑。",
      "reviewer_id": "IP08",
      "previous_id": "23"
    }
  ],
  "conditional": [
    "IPA处理范围的需求/实现策略缺口",
    "force重装失败仍用旧快照：fail-open与落库新鲜度承诺冲突"
  ],
  "note": "非旧22项全量复跑；实际证据边界见每专项"
}