# M4 迁移验证验收记录(GovDoc + CHS;发布段待 T13 回填)

> **状态**: 两项目迁移验收完成(2026-07-22);v1.0 发布与文档同步进行中。
> 工作区: `~/Projects/m4-worktrees/{GovDoc-SaaS,CHSAnalyzer}` 各自 `feat/polygateway-migration` 分支;reference/ 本体全程停 main 作蓝本。

## 1. GovDoc-SaaS(全量迁移 ✅,verifier 清零)

| 项 | 结果 |
|---|---|
| 删除 | llm/ 六文件(client 582/breaker 70/streaming 131/redis_cache 94/telemetry_sqlite 204)+ test_breaker + 手写 embedding 客户端,净 −1171 行 |
| 改写 | types.py(LLMResponse re-export)、protocols.py(删 TelemetryRecorder)、test_imports、两 pyproject + Makefile(删 redis extra、加 polygateway 依赖) |
| 净新增 | `api/assembly.py` 装配点(from_env + `AGENT_RETRYABLE_EXCEPTIONS`)+ app.py lifespan;`PgwEmbeddingProvider` 适配器 |
| 测试 | `make ci` 全绿: 146+85 passed / 65+19 skipped(基线 143+85/65+19,skip 零增) |
| 冒烟 | 真实网关调用 + isinstance(LLMProvider) + 遥测行 + 二次调用缓存命中(跨进程) |
| 关键行为证据 | B15 两态钉死: 缺省 retryable_exceptions 下库 TransientError 致 `stop_reason=="error"`(静默失效态);注入后步级重试触发(test_agent_loop.py 新增两用例) |
| verifier | 清零通过(0C/0I/3 Minor 均为文档措辞,归 T14 回写) |

## 2. CHSAnalyzer(全量迁移 ✅,verifier 1I/3M 全清)

| 项 | 结果 |
|---|---|
| 删除 | governance/invokers/selector/streaming + coordination limiter/scripts/provider_gate + errors.py Provider 族 + config.py 治理解析段 + ports.py 限流/熔断端口 + 18 个自测/fake,累计约 −5600 行 |
| 新增/改写 | `pgw_shims.py`(PgwVlmProvider,magic bytes 逐字保真)、table_locator(+PgwTableLocator,几何纯函数零改动)、container 换库工厂(lease/stall 守卫归库 G6)、tracking 直接 except 库 `GatewayUnavailableError`/`RequestRejectedError`、startup 活性守卫改读 GatewaySettings |
| 测试 | `make test` 全绿: 836 passed / 99 skipped / 覆盖 86%(基线 961/111;分母经 verifier 独立复算闭合: 删除 −131、新增 +6,skip −12 全来自被删文件) |
| 冒烟 | 真实 MonkeyOCR 定位命中 + 真实网关 VLM(qwen3.7-plus)提取,3 样本全过业务解析门 |
| 回归(用户拍板 50 样本) | **提取解析 50/50(100%)**、定位零异常(35 命中,15 为合法无表/无 marker 语义)、p50 22.4s、错误分类零散落;遥测完整性以干净窗口 10/10 专项复验(证据 tests/outputs/m4/) |
| judge | **豁免**(用户拍板 2026-07-22): core/eval/ 评估流水线实测零调用方、从未接线,judge.py 原样保留;待评估流水线真正启用时再收编走库 |
| 交付物 | 生产切换手册 `research-wiki/findings/2026-07-22-polygateway-cutover-runbook.md`(原子切换/禁混跑/低峰/回滚/行为差异五节) |
| verifier | 代码本体逐条合格(审计 26 条抽查、shim 逐字段、首表口径等价、越界零);1 Important + 3 Minor 全部处置见 §3 |

## 3. verifier findings 处置

| # | 内容 | 处置 |
|---|---|---|
| I1 | 库 `test_postgres_telemetry` fixture 每次 `DROP TABLE llm_calls`——pre-commit 钩子跑测试即清空共享遥测表,曾抹掉回归批跑证据(双向污染: 批跑行也曾使该测试 51==50 失败) | **已修**(PGW 3846305): 测试改 run 级 call_id 前缀隔离 + teardown 只删自己的行,零 DROP;6/6 绿 |
| M1 | WT/C `tests/contracts_limiter.py` 成孤儿 | 已删(6d098ec) |
| M2 | 遥测 10/10 复验未落档且行被 I1 抹掉 | I1 修复后重跑并落档 `tests/outputs/m4/chs_telemetry_10_rerun.log`(10/10) |
| M3 | 4 处 docstring 引用已删的 ProviderError 族 | 已更新(6d098ec) |

## 4. 缺口回补清单(库侧)

M4 全程唯一库侧改动即 §3-I1 的测试隔离修复;**零功能缺口**——两项目所有旧行为均由库既有能力承接(G1-G6/R1-R12 在 M1-M3 已销账的判断经实迁验证成立)。

## 5. 教训(已入记忆)

- 共享后端(Redis db3 与 PG polygateway 库)靠**时序**隔离: 真实批跑期间执行 `git commit`(钩子跑库测试)= 并跑事故;
- tmux 里跑 conda 程序须 `conda run --no-capture-output ... python -u`,否则输出全程缓冲;
- 网关模型清单要先探测再定方案(Q6"走中转调 claude"因网关无 claude 系而作废,judge 最终因未接线豁免)。

## 6. 发布记录(T13,2026-07-22)

- 0.1.0 试发布演练通过(twine → Gitea PyPI → 干净 conda 环境匿名回装);
- bump 1.0.0 + CHANGELOG.md;发布门 `make ci` 507 过 0 败;发布中发现 `__version__` 未随 pyproject 同步,已修(测试改为对照 pyproject 单一版本源),Gitea 侧删旧包重传;
- **v1.0.0 已发布**: 干净环境回装 `polygateway[redis,postgres,structured]==1.0.0` 版本核验通过;两项目环境切正式版后套件重验全绿(GovDoc 146+85 / CHS 836);
- 依赖定稿: GovDoc 根 pyproject `==1.0.*` + docagent-core `>=1.0,<2` + Makefile 带 Gitea extra-index;CHS requirements.txt `==1.0.*` + extra-index-url 行;
- 库仓库: main 快进合并 feat/m4-migration 并打 tag `v1.0.0`;迁移分支已 push GitHub 并发 PR(GovDoc-SaaS#2 / CHSAnalyzer#19,合并由用户执行);
- 遗留: PolyGateway 托管至 Gitea 需用户建仓(token 缺 write:user 域,push-to-create 未启用),远程 `gitea` 已配好待 push;reference/ 本体收尾同步在用户合并 PR 后执行(ff-pull + worktree remove)。
