#!/usr/bin/env python3 """Generate docs/查价系统-工程任务拆解-v1.2.md from v1.1.""" from __future__ import annotations import re from pathlib import Path ROOT = Path(__file__).resolve().parents[1] SRC = ROOT / "docs" / "查价系统-工程任务拆解-v1.1.md" DST = ROOT / "docs" / "查价系统-工程任务拆解-v1.2.md" ABOLISHED = {75, 76, 77, 78} V12_NOTE_TASKS = {22, 23, 28, 73, 110, 117, 88} TASK_119_120 = """## task-119 - **ID**:task-119 - **状态**:待执行 - **模块**:Lib/Infra - **目标**:v0.6 RPA 环境变量模板(GD-15) - **输入**:PRD §8.2.1;技术设计 §6.2 CHG-T03 - **输出**:`.env.example` 增 `MOTHERSHIP_QUOTE_URLS`、`RPA_STORAGE_STATE_PATH`、`RPA_SELECTOR_*`(8 项)、`RPA_USE_PATCHRIGHT` - **实现范围**:禁止内置 dashboard / `/quote` 默认 URL;缺 `MOTHERSHIP_QUOTE_URLS` 时 rpa-worker fail-fast - **验证**:`.env.example` 含全部 RPA 必填项;未配置 URL 列表时 worker 启动报错退出 --- ## task-120 - **ID**:task-120 - **状态**:待执行 - **模块**:Lib/Infra - **目标**:Docker RPA 持久化(storageState volume) - **输入**:技术设计 §6.1 §7.5 CHG-T04 - **输出**:`docker-compose.yml` rpa-worker 注入 RPA env + `rpa_state` volume → `/app/.rpa` - **实现范围**:volume 挂载 + env 透传;不含业务逻辑 - **验证**:`docker compose up -d` 后 `.rpa/mothership-storage.json` 跨容器重启保留 --- """ TASK_121_122 = """## task-121 - **ID**:task-121 - **状态**:待执行 - **模块**:RPA - **目标**:Playwright codegen 录制入口脚本 - **输入**:PRD §8.2.3 GD-15;技术设计 §9.2 - **输出**:`scripts/record-mothership.sh`(headed,`RPA_HEADLESS=false`) - **实现范围**:启动 codegen;禁止 dashboard.mothership.com / www.mothership.com/quote - **验证**:脚本可执行;录制产出 goto URL 与 selector 可提取 --- ## task-122 - **ID**:task-122 - **状态**:待执行 - **模块**:RPA - **目标**:录制 SOP 与 env 产出 checklist - **输入**:task-121;PRD §8.2.3 LA→Dallas 标准样例 - **输出**:`docs/rpa-recording-sop.md`;URL→`MOTHERSHIP_QUOTE_URLS`、selector→`RPA_SELECTOR_*` 映射表 - **实现范围**:文档化人工录制流程;不含自动化提取工具 - **验证**:人工 checklist:env 填齐 8 个 selector 后可跑 probe --- """ PHASE_5_V12 = """# Phase 5 — RPA v1.2 重构(PRD v0.6) > 执行顺序:task-123 → task-124 → task-125 → task-126 → task-127 → task-128 → task-129 → task-130 → task-131 > **里程碑 M2(v1.2)**:`RPA_MOCK_MODE=false` 下 `scripts/probe-rpa.ts` 连续 3 次 exit 0,四档 `source_type=rpa`。 ## task-123 - **ID**:task-123 - **状态**:待执行 - **模块**:RPA - **目标**:实现 QuotePageAdapter 多 URL 探测 - **输入**:技术设计 §7.2.3 CHG-T05/T06;task-122 - **输出**:`workers/rpa/quote-page-adapter.ts`:`resolveQuoteEntry` / `preCheck` / `isLoginWithoutCredentials` - **实现范围**:按 `MOTHERSHIP_QUOTE_URLS` 顺序 goto + preCheck;全失败 → QUOTE_ENTRY_UNAVAILABLE - **验证**:单测 mock 多 URL;首个可用 URL 写入 activeUrl;全失败抛 QUOTE_ENTRY_UNAVAILABLE --- ## task-124 - **ID**:task-124 - **状态**:待执行 - **模块**:RPA - **目标**:storageState 生命周期(§7.5) - **输入**:技术设计 §7.5 CHG-T09;task-120 - **输出**:重构 `workers/rpa/session-manager.ts`:`createContext` load、`persistContext` writeback、`invalidateStorageState` - **实现范围**:成功查价写回;login 无凭据先 invalidate 再重试 goto - **验证**:单测:存在 state 文件时 load;成功后 persist;invalidate 删除文件 --- ## task-125 - **ID**:task-125 - **状态**:待执行 - **模块**:RPA - **目标**:地址联想 selector env 化 - **输入**:技术设计 §7.2.1 CHG-T07;task-123 - **输出**:`workers/rpa/mothership.ts` `fillAddress` 改用 `RPA_SELECTOR_PICKUP/DELIVERY/ADDRESS_SUGGESTION` - **实现范围**:失败 → ADDRESS_SUGGESTION_NOT_FOUND;不重试 - **验证**:mock 页面:联想成功锁定;无下拉 → ADDRESS_SUGGESTION_NOT_FOUND --- ## task-126 - **ID**:task-126 - **状态**:待执行 - **模块**:RPA - **目标**:表单填写与提交 selector env 化 - **输入**:技术设计 §7.2 CHG-T07;task-125 - **输出**:`fillCargo` + submit 改用 `RPA_SELECTOR_SUBMIT` 等 env - **实现范围**:移除硬编码 data-testid;全部读 `process.env.RPA_SELECTOR_*` - **验证**:录制回放:字段填入正确;submit 点击成功 --- ## task-127 - **ID**:task-127 - **状态**:待执行 - **模块**:RPA - **目标**:四档抓取 selector env 化 - **输入**:技术设计 §7.2 步骤 7 CHG-T07;task-126 - **输出**:`scrapeQuotes` 改用 `RPA_SELECTOR_STANDARD/GUARANTEED/LOWEST/FASTEST/PRICE` - **实现范围**:standard/guaranteed × lowest/fastest 各抓取一次;items.length===4 - **验证**:mock 结果页返回 4 档;每档 rawFreight>0 --- ## task-128 - **ID**:task-128 - **状态**:待执行 - **模块**:RPA - **目标**:错误分类 v0.6(入口 vs 抓取) - **输入**:PRD §5.6 §11.5 CHG-P03/P04;技术设计 §7.3 CHG-T08 - **输出**:`workers/rpa/error-mapper.ts` + job-handler:QUOTE_ENTRY_UNAVAILABLE;login 无凭据 → STRUCT_CHANGE - **实现范围**:入口/地址错误不重试;SESSION_EXPIRED 仅已配置凭据且曾成功登录 - **验证**:单测各错误分支映射;QUOTE_ENTRY_UNAVAILABLE 不触发 job 重试 --- ## task-129 - **ID**:task-129 - **状态**:待执行 - **模块**:Quote Engine - **目标**:Fallback 入口错误处理 - **输入**:PRD §2.5 CHG-P03;task-128 - **输出**:增补 `modules/quote/fallback-orchestrator.ts`:入口类错误不消耗 RPA job 重试配额 - **实现范围**:QUOTE_ENTRY_UNAVAILABLE / ADDRESS_SUGGESTION_NOT_FOUND 直接走 L3 或 400 - **验证**:单测:入口错误 job attempts=1;PAGE_LOAD_TIMEOUT 仍可重试 1 次 --- ## task-130 - **ID**:task-130 - **状态**:待执行 - **模块**:RPA - **目标**:升级 probe 探针(连续 3 次 exit 0) - **输入**:PRD TC-601 §12.7 CHG-P08/P09;技术设计 CHG-T02 - **输出**:升级 `scripts/probe-rpa.ts` + `scripts/probe-rpa-3x.sh`;断言 items.length===4 - **实现范围**:`RPA_MOCK_MODE=false` 真实探测;3x 脚本循环调用 - **验证**:`bash scripts/probe-rpa-3x.sh` 连续 3 次 exit 0;输出 4 档 JSON --- ## task-131 - **ID**:task-131 - **状态**:待执行 - **模块**:RPA - **目标**:Patchright 可选驱动(P1) - **输入**:技术设计 §6.2 `RPA_USE_PATCHRIGHT` CHG-T03 - **输出**:`workers/rpa/session-manager.ts` 支持 `RPA_USE_PATCHRIGHT=true` 切换浏览器 - **实现范围**:默认 Playwright;Patchright 为可选降级路径 - **验证**:`RPA_USE_PATCHRIGHT=true` 时 worker 启动无报错;false 时行为不变 --- """ TASK_132 = """## task-132 - **ID**:task-132 - **状态**:待执行 - **模块**:Frontend - **目标**:实现 /admin/queues Bull Board 队列监控页(P1) - **输入**:技术设计 §8.2 CHG-T10;task-021 - **输出**:`app/admin/queues/page.tsx` + Bull Board 挂载;admin RBAC - **实现范围**:展示 quote-rpa 队列深度、失败 job、延迟;文案全中文 - **验证**:admin → 页面渲染队列指标;非 admin → 403 --- """ TASK_133_135 = """## task-133 - **ID**:task-133 - **状态**:待执行 - **模块**:Test - **目标**:TC-601 修订(四档 + probe 对齐 PRD v0.6) - **输入**:PRD TC-601 CHG-P08;task-130 - **输出**:修订 `e2e/rpa.test.ts` / `__tests__/integration/rpa-probe.test.ts`;items=4 断言 - **实现范围**:mock 关闭时 probe 四档;文档对齐 v0.6 口径 - **验证**:TC-601 items.length===4;非 2 条 --- ## task-134 - **ID**:task-134 - **状态**:待执行 - **模块**:Test - **目标**:Go-No-Go probe 3/3 门禁 - **输入**:PRD §12.7 CHG-P09;task-130 - **输出**:`scripts/go-no-go-check.ts` 增 probe 3/3 条件(`RPA_MOCK_MODE=false` 时启用) - **实现范围**:probe 失败 → Go-No-Go FAIL;mock 模式跳过 probe 门禁 - **验证**:mock 模式 PASS;真实 env 下 probe 3/3 才 PASS --- ## task-135 - **ID**:task-135 - **状态**:待执行 - **模块**:Test - **目标**:CI nightly probe 步骤 - **输入**:task-134;task-118 - **输出**:`.github/workflows/e2e-nightly.yml` 可选 headed probe 步骤(secrets 配 MOTHERSHIP env) - **实现范围**:nightly 不阻断 merge;probe 失败上传 artifact - **验证**:workflow 语法有效;secrets 缺失时跳过 probe 步骤 --- """ HEADER_V12 = """# 查价系统 工程任务拆解清单 v1.2 | 版本 | 日期 | 说明 | |------|------|------| | v1.0 | 2026-06-16 | 首版原子任务拆解 | | v1.1 | 2026-06-16 | 对照 Traceability Matrix 补齐;口径对齐 PRD v0.4 | | v1.1.1 | 2026-06-16 | **按推荐开发依赖顺序重排任务章节**(非 Task ID 数字序) | | v1.2 | 2026-06-16 | 对齐 PRD v0.6 + 技术设计 v1.2;基线 001–118 已完成;增量 119–135 | > **当前版本请使用本文件(v1.2)。** v1.1 文件保留作历史对照。 > 配套文档:[查价系统-PRD.md](./查价系统-PRD.md)(v0.6)· [查价系统-技术设计.md](./查价系统-技术设计.md)(v1.2)· [查价系统-UI设计.md](./查价系统-UI设计.md) · [查价系统-Traceability-Matrix-v1.0.md](./查价系统-Traceability-Matrix-v1.0.md) > **v1.1 基线(task-001~118,除废止)= 已完成;v1.2 增量(task-119~135)= PRD v0.6 + 技术设计 v1.2 待交付。** --- ## 开发顺序总览(自上而下执行) | Phase | 名称 | Task 序列 | 状态 | |-------|------|-----------|------| | 0 | 环境与底座 | 072→073→**119→120**→070→071→097→030 | 072–030 已完成;119–120 待执行 | | 1 | 基础设施与缓存 | 096→031~036→035-ip | 已完成 | | 2 | 鉴权 | 001~005→098 | 已完成 | | 3 | 领域逻辑 | 040~044→010~014,092,095,109,111,019 | 已完成 | | 4 | API 层 | 015~017→042~043→018 | 已完成 | | 5.0 | RPA 录制前置 | **121→122** | 待执行 | | 5 | RPA v1.2 重构 | **123→131**(基线 020–029,110,093,099,100 已完成) | 123–131 待执行 | | 6 | 预警/审计 | 050~057→094→105 | 已完成 | | 7 | 观测指标 | 101→102 | 已完成 | | 8 | 前端 | 060~069→106→063,064,079,103,104→**132**→080 | 132 待执行 | | 9 | 测试/CI | 081~118→**133→135**→091 | 133–135 待执行 | | — | 已废止 | 075~078 | — | **里程碑 M1(Phase 0~4)**:已完成 — `POST /quotes` L2 命中、幂等、校验 400。 **里程碑 M2(v1.2)**:`RPA_MOCK_MODE=false` 下 `scripts/probe-rpa.ts` **连续 3 次 exit 0**,四档 `source_type=rpa`(TC-601 + §12.7 Go/No-Go)。 --- ## 模块索引(按职能归类,非执行顺序) | 模块 | Task ID | 数量 | |------|---------|------| | 数据库(DB) | 070–073 | 4 | | 基础设施(Lib) | 096–098, **119–120** | 5 | | 缓存系统 | 030–036, 035-ip | 8 | | 用户系统(Auth) | 001–005 | 5 | | 加价系统(Markup) | 040–044 | 5 | | 查价核心(Quote Engine) | 010–019, 092, 095, 109, 111, **129** | 15 | | API 路由 | 015–018, 042–043 | 6 | | RPA 系统 | 020–029, 093, 099–100, 110, **121–131** | 23 | | 预警系统(Alert) | 050–057, 094, 105 | 10 | | 观测与指标 | 101–102 | 2 | | 前端 UI | 060–080, 103–104, 106, **132** | 19 | | 测试系统 | 081–091, 112–118, **133–135** | 21 | | **合计(可执行)** | | **~119** | | **已废止** | 075–078 | 4 | ## 技术栈约定 - Next.js 15 App Router + TypeScript - MySQL 8.0 + Prisma ORM - Redis 7 + BullMQ - Playwright(RPA Worker) - Tailwind CSS + shadcn/ui + phosphor-icons ## v1.1 口径冻结(对齐 PRD v0.4,基线已完成) | 决策 | 规则 | 任务影响 | |------|------|----------| | GD-11 嵌入形态 | 查价 UI 为**宿主内嵌组件**,中台仅 admin 管理端 | task-106 替代 075–077;task-064 仅 admin | | §2.3 加价配置 | 中台**不提供**运营加价 UI,由宿主调 API | task-078 **废止**;task-042/043 保留 | | 四档报价 | GD-1/TC-102 为准(quotes=4) | task-088/111 按 4 档验收 | | Fallback | 独立 FallbackOrchestrator 模块 | task-092 | | 管理端 RPA | §3.1 预警 + RPA/开发进度 | task-093, 103, 104 | | audit / metrics | §10.6 audit log + GD-4 三率 | task-094, 101, 102, 117 | ## v1.2 口径冻结(对齐 PRD v0.6 / 技术设计 v1.2) | 决策 | 规则 | 任务影响 | |------|------|----------| | GD-15 入口 URL | `MOTHERSHIP_QUOTE_URLS` headed 录制;禁止 dashboard / `/quote` | task-119, 121, 122, 123 | | 入口 vs 抓取错误 | 入口错误与抓取错误分开分类;地址未锁定单独处理 | task-125, 128, 129 | | 新错误码 | `QUOTE_ENTRY_UNAVAILABLE`、`ADDRESS_SUGGESTION_NOT_FOUND` | task-128, 110(基线) | | QuotePageAdapter | 多 URL 探测 + storageState + login 无凭据→STRUCT_CHANGE | task-123, 124 | | Selector env 化 | 全部 `RPA_SELECTOR_*`,禁止手猜 selector | task-125–127 | | probe 门禁 | 连续 3 次 exit 0 + 四档 | task-130, 133–135 | | Bull Board | `/admin/queues` 队列监控(P1) | task-132 | ## v1.0 → v1.1 变更摘要 | 类型 | Task | 说明 | |------|------|------| | 新增 | 092–118(除重复编号) | 见各 Phase 章节 | | 修订 | 005, 014, 016, 018, 029, 050, 064, 065, 066, 079, 085, 091 | 补 RBAC/置信度/Fallback 引用等 | | 废止 | 075, 076, 077, 078 | PRD v0.4 口径下不交付 | | 替代 | 106 | 宿主内嵌查价组件(替代 075) | | 排序 | v1.1.1 | 正文按 Phase 0→9 开发顺序排列 | ## v1.1 → v1.2 变更摘要 | CHG ID | 类型 | Task | 说明 | |--------|------|------|------| | CHG-P02/T03 | 新增 | 119, 121, 122 | GD-15 env + 录制前置 | | CHG-T04 | 新增 | 120 | docker rpa_state volume | | CHG-P05/T05/T06 | 新增 | 123 | QuotePageAdapter | | CHG-T09 | 新增 | 124 | storageState 生命周期 | | CHG-T07 | 新增 | 125–127 | selector env 化 | | CHG-P03/P04/T08 | 新增 | 128, 129 | 错误分类 v0.6 | | CHG-P08/T02 | 新增 | 130 | probe 3/3 | | CHG-T03 P1 | 新增 | 131 | Patchright 可选 | | CHG-T10 | 新增 | 132 | /admin/queues Bull Board | | CHG-P08/P09 | 新增 | 133–135 | TC-601 + Go-No-Go + CI | | — | 状态 | 001–118 | 全部标「已完成」(废止除外) | | — | 说明 | 022, 023, 028, 073, 110, 117, 088 | 基线已实现;行为变更由 task-12x 承接 | --- """ APPENDIX_A = """## 附录 A:验收用例 → Task 映射(v1.2 全覆盖) | TC | 主验证 Task | 辅助 Task | |----|-------------|-----------| | TC-101~108 | 085, 112 | 014, 032, 013, 011, 012 | | TC-201~204 | 082, 010 | — | | TC-205~206 | 115, 092, **129** | 052, 053 | | TC-207 | 084, 013 | — | | TC-208~209 | 018, 028, 036, **128** | 092 | | TC-210~212 | 087, 051 | — | | TC-301~304 | 090, 018, 021 | — | | TC-401 | 113 | 032, 051 | | TC-402~404 | 084, 019 | 013 | | TC-403 | 114 | 041 | | TC-501~506 | 086, 094, 035-ip | 004, 010 | | TC-601~605 | 088, 115, **130, 133** | 026, 027, 092, 123–127 | | Go-No-Go | 117, 101, 102, **134** | 090, **130** | | probe 3/3 | **130, 134, 135** | 121, 122 | --- """ APPENDIX_B = """## 附录 B:完整性矩阵(PRD v0.6 + 技术设计 v1.2) ### B.1 维度覆盖 | 维度 | v1.1 基线 Task | v1.2 增量 Task | |------|----------------|----------------| | PRD 功能点 | 001–118(除废止) | GD-15(119–122)、错误码(128–129)、probe(130,133–135)、Bull Board(132) | | 技术模块 | 各 Phase 已覆盖 | quote-page-adapter(123)、storageState(124)、selector env(125–127)、probe(130) | | UI 页面 | 106 + admin 6 页 | `/admin/queues`(132) | | API(§8.5 七接口 + RPA admin) | 015–018, 042–043, 056–057, 029/093/099/100 | 无新增 REST | | 数据表(6 张) | 070–073 | 无新增表 | ### B.2 PRD v0.6 变更 → Task | CHG ID | 章节 | Task | |--------|------|------| | CHG-P02 | GD-15 | 119, 121, 122 | | CHG-P03 | §2.5 | 128, 129 | | CHG-P04 | §5.6 | 128 | | CHG-P05 | §8.2 | 123, 124 | | CHG-P06 | §8.2.1~8.2.3 | 119, 121, 122, 125–127 | | CHG-P07 | §11.5 | 124, 128 | | CHG-P08 | TC-601 | 130, 133 | | CHG-P09 | §12.7 | 134, 135 | ### B.3 技术设计 v1.2 变更 → Task | CHG ID | 章节 | Task | |--------|------|------| | CHG-T02 | §5 目录 | 121, 130 | | CHG-T03 | §6.2 env | 119, 131 | | CHG-T04 | §6.1 compose | 120 | | CHG-T05~T07 | §7.2 | 123–127 | | CHG-T08 | §7.3 | 128 | | CHG-T09 | §7.5 | 124, 120 | | CHG-T10 | §8.2 | 132 | | CHG-T11~T12 | §9.2 | 121, 122 | ### B.4 v1.1 Traceability Matrix 缺口(已闭合,基线) | Matrix 缺口 | Task | |-------------|------| | Fallback 编排无独立 Task | task-092, 115 | | GET /api/rpa/status + /admin/rpa | task-093, 103 | | audit log | task-070(schema), 094 | | confidence_score | task-095 | | lib/response.ts / lib/prisma.ts | task-096, 097 | | GD-4 metrics | task-101, 102, 104, 117 | | IP 限流 | task-035-ip | | 503 retry | task-098 | | 管理员暂停 RPA | task-099, 103 | | 人工兜底录入 | task-100, 079 | | display_unit | task-065 | | 客户端退避 | task-066 | | 宿主嵌入 vs 独立页 | task-106(废止 075~077) | | 中台加价 UI 漂移 | 废止 078 | | TC-108/302/401/403 | task-112~114 | | quote_id 冲突 | task-109 | | CARRIER/ADDRESS 错误码 | task-110 | | BR-5.5 四档齐全 | task-111 | | SESSION_EXPIRED/SECURITY | task-105 | | rpa:operate RBAC | task-005 | """ def task_num_from_heading(line: str) -> int | None: m = re.match(r"^## (?:~~)?task-(\d+)", line) return int(m.group(1)) if m else None def process_task_block(block: str, num: int) -> str: if num in ABOLISHED: return block lines = block.split("\n") out: list[str] = [] id_inserted = False for line in lines: out.append(line) if not id_inserted and line.startswith("- **ID**:"): out.append("- **状态**:已完成") id_inserted = True if num in V12_NOTE_TASKS: inserted = False for i, line in enumerate(out): if ( line.strip() == "---" and i + 2 < len(out) and out[i + 1].strip() == "" and out[i + 2].startswith("## task-") ): out.insert(i, "") out.insert(i, "- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接") inserted = True break if not inserted: out.append("") out.append("- **v1.2 说明**:基线已实现;行为变更由 task-12x 承接") return "\n".join(out) def split_tasks(body: str) -> list[tuple[str | None, str]]: """Split body into (task_num, block) segments. task_num None for non-task blocks.""" parts = re.split(r"(?=^## (?:~~)?task-\d+)", body, flags=re.MULTILINE) result: list[tuple[str | None, str]] = [] for part in parts: if not part.strip(): continue first_line = part.split("\n", 1)[0] num = task_num_from_heading(first_line) result.append((num, part)) return result def main() -> None: src_text = SRC.read_text(encoding="utf-8") # Find where Phase 0 body starts (after header section) phase0_marker = "# Phase 0 — 环境与底座" idx = src_text.find(phase0_marker) if idx < 0: raise SystemExit("Phase 0 marker not found") header_old = src_text[:idx] body = src_text[idx:] # Split appendices appendix_a_marker = "## 附录 A:" app_idx = body.find(appendix_a_marker) if app_idx < 0: raise SystemExit("Appendix A not found") task_body = body[:app_idx] # Update Phase 0 header line task_body = task_body.replace( "> 执行顺序:task-072 → task-073 → task-070 → task-071 → task-097 → task-030", "> 执行顺序:task-072 → task-073 → **task-119 → task-120** → task-070 → task-071 → task-097 → task-030", 1, ) # Process tasks and rebuild segments = split_tasks(task_body) rebuilt: list[str] = [] i = 0 while i < len(segments): num, block = segments[i] if num is None: rebuilt.append(block) i += 1 continue processed = process_task_block(block, num) # Insert 119-120 before task-070 (after task-073 section) if num == 70: rebuilt.append(TASK_119_120) # Phase 5.0 header + 121-122 before baseline; v1.2 block after task-100 if num == 18: processed = processed.replace( "# Phase 5 — RPA(W2)", "# Phase 5.0 — RPA 录制前置(W2 前置,CHG-T11/T12)\n\n" "> 执行顺序:task-121 → task-122 \n" "> **必须在 task-123 之前完成**:无 env 则 adapter/probe 无法验收。\n\n" + TASK_121_122 + "\n# Phase 5 — RPA 基线(W2,已完成)", 1, ) processed = processed.replace( "> 执行顺序:task-020 → task-021 → task-022 → task-023 → task-024 → task-025 → task-026 → task-027 → task-028 → task-110 → task-029 → task-093 → task-099 → task-100", "> 执行顺序:task-020 → … → task-100(**全部已完成**;v1.2 增量见下方 Phase 5 v1.2 重构)", 1, ) if num == 100: phase6_marker = "\n# Phase 6 — 预警" if phase6_marker in processed: processed = processed.replace( phase6_marker, "\n\n" + PHASE_5_V12.rstrip() + "\n\n" + phase6_marker.lstrip("\n"), 1, ) else: processed = processed + "\n\n" + PHASE_5_V12 rebuilt.append(processed) if num == 104: rebuilt.append(TASK_132) if num == 118: rebuilt.append(TASK_133_135) i += 1 final_body = "".join(rebuilt) final_text = HEADER_V12 + final_body + APPENDIX_A + APPENDIX_B + "\n" DST.write_text(final_text, encoding="utf-8") print(f"Wrote {DST} ({len(final_text)} chars)") if __name__ == "__main__": main()