From 7f67dd1b9a452dd0a72c15ecd7d15611e428ce83 Mon Sep 17 00:00:00 2001 From: damingishere-coder Date: Tue, 1 Sep 2026 08:39:31 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E5=89=8D=E5=AF=BC=E9=9B=B6=E8=AF=AF=E5=88=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IMAGE_LEADING_ZERO_RECOVERY_TASK.md | 57 +++++++++++++++++++++++++++ app/image/fact_verification.py | 4 +- app/image/regeneration.py | 7 +++- tests/test_image_fact_verification.py | 13 ++++++ tests/test_v2_image_regeneration.py | 20 ++++++++++ 5 files changed, 99 insertions(+), 2 deletions(-) create mode 100644 IMAGE_LEADING_ZERO_RECOVERY_TASK.md diff --git a/IMAGE_LEADING_ZERO_RECOVERY_TASK.md b/IMAGE_LEADING_ZERO_RECOVERY_TASK.md new file mode 100644 index 0000000..0f86005 --- /dev/null +++ b/IMAGE_LEADING_ZERO_RECOVERY_TASK.md @@ -0,0 +1,57 @@ +# 图片前导零误判恢复任务 + +## 背景 + +2026-09-01 的群 26(米游涩泛二次元同好摸鱼群2.3)和群 27(米游涩泛二次元同好摸鱼群3.2)已生成图片,但严格 OCR 事实校验把版式或日期中的 `05`、`09`、`01` 当成未获证据支持的数字,最终进入图片失败状态。用户要求修复并补齐两张图片,但不得安排或触发微信图片发送。 + +## 目标 + +- 将带前导零的整数 OCR 结果按其数值归一化,使 `05` 与 `5`、`09` 与 `9`、`01` 与 `1` 等价。 +- 图片恢复成功时清除对应的图片内容校验失败字段。 +- 只恢复 2026-09-01 的群 26、27 图片,并保留 `send_hold=true` 与人工发送要求。 + +## 允许修改范围 + +- `app/image/fact_verification.py` +- `app/image/regeneration.py` +- `tests/test_image_fact_verification.py` +- `tests/test_v2_image_regeneration.py` +- 本任务说明文件 +- 2026-09-01 群 26、27 的运行状态与图片产物(在备份后通过既有恢复入口写入) + +## 禁止修改范围 + +- 不修改 Prompt、消息、排行榜或其他四个群的图片。 +- 不调用微信发送、发送排程、邮件发送或目标核验接口。 +- 不修改 Provider、登录、认证、`.env`、API Key 或远端权限。 +- 不重启生产服务,不改写 Git 历史,不自动合并 PR。 + +## 已确定实现要求 + +- 只去除整数部分的冗余前导零,不放宽未知数字、单位、百分比和禁止事实校验。 +- 恢复流程必须在图片进入 `READY_TO_SEND` 前写入并保留 `send_hold=true`、`needs_manual_send=true`。 +- 群 26 仅可认领其任务记录中唯一且 SHA-256 匹配的候选图。 +- 群 27 使用同一已保存 Prompt 重新生图;结果必须通过 PNG 解码、尺寸、哈希及严格事实校验。 + +## 验收标准 + +- 前导零回归测试通过,未知数字仍然失败关闭。 +- 图片内容校验失败经成功恢复后,`error`、`error_type`、`failed_stage` 被清除。 +- 两群 `daily_image.png` 均存在、可解码、接口返回 `image/png`,状态为 `READY_TO_SEND`。 +- 两群 `send_hold=true`、`needs_manual_send=true`、`sent_at` 为空,且没有微信发送尝试证据。 +- 调度快照与两个 `run.json` 的图片成功状态一致。 + +## 测试命令 + +```powershell +.\.venv\Scripts\python.exe -m pytest tests\test_image_fact_verification.py tests\test_v2_image_regeneration.py -q +.\.venv\Scripts\python.exe -m pytest tests -q +git diff --check +``` + +## 返回格式 + +- 根因与修复摘要 +- 两张图片的路径、尺寸、SHA-256、事实校验结果 +- `send_hold` 与零发送证据 +- Git 分支、提交、Push、PR、CI 状态与未合并说明 diff --git a/app/image/fact_verification.py b/app/image/fact_verification.py index 3156792..635ca23 100644 --- a/app/image/fact_verification.py +++ b/app/image/fact_verification.py @@ -89,7 +89,9 @@ def _canonical_number(value: str) -> str: normalized = normalized.replace("块", "元") if re.fullmatch(r"0+", normalized): return "0" - return normalized + # OCR 常保留日期、分镜或昵称中的整数前导零;它们不应把 + # 05/09 之类的版式数字与已允许的 5/9 判为两个不同事实。 + return re.sub(r"^0+(?=\d)", "", normalized) def _numeric_facts(value: str) -> set[str]: diff --git a/app/image/regeneration.py b/app/image/regeneration.py index f2f307b..14fa991 100644 --- a/app/image/regeneration.py +++ b/app/image/regeneration.py @@ -21,6 +21,7 @@ from app.image.fact_verification import strict_fact_verification_enabled from app.image.image_task import ImageTaskResult, verify_image_contract from app.v2.constants import ( + IMAGE_CONTENT_VERIFICATION_FAILED, IMAGE_FILE_MISSING, IMAGE_GENERATION_FAILED, READY_TO_SEND, @@ -244,7 +245,11 @@ def _promote_image( current = store.load_run(group_name, run_date) next_status = SENT if current.get("status") == SENT else READY_TO_SEND success_fields: dict[str, Any] = {} - if str(current.get("error_type") or "") in {IMAGE_GENERATION_FAILED, IMAGE_FILE_MISSING}: + if str(current.get("error_type") or "") in { + IMAGE_CONTENT_VERIFICATION_FAILED, + IMAGE_GENERATION_FAILED, + IMAGE_FILE_MISSING, + }: success_fields.update(error=None, error_type=None, failed_stage=None) store.update( group_name, diff --git a/tests/test_image_fact_verification.py b/tests/test_image_fact_verification.py index 5ab152b..cded814 100644 --- a/tests/test_image_fact_verification.py +++ b/tests/test_image_fact_verification.py @@ -94,6 +94,19 @@ def test_numeric_units_do_not_join_across_ocr_lines(tmp_path): assert review.unknown_numeric == () +def test_allows_zero_padded_layout_numbers(tmp_path): + prompt, image = _evidence(tmp_path) + + review = review_image_facts( + prompt, + image, + ocr_text="分镜 01\n成员 05\n日期 09\n体重 078.8万元", + ) + + assert review.ok + assert review.unknown_numeric == () + + def test_allows_deterministic_header_numbers_and_currency_alias(tmp_path): prompt = tmp_path / "image_prompt.txt" prompt.write_text( diff --git a/tests/test_v2_image_regeneration.py b/tests/test_v2_image_regeneration.py index 6ae5ed5..fb9ba18 100644 --- a/tests/test_v2_image_regeneration.py +++ b/tests/test_v2_image_regeneration.py @@ -94,6 +94,26 @@ def test_success_atomically_replaces_image_backs_up_old_and_holds_send(tmp_path) assert run["image_force_local_fallback"] is False +def test_success_clears_image_content_verification_failure(tmp_path): + settings, store, group, run_date = _run(tmp_path, status="FAILED") + store.update( + group, + run_date, + failed_stage="image", + error="图片事实校验失败:无证据数字:05", + error_type="IMAGE_CONTENT_VERIFICATION_FAILED", + ) + + run = run_regeneration_now(settings, group, run_date, SuccessGenerator()) + + assert run["status"] == "READY_TO_SEND" + assert run["error"] is None + assert run["error_type"] is None + assert run["failed_stage"] is None + assert run["send_hold"] is True + assert run["needs_manual_send"] is True + + def test_cli_failure_keeps_old_image_and_fails_closed(tmp_path): settings, store, group, run_date = _run(tmp_path) From 4e5cc234cdeade4821fc2f7c8356835461788f8f Mon Sep 17 00:00:00 2001 From: damingishere-coder Date: Fri, 4 Sep 2026 08:05:08 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E8=A1=A5=E5=85=A8=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=9B=BE=E6=97=A5=E6=9C=9F=E8=AF=81=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IMAGE_LEADING_ZERO_RECOVERY_TASK.md | 30 ++++++++----- app/image/fact_verification.py | 22 ++++++++++ tests/test_image_fact_verification.py | 63 +++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 11 deletions(-) diff --git a/IMAGE_LEADING_ZERO_RECOVERY_TASK.md b/IMAGE_LEADING_ZERO_RECOVERY_TASK.md index 0f86005..195170a 100644 --- a/IMAGE_LEADING_ZERO_RECOVERY_TASK.md +++ b/IMAGE_LEADING_ZERO_RECOVERY_TASK.md @@ -1,14 +1,15 @@ -# 图片前导零误判恢复任务 +# 图片日期数字误判永久修复与 2026-09-04 恢复任务 ## 背景 -2026-09-01 的群 26(米游涩泛二次元同好摸鱼群2.3)和群 27(米游涩泛二次元同好摸鱼群3.2)已生成图片,但严格 OCR 事实校验把版式或日期中的 `05`、`09`、`01` 当成未获证据支持的数字,最终进入图片失败状态。用户要求修复并补齐两张图片,但不得安排或触发微信图片发送。 +2026-09-01 的前导零修复仍停留在未合并的 PR #15,当前 `master` 与 Alter 托管的 8766 生产进程仍运行旧逻辑。2026-09-04 群 23(茶馆V3.0(三周年纪念)🐮🐴)再次失败:Level 3 本地信息图页眉包含运行日期 `2026-09-04`,严格 OCR 事实校验报“无证据数字:04”。简化 Prompt 没有携带本地信息图直接使用的 `run.json` 元数据,因此仅去除前导零仍不能覆盖 11 日以后等日期。用户要求修复本次失败并防止同类情况再次出现。 ## 目标 - 将带前导零的整数 OCR 结果按其数值归一化,使 `05` 与 `5`、`09` 与 `9`、`01` 与 `1` 等价。 +- 将 Level 3 渲染实际使用的可信 `run.json` 群名、运行日期、统计区间和计数加入事实证据,准确允许本次运行日期,同时继续拒绝其他日期和无依据数字。 - 图片恢复成功时清除对应的图片内容校验失败字段。 -- 只恢复 2026-09-01 的群 26、27 图片,并保留 `send_hold=true` 与人工发送要求。 +- 只恢复 2026-09-04 的群 23 图片,并保留 `send_hold=true` 与人工发送要求。 ## 允许修改范围 @@ -17,11 +18,11 @@ - `tests/test_image_fact_verification.py` - `tests/test_v2_image_regeneration.py` - 本任务说明文件 -- 2026-09-01 群 26、27 的运行状态与图片产物(在备份后通过既有恢复入口写入) +- 2026-09-04 群 23 的运行状态与图片产物(在备份后通过既有恢复入口写入) ## 禁止修改范围 -- 不修改 Prompt、消息、排行榜或其他四个群的图片。 +- 不修改 Prompt、消息、排行榜或其他五个群的图片。 - 不调用微信发送、发送排程、邮件发送或目标核验接口。 - 不修改 Provider、登录、认证、`.env`、API Key 或远端权限。 - 不重启生产服务,不改写 Git 历史,不自动合并 PR。 @@ -30,16 +31,17 @@ - 只去除整数部分的冗余前导零,不放宽未知数字、单位、百分比和禁止事实校验。 - 恢复流程必须在图片进入 `READY_TO_SEND` 前写入并保留 `send_hold=true`、`needs_manual_send=true`。 -- 群 26 仅可认领其任务记录中唯一且 SHA-256 匹配的候选图。 -- 群 27 使用同一已保存 Prompt 重新生图;结果必须通过 PNG 解码、尺寸、哈希及严格事实校验。 +- 群 23 仅使用已有 `run.json`、`ranking.json` 和已保存 Prompt 确定性重建 Level 3 本地信息图;禁止调用外部图片生成。 +- 恢复结果必须通过 PNG 解码、尺寸、哈希及严格事实校验,写入前先备份原运行目录。 ## 验收标准 - 前导零回归测试通过,未知数字仍然失败关闭。 - 图片内容校验失败经成功恢复后,`error`、`error_type`、`failed_stage` 被清除。 -- 两群 `daily_image.png` 均存在、可解码、接口返回 `image/png`,状态为 `READY_TO_SEND`。 -- 两群 `send_hold=true`、`needs_manual_send=true`、`sent_at` 为空,且没有微信发送尝试证据。 -- 调度快照与两个 `run.json` 的图片成功状态一致。 +- 真实 `2026.09.04` OCR 页眉通过,伪造的其他运行日期仍失败关闭。 +- 群 23 `daily_image.png` 存在、可解码、接口返回 `image/png`,状态为 `READY_TO_SEND`。 +- 群 23 `send_hold=true`、`needs_manual_send=true`、`sent_at` 为空,且没有微信发送尝试证据。 +- Dashboard 与 `run.json` 的图片成功/人工持有状态一致。 ## 测试命令 @@ -49,9 +51,15 @@ git diff --check ``` +## 运行恢复记录 + +- 首次计划的恢复前目录复制因 PowerShell `-LiteralPath` 不展开通配符而失败,且该错误当时未终止后续恢复;因此不存在可声明为成功的恢复前文件副本。 +- 恢复完成后已将 7 个产物逐文件 SHA-256 校验复制到 `output/.backups/image-date-fact-post-recovery-20260904-075918/`,用于保留当前验收态。 +- 原失败证据仍保留在 `run.json` 的 `attempt_ledger`;恢复未调用外部图片生成或微信发送。 + ## 返回格式 - 根因与修复摘要 -- 两张图片的路径、尺寸、SHA-256、事实校验结果 +- 恢复图片的路径、尺寸、SHA-256、事实校验结果 - `send_hold` 与零发送证据 - Git 分支、提交、Push、PR、CI 状态与未合并说明 diff --git a/app/image/fact_verification.py b/app/image/fact_verification.py index 635ca23..1860014 100644 --- a/app/image/fact_verification.py +++ b/app/image/fact_verification.py @@ -122,6 +122,28 @@ def _load_evidence(prompt_file: Path) -> tuple[list[str], str, int]: line.strip() for line in visible_prompt.splitlines() if line.strip() ] numeric_evidence_lines: list[str] = [] + # Level 3 本地信息图会直接渲染 run.json 中的群名、运行日期和人数, + # 而简化 Prompt 不一定重复这些字段。把渲染所依赖的确定性元数据加入 + # 证据,避免日期 04/11 等被误判,同时仍拒绝不属于本次运行的数字。 + run_path = prompt_file.with_name("run.json") + try: + run = json.loads(run_path.read_text(encoding="utf-8")) + except (OSError, UnicodeError, json.JSONDecodeError): + run = {} + if isinstance(run, dict): + trusted_run_fields = ( + "group_name", + "run_date", + "period_start", + "period_end", + "message_count", + "speaker_count", + ) + for field in trusted_run_fields: + value = str(run.get(field) or "").strip() + if value: + evidence_lines.append(value) + numeric_evidence_lines.append(value) for heading in ("群名称", "统计时间", "数据"): match = re.search( rf"【{heading}】\s*(.*?)(?=\n【|\Z)", diff --git a/tests/test_image_fact_verification.py b/tests/test_image_fact_verification.py index cded814..2241887 100644 --- a/tests/test_image_fact_verification.py +++ b/tests/test_image_fact_verification.py @@ -107,6 +107,69 @@ def test_allows_zero_padded_layout_numbers(tmp_path): assert review.unknown_numeric == () +def test_allows_local_fallback_header_from_trusted_run_metadata(tmp_path): + prompt = tmp_path / "image_prompt.txt" + prompt.write_text( + "【任务】\n外部内容整理失败,仅使用当天 ranking.json 生成本地简化信息图。", + encoding="utf-8", + ) + (tmp_path / "messages.json").write_text( + json.dumps([{"sender_name": "群友甲", "content": "测试消息"}], ensure_ascii=False), + encoding="utf-8", + ) + (tmp_path / "ranking.json").write_text( + json.dumps({"message_count": 161, "speaker_count": 20}), + encoding="utf-8", + ) + (tmp_path / "run.json").write_text( + json.dumps( + { + "group_name": "茶馆V3.0(三周年纪念)🐮🐴", + "run_date": "2026-09-04", + "message_count": 161, + "speaker_count": 20, + }, + ensure_ascii=False, + ), + encoding="utf-8", + ) + image = tmp_path / "daily_image.png" + Image.new("RGB", (16, 16), "white").save(image) + + review = review_image_facts( + prompt, + image, + ocr_text="茶馆V3.0(三周年纪念)\n2026.09.04 · 简化版数据卡片\n消息 161\n参与 20", + ) + + assert review.ok + assert review.unknown_numeric == () + + +def test_rejects_local_fallback_header_for_a_different_run_date(tmp_path): + prompt = tmp_path / "image_prompt.txt" + prompt.write_text("【任务】\n生成本地简化信息图。", encoding="utf-8") + (tmp_path / "messages.json").write_text( + json.dumps([{"sender_name": "群友甲", "content": "测试消息"}], ensure_ascii=False), + encoding="utf-8", + ) + (tmp_path / "run.json").write_text( + json.dumps({"run_date": "2026-09-04"}), + encoding="utf-8", + ) + image = tmp_path / "daily_image.png" + Image.new("RGB", (16, 16), "white").save(image) + + review = review_image_facts( + prompt, + image, + ocr_text="2026.09.11 · 简化版数据卡片", + ) + + assert not review.ok + assert "11" in review.unknown_numeric + + def test_allows_deterministic_header_numbers_and_currency_alias(tmp_path): prompt = tmp_path / "image_prompt.txt" prompt.write_text( From ac9a4351166625cb1d1ec317a741fd81d8f42930 Mon Sep 17 00:00:00 2001 From: damingishere-coder Date: Fri, 4 Sep 2026 08:31:18 +0800 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20=E8=AE=B0=E5=BD=95=E4=B8=A4?= =?UTF-8?q?=E7=BE=A4=E6=AD=A3=E5=B8=B8=E7=BE=A4=E6=8A=A5=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E9=AA=8C=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- IMAGE_LEADING_ZERO_RECOVERY_TASK.md | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/IMAGE_LEADING_ZERO_RECOVERY_TASK.md b/IMAGE_LEADING_ZERO_RECOVERY_TASK.md index 195170a..07e97e4 100644 --- a/IMAGE_LEADING_ZERO_RECOVERY_TASK.md +++ b/IMAGE_LEADING_ZERO_RECOVERY_TASK.md @@ -2,14 +2,15 @@ ## 背景 -2026-09-01 的前导零修复仍停留在未合并的 PR #15,当前 `master` 与 Alter 托管的 8766 生产进程仍运行旧逻辑。2026-09-04 群 23(茶馆V3.0(三周年纪念)🐮🐴)再次失败:Level 3 本地信息图页眉包含运行日期 `2026-09-04`,严格 OCR 事实校验报“无证据数字:04”。简化 Prompt 没有携带本地信息图直接使用的 `run.json` 元数据,因此仅去除前导零仍不能覆盖 11 日以后等日期。用户要求修复本次失败并防止同类情况再次出现。 +2026-09-01 的前导零修复仍停留在未合并的 PR #15,当前 `master` 与 Alter 托管的 8766 生产进程仍运行旧逻辑。2026-09-04 群 23(茶馆V3.0(三周年纪念)🐮🐴)再次失败:Level 3 本地信息图页眉包含运行日期 `2026-09-04`,严格 OCR 事实校验报“无证据数字:04”。简化 Prompt 没有携带本地信息图直接使用的 `run.json` 元数据,因此仅去除前导零仍不能覆盖 11 日以后等日期。随后确认群 27(米游涩泛二次元同好摸鱼群3.2)也把 Level 3 排行数据卡误当成成功图片并进入待发送。用户明确指出此类数据卡不是群报,因此本次恢复必须同时满足事实校验和正常分镜群报版式,不能再用 Level 3 卡片冒充成功。 ## 目标 - 将带前导零的整数 OCR 结果按其数值归一化,使 `05` 与 `5`、`09` 与 `9`、`01` 与 `1` 等价。 - 将 Level 3 渲染实际使用的可信 `run.json` 群名、运行日期、统计区间和计数加入事实证据,准确允许本次运行日期,同时继续拒绝其他日期和无依据数字。 - 图片恢复成功时清除对应的图片内容校验失败字段。 -- 只恢复 2026-09-04 的群 23 图片,并保留 `send_hold=true` 与人工发送要求。 +- 恢复 2026-09-04 的群 23 和群 27 正常分镜群报,并保留 `send_hold=true` 与人工发送要求。 +- 依靠独立的 PR #16 阻止 Level 3/Pillow 诊断图被标记为成功、推进到可发送状态或被发送层认领。 ## 允许修改范围 @@ -18,11 +19,11 @@ - `tests/test_image_fact_verification.py` - `tests/test_v2_image_regeneration.py` - 本任务说明文件 -- 2026-09-04 群 23 的运行状态与图片产物(在备份后通过既有恢复入口写入) +- 2026-09-04 群 23、群 27 的运行状态与图片产物(在备份后通过既有恢复入口写入) ## 禁止修改范围 -- 不修改 Prompt、消息、排行榜或其他五个群的图片。 +- 不修改消息、排行榜或其他四个群的图片。 - 不调用微信发送、发送排程、邮件发送或目标核验接口。 - 不修改 Provider、登录、认证、`.env`、API Key 或远端权限。 - 不重启生产服务,不改写 Git 历史,不自动合并 PR。 @@ -31,17 +32,19 @@ - 只去除整数部分的冗余前导零,不放宽未知数字、单位、百分比和禁止事实校验。 - 恢复流程必须在图片进入 `READY_TO_SEND` 前写入并保留 `send_hold=true`、`needs_manual_send=true`。 -- 群 23 仅使用已有 `run.json`、`ranking.json` 和已保存 Prompt 确定性重建 Level 3 本地信息图;禁止调用外部图片生成。 -- 恢复结果必须通过 PNG 解码、尺寸、哈希及严格事实校验,写入前先备份原运行目录。 +- Level 3/Pillow 排行或诊断数据卡不属于正常群报,即使事实校验通过也不得作为成功结果或进入发送流程。 +- 群 23 和群 27 仅使用各自已有 `run.json`、`ranking.json`、证据对话和历史成功群报作为依据,通过内置 ImageGen 生成正常分镜群报;不得虚构人名、数字、引语或外部事实。 +- 恢复结果必须通过 PNG 解码、尺寸、哈希及严格事实校验,写入前先备份原图,并通过既有恢复入口原子提升。 ## 验收标准 - 前导零回归测试通过,未知数字仍然失败关闭。 - 图片内容校验失败经成功恢复后,`error`、`error_type`、`failed_stage` 被清除。 - 真实 `2026.09.04` OCR 页眉通过,伪造的其他运行日期仍失败关闭。 -- 群 23 `daily_image.png` 存在、可解码、接口返回 `image/png`,状态为 `READY_TO_SEND`。 -- 群 23 `send_hold=true`、`needs_manual_send=true`、`sent_at` 为空,且没有微信发送尝试证据。 +- 两群 `daily_image.png` 均为正常分镜群报,存在、可解码、接口返回 `image/png`,状态为 `READY_TO_SEND`。 +- 两群 `send_hold=true`、`needs_manual_send=true`、所有发送时间戳为空,且没有微信发送尝试证据。 - Dashboard 与 `run.json` 的图片成功/人工持有状态一致。 +- PR #16 的诊断回退图发送防线通过 CI,但在用户确认合并并部署前不得宣称生产永久生效。 ## 测试命令 @@ -55,7 +58,10 @@ git diff --check - 首次计划的恢复前目录复制因 PowerShell `-LiteralPath` 不展开通配符而失败,且该错误当时未终止后续恢复;因此不存在可声明为成功的恢复前文件副本。 - 恢复完成后已将 7 个产物逐文件 SHA-256 校验复制到 `output/.backups/image-date-fact-post-recovery-20260904-075918/`,用于保留当前验收态。 -- 原失败证据仍保留在 `run.json` 的 `attempt_ledger`;恢复未调用外部图片生成或微信发送。 +- 用户复核后确认该恢复态仍是错误的 Level 3 数据卡,故该卡片仅作为 `daily_image.previous.png` 留存,不再视为成功群报。 +- 群 27 的错误数据卡在替换前已备份到 `output/.backups/invalid-group-report-hold-20260904-081524/`。 +- 两群已用内置 ImageGen 生成正常分镜群报,严格事实校验均通过;通过既有恢复入口原子替换后,`image_fallback_level=0`、`image_variant=normal`,并继续持有发送锁。 +- 原失败证据仍保留在 `run.json` 的 `attempt_ledger`;本次未调用微信发送。 ## 返回格式