Files
PolyGateway/research-wiki/findings/2026-09-09-135-call-observability-validation.md
T
iomgaa 433039be79 chore: prepare release 1.3.5
Version bump in pyproject and __init__, CHANGELOG dated 2026-09-09,
README install lower bound raised to >=1.3.5, and release-prep evidence
(remote check, gates, real gateway smoke and one bounded live probe)
recorded in the 1.3.5 validation finding.
2026-09-09 13:21:40 -04:00

291 lines
23 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
type: finding
node_id: finding:2026-09-09-135-call-observability-validation
title: "1.3.5 T2/T3/T4 验收证据:36 列遥测、失败终态、PG 存储兼容与变异矩阵"
date: 2026-09-09
---
# 1.3.5 T2/T3 验收证据
> 范围:**仅 T2(遥测 10 列 / 诊断保真 / scope+operation / 装配闸)与 T3(终态行 / 取消 / 统一出口)**,
> 外加计划 T4 中"签名与列数机械迁移"那一片(与 schema 同批完成,避免先提交 schema 却留写入缺键)。
> **不含** T4 的 PG 集成、变异矩阵与文档同步——另任务承接,缺口见 §5。
> 设计:`designs/2026-09-09-135-call-observability-design.md`;计划:`plans/2026-09-09-135-call-observability.md`。
> 基线 HEAD `87c261b`T1 已提交,1419 unit 全绿)。全部命令在 `PolyGateway` conda 环境执行。
## 1. 红绿证据链
TDD 纪律要求"先失败后通过",且红必须是**行为红**而非 import 红。下表每行都对应本会话的真实工具输出。
| # | 阶段 | 命令 | 结果 |
| --- | --- | --- | --- |
| 0 | 基线 | `pytest tests/unit -q` | **1419 passed**(对照底) |
| 1 | 红(T2/T3 目标行为) | `pytest tests/unit/test_telemetry.py -k "RowLevelObservability or RecorderShapeGate"` | **26 failed**,证据 `tests/outputs/135/red-01-emitter-rows.txt` |
| 2 | 实现后全量红面 | `pytest tests/unit -q` | **137 failed / 1308 passed**(机械迁移面暴露),`red-02-after-impl.txt` |
| 3 | 迁移中 | 同上 | 60 → 25 → 8 → 4 failed`red-03`/`red-04`/`red-05` |
| 4 | 绿 | `pytest tests/unit -q` | **1467 passed, 0 failed** |
| 5 | 静态门 | `make check`ruff lint+format + import-linter | **Contracts: 1 kept, 0 broken** |
第 1 步的红是行为红而非 import 红:新用例调用的是**已存在**的 `TelemetryEmitter`
失败形态是"缺 `scope=`/`operation=`/`stats=` 关键字"与"断言的列不存在",不是模块导不进来。
补测阶段另有两次真实红(均由 conda 实跑暴露、当场修正,非噪音):
`test_client.py``NameError: ResultInvalidError / ChatRequest`(漏 import)、
`TestTerminalRowSqlSemantics``'coroutine' object has no attribute 'execute'`async helper 漏 await)。
## 2. 核心要求逐条对应
### 2.1 36 列与 Emitter 完整一致(不留"schema 有列、写入缺键"
一次性同批改完五处列定义 + 端口签名 + Emitter 写入,故不存在中间态。
| 判据 | 证据 |
| --- | --- |
| `len(COLUMNS) == 36`、物理列 37 | `test_telemetry.py::TestSchemaModule::test_columns_and_ddl_are_frozen``TestSQLiteSchemaMode` 断言 23 → 37 |
| 五处列序一致(DDL/BACKFILL×2/COLUMNS | 新建库与 ALTER 追加列序同为 `_EXPECTED_COLUMNS` |
| 端口实测 36 字段、10 新参 keyword-only 且无默认值 | `test_ports.py::TestTelemetryRecorderSignature``inspect.signature` 实测,不凭记忆) |
| **Emitter 实参键集合 == `schema.COLUMNS`** | `TestEmitterRecorderContract` 三入口逐个断言 `set(rows[0]) == set(COLUMNS)` |
| 1.2.1 冻结 INSERT 未被改写 | 按 `_PRE_135_COLUMNS`(26 列)重现原文;全量 36 列另按占位符个数断言 |
装配闸(C3):`_assert_recorder_shape``TelemetryEmitter.__init__` 做一次 `signature.bind`
参数名**从协议签名派生**而非手抄第四份清单——`test_gate_derives_parameter_names_from_the_protocol`
用 monkeypatch 换掉协议后闸自动跟随,证明没有硬编码。旧签名 recorder / 无该方法 / 不可 inspect
一律装配期 `ValueError`(不是 warning:降级铁律管的是运行期写失败,不是配置错误)。
### 2.2 三个 client 的**真实链路**失败终态
不是只测 Emitter,而是驱动真实 client + MockTransport 到落库。
| 链路 | 用例 | 断言 |
| --- | --- | --- |
| chat 重试耗尽 | `TestChatTerminalFailureRows::test_retry_exhaustion_writes_exactly_one_terminal_row` | 3 条 attempt + **恰 1 条**终态;终态 `attempts == 3` |
| chat 结构化耗尽 | `test_structured_exhaustion_writes_the_only_failure_row` | attempt 行**全是成功行**,终态是唯一失败记录;error 含有界 `validation=`/`repair=` 且 < 1200 字符、不含 raw_text |
| chat 400 直拒 | `test_request_rejected_now_has_both_an_attempt_and_a_terminal_row` | 尝试行 + 终态行**各 1**(已批准的行数翻倍);两行共享同一 `logical_call_id` |
| embedding | `test_embedding.py::TestReasonlessTelemetryContract::test_failed_attempts_still_have_null_effort` | attempt 数 == 脚本长度,终态恰 1 |
| OCR 两方法 | `test_ocr_client.py` 同名用例(参数化 `recognize_text`/`parse_layout` | 终态恰 1,且 `operation` 为**公开方法名** |
去重:`test_terminal_row_is_written_once_per_logical_call` 连调出口 3 次,SQL 可见仍 1 条(`claim_terminal`)。
非领域异常:`test_non_domain_exception_writes_no_terminal_row` —— `KeyError` 原样传播、**0 条**终态、分类不被改写。
**双写已消除**`TelemetryMW` 的两个终态分支删除,改由三个 client 的公开边界经
`emit_terminal_once` 统一写出;`TestTelemetryMW::test_scope_level_failure_is_not_written_here_anymore`
`test_cancellation_is_not_written_here_anymore` 锁死"本层不再写终态",防回归双计。
### 2.3 取消口径
- chat 取消:`test_cancellation_writes_at_most_one_terminal_row` —— 恰 1 条,`error == "cancelled"`
`error_type` 为 NULL(**字符串不解析猜诊断**)。
- 三链路同策略"尽力写一条、允许 0"`emit_terminal_once` **不 shield、不开后台任务**
快照冻结是同步动作;写入 await 上再被取消则 `CancelledError` 原样传播(与 TelemetryMW 历史行为同款)。
### 2.4 SQL 不双计(按真实 SQLite 落库断言)
`TestTerminalRowSqlSemantics` 用真实 `SQLiteRecorder` 驱动一次失败 chat 后直接查表:
| 迁移影响 | 断言 |
| --- | --- |
| 失败计数判据 | `error IS NOT NULL`**3**2 尝试 + 1 终态),`event_kind='terminal_failure'`**1** |
| 费用不双计 | 终态行 `cost IS NOT NULL` 计数为 **0**;且 `usage_source='unavailable'`、token 全 0 |
| 时延分组 | 终态 `latency_ms` ≥ 任何单次尝试(含退避),故看板必须按 `event_kind` 分组 |
| 双时钟微差 | 终态 `latency_ms == total_latency_ms`(同一份冻结快照) |
| 逻辑两列归属 | 尝试行 `attempts`/`total_latency_ms` 恒 NULL |
| §5 归因查询 | 同一 `logical_call_id` 同时给出整池 reason 文案与逐源 503 现场;终态 `http_status_code` 为 NULLC1 不冒充) |
### 2.5 诊断保真与 operation 修正
- 中转把 529 改写成 503 → **记 503 不猜回 529**;直接 529 记 529。
-`str()` 的 Connect/Read/Write/PoolTimeout → `cause_type` 落对应 httpx 类名,`error` 退回类名。
- 成功行五列全 NULL(**不统一填 200**)。
- `_status_to_error` 增 keyword `operation``embed()` 非 200 改传 `"embedding"`(修正历史误标),
流式与非流式 chat 两处仍 `"chat"`(按实施计划 §2 归属表,未按设计行号误标)。
- 新列 `operation` 恒为公开方法四值,**绝不读 `exc.operation`**
`test_operation_is_given_by_the_call_site_not_the_exception``exc.operation="download_result"` 反证。
- OCR `"类名: msg"` 前缀由出口的显式策略参数 `class_prefixed_error` 承载,不再三处各拼一遍。
## 3. 有界 validation 说明的单一所有者
`structured.py``_MAX_FEEDBACK_ERRORS`/`_MAX_ERROR_CHARS`/`_format_errors` 改名为
公开的 `MAX_FEEDBACK_ERRORS`/`MAX_ERROR_CHARS`/`format_bounded_errors`
由"重问反馈"与"终态结构化说明"两个消费者共同引用,**数值只有一份**。
行为逐字不变(`test_structured.py` 22 项全绿,含反馈文案用例)。
## 4. 未改动确认(防越界)
`errors.py``admission.py``ratelimit.py``breaker.py``sources.py``thinking.py`
`providers.py``telemetry/sqlite.py``telemetry/postgres.py``transports/monkey_ocr.py`
一字未动——两个 recorder 靠 `**fields` + `schema.COLUMNS` 自动吃到新列。
缓存 key 公式、重试预算与退避、429 免预算、stall 算法、取消结算、推理能力表均未触碰。
唯一顺带修正:`RetryMW.__init__``emitter` 注解由 `object | None` 收紧为
`TelemetryEmitter | None`TYPE_CHECKING 导入,同层不破分层契约),因本轮改了它的 `_emit`
## 5. T4 验收(PG 存储兼容、变异矩阵、文档同步)
基线 HEAD `393f2bf`(1467 unit 全绿),全部命令在 `PolyGateway` conda 环境执行。
### 5.1 PG 存储兼容(真实实验室 Postgres,复用 `pg_sandbox`
机械迁移:`_EXPECTED_COLUMNS` 27 → 37 列、`_record_minimal` 字段字典补 10 键(与单测同款)、
`_PRE_TENANT_COLUMNS` 的缺列集由4 列扩到 14 列。新十列一律由 `_CALL_OBSERVABILITY_COLUMNS` 派生,
两处 manual 档告警的逐字断言改成按 `COLUMNS` 序派生的 `_PRE_TENANT_MISSING_NOTICE`——
另抄一份列名必然漂移,而漂移的表现是“manual 档没补列”这条断言假绿。
| 阶段 | 命令 | 结果 |
| --- | --- | --- |
| 机械迁移前(已知必红) | `pytest tests/integration/test_postgres_telemetry.py -q -x` | **1 failed / 18 passed**manual 档列序断言 `Right contains 10 more items, first extra item: 'scope'` |
| 机械迁移后 | 同上(无 `-x` | **27 passed**EXIT=0`tests/outputs/135/pg-telemetry-after-mechanical.log` |
| 新增四项验收后 | 同上 | **30 passed**EXIT=0`tests/outputs/135/pg-telemetry-new-cases.log` |
新增 `TestCallObservabilityColumnsAcceptance`(对应计划 T4 的 PG 四项),共用一张 27 列的 1.3.4 形态旧表(`pre_135_schema`):
| 验收项 | 用例与关键断言 |
| --- | --- |
| auto 追加 10 列 + 旧行 NULL | `test_pre_135_table_gains_the_ten_columns_and_old_rows_stay_null`:物理列 27 → 37 且列序 == `_EXPECTED_COLUMNS`attempt 行与 terminal 行十列取值**整体比对**(不是逐条 in);历史行 `dict.fromkeys(...)` 十列全 NULL |
| manual 缺列裁剪 | `test_manual_trims_the_insert_on_a_pre_135_table`:表结构逐字不动(== `_PRE_135_COLUMNS`);裁剪后 26 列逐列等于提交值(防整体错位);恰 1 条告警且含可直接粘贴的首/末列 ALTER;无“写入失败”/“补列失败”,`degraded is False` |
| 新旧进程混写 | `test_old_and_new_writers_share_one_table`:新版建表写 36 列 → 旧版进程用 `insert_sql("postgres", 26 列集)` 写入 → 新版再写;三行共存、表结构不变、旧行新列全 NULL、无写入失败 warning |
| 下游口径验收 | 同上用例尾部:`WHERE event_kind = 'terminal_failure'` 计得 1`event_kind IS NULL` 计得 1(混写期旧行既不误计成失败也不误计成成功) |
纪律:未引用 `assert_no_leftovers`(它是 `test_pg_sandbox.py` 的模块级 fixture,对本文件不可见,上提它要改 `conftest.py`);
未新建沙箱设施、未碰共享表 `llm_calls`、未读或打印 DSN。新增的只有一个 `_execute_args`(带参数单语句)与
`_minimal_fields`(从 `_record_minimal` 拆出的字段字典,供“旧进程”复用同一份取值)。
### 5.2 变异矩阵(仓库外副本,主工作区生产代码零改动)
驱动脚本 `/tmp/pgw-135-mut/run_mutations.py`;每项“还原副本 → 施加单点变异(断言替换命中)→ 跑指定节点 → 还原”。
完整日志 `tests/outputs/135/mutations.log`,脚本总退出码 **EXIT=0**
| # | 变异 | exit | 被杀断言(节选) |
| --- | --- | --- | --- |
| M1 | `register_attempt()` 挪到 transport 成功之后 | 1 | `test_failed_retries_are_counted``test_budget_free_429_still_counts_as_an_attempt``test_retry_exhausted_counts_every_attempt` |
| M2 | `ChatRequest` 每次 `replace` 复制出新上下文 | 1 | `TestLogicalCallStats` 4 项 + `test_retry_exhaustion_writes_exactly_one_terminal_row` |
| M3 | `_rehydrate` 去掉 `call_stats=None` 覆盖 | 1 | `test_historic_dict_never_impersonates_call_stats` |
| M4 | Emitter 入口提前 `str(exc)` 压平 | 1 | 529/503 保真、4 个空超时文案 `cause_type`、类名前缀策略、结构化有界说明等 9 项 |
| M5 | 终态行复制最后一次 attempt 的 token/cost | 1 | `test_terminal_rows_never_contribute_to_cost``test_terminal_row_costs_nothing` |
| M6 | 去掉 `claim_terminal` 去重 | 1 | `test_terminal_row_is_written_once_per_logical_call``test_claim_terminal_is_true_once` |
| M7 | 装配闸改为捕获 `TypeError` 后 warning | 1 | `test_old_signature_recorder_is_refused_at_assembly``test_uninspectable_recorder_is_a_configuration_error` |
还原校验:副本 `.py` 文件集合散列与纯净态**逐字一致**(`66b58b9a…`),还原后同一批节点 **172 passed**
> **方法论陷阱(影响本仓所有变异证据的有效性)**:只设 `PYTHONPATH=<副本>/src` 是**无效的**——
> `pyproject.toml` 的 `[tool.pytest.ini_options] pythonpath = ["src"]` 会把**仓库内**的 `src` 抢先塞进 `sys.path[0]`
> 于是测的仍是原代码。首轮实跑七项变异**全部“存活”(exit=0)**就是这个坑;
> 改用 `-o pythonpath=<副本>` 覆盖后七项全部被杀。今后做变异必须先证“副本真的被导入”,
> 否则“变异存活”会被误读成“测试不够强”,而真相是变异根本没生效。
### 5.3 文档同步(字段数一律 `inspect` 实测,不凭记忆)
实测值:`len(inspect.signature(TelemetryRecorder.record_llm_call).parameters) - 1 == 36``len(COLUMNS) == 36`,物理列 37。
| 位置 | 改了什么 |
| --- | --- |
| `README.md` 能力表 | “必录 26 字段” → 36 字段 + 三类行与诊断列;新增“逻辑调用统计”一行 |
| `README.md` 新小节 | 《1.3.5 逻辑调用统计与失败诊断》:`call_stats` 读法、**SQL 迁移五项**、归因查询、存储侧升级 |
| `README.md` cap 覆盖面 | 补“`error_body` 沿用 `summarize_body` 上限、结构化说明自带限长,**两者都不在 cap 覆盖内**” |
| `ARCHITECTURE.md` §7.8 | 必录字段 26 → 36(十列逐个列出);新增“逻辑调用十列”段(列语义表 + 不变量 I3/I4 + `operation` vs `exc.operation` 两个语义 + 装配闸);cap 段补两处诊断文本不在覆盖面 |
| `CHANGELOG.md` | 新建《未发布》:公共面四项变更表 + 下游必须做的事(点名 `WHERE event_kind` 与装配期报错) |
| `.env.example` | `PGW_TELEMETRY_TEXT_CAP` 块补 1.3.5 覆盖面例外 |
| `schemas/llm-calls.md` | 标题 26 → 36 字段;十列逐行登记;新增《三类行与失败归因口径》(含归因 SQL 与迁移四条) |
| `metrics/call-telemetry-coverage.md` | 新增《1.3.5 三类行与逻辑调用覆盖》;live 基线列**不写伪百分比** |
版本号与发布步骤**未动**(任务边界);`pyproject.toml` / `__init__.py` 仍为 1.3.4。
### 5.4 本轮收口验证
| 检查 | 命令 | 结果 |
| --- | --- | --- |
| 静态 | `make check`ruff format+lint + import-linter | 见 §7 实跑记录 |
| 全量单测 | `pytest tests/unit -q` | 见 §7 |
| 目标集成 | `pytest tests/integration/test_postgres_telemetry.py -q` | **30 passed** |
## 6. 剩余缺口(不归本任务,不自称已关)
| 缺口 | 说明 |
| --- | --- |
| 独立验证 | 未派全新上下文 verifier(合并前硬门,由父会话前台派) |
| slow / e2e | 未跑,属发布清单第 4 步(CLAUDE §4.4.1 |
| live 覆盖基线 | `metrics/call-telemetry-coverage.md` 的实际基线列仍待首次生产运行填入 |
| 下游自建 recorder | 装配闸只能证明形状可被接受,证不了函数体真的落这些列(已写进 README/CHANGELOG |
## 7. 环境噪音记录(不是缺陷)
自动检查器用**系统解释器 Python 3.13.9**(无 `redis`/`pydantic`/`httpx`/`loguru`/`asyncpg` 等依赖),
持续报 `test_client.py` 2 项失败与大量 "Import could not be resolved"、`StrEnum is unknown import symbol`
已核实为环境问题、非本轮引入:
- 失败根因是 `ModuleNotFoundError: No module named 'redis'`optional extra),本轮 diff 对 redis 零改动;
- 在**未改动的 HEAD** 上用同一系统解释器复跑,同样 2 failed / 90 passed
- 项目强制环境 `conda run -n PolyGateway`Python 3.12.13)下:`test_client.py` 98 passed、全量 1467 passed。
判据以 CLAUDE.md §2 规定的 conda 环境与 `make check` 为准。
**T4 轮次同样现象(已逐项复现并关闭)**:检查器报 `tests/integration/test_postgres_telemetry.py` “2/3 failed”
与 6 处 `Import "asyncpg" could not be resolved`。根因与上同:`asyncpg>=0.29` 是 optional extra
`pyproject.toml:25``postgres`),检查器解释器里没装。已做确定性复现:
- 那 3 条恰是本文件**仅有的不依赖真实 PG 的用例**,其中 2 条要造 `PostgresRecorder`
-`/home/iomgaa/miniconda3/bin/python`(无 asyncpg)跑这 3 条:**2 failed / 1 passed / 0.09s**,与检查器报告逐字吹合;
- 同 3 条在 `conda run -n PolyGateway` 下:**3 passed**;整文件 **30 passed**(真实 PG);
- 被标记的 6 行均为**本轮未触及的旧行**,本轮只新增 1 处同款函数内 `import asyncpg`
未为此修改代码:给旧行加 type-ignore 属任务外改动(反 gold-plating),且并非真修复。
## 8. 1.3.5 发布准备(2026-09-09,唯一 writer 会话)
范围仅**发布准备文档与版本号**:README 安装下界、CHANGELOG 定版、两处版本、本节记录。生产代码与测试**零改动**(`git diff` 只有 `CHANGELOG.md` / `README.md` / `pyproject.toml` / `__init__.py` 各 1 行,外加本文件追加的这一节)。未 merge/push/tag/构建/上传。
### 8.1 先查远端占用(改文件之前)
| 检查 | 结果 |
| --- | --- |
| `git ls-remote --tags origin` | 最高 `refs/tags/v1.3.4`**无 v1.3.5**;远端分支只有 `main``af57f93`)与 `docs/1.3.4-release-evidence` |
| 本地 tag | 同样止于 `v1.3.4` |
远端未被占用是**本次时点**的事实,父会话真正 push/tag 前仍须复查以防竞态。
### 8.2 版本与文档数字(数字一律实测)
| 项 | 结果 |
| --- | --- |
| 两处版本 | `pyproject.toml``src/polygateway/__init__.py` 同为 `1.3.5`;由 `tests/unit/test_package.py` 机械断言一致(**6 passed** |
| README 安装下界 | `>=1.3.4,<2``>=1.3.5,<2`(本版含装配期形状闸与 SQL 口径迁移,旧下界会让下游装到不含新列的包) |
| README 迁移醒目度 | 《1.3.5 逻辑调用统计与失败诊断》为顶层小节且带 `[!WARNING]`(点名"失败行变多、旧失败 SQL 会多数、自建 recorder 装配期报错"),能力表"逻辑调用统计"一行直链该锚点;沿用 1.3.4 的同款版式,未重排既有 1.3.4 迁移节 |
| CHANGELOG 定版 | `## 未发布``## 1.3.5(2026-09-09)`,日期取自本机 `date +%F` 实际值,正文未改 |
| 字段数复核 | `inspect.signature(TelemetryRecorder.record_llm_call)` 去 self **36 参**`len(schema.COLUMNS) == 36`、探针实测物理列 **37**——READMECHANGELOG 的 3637 与实测吻合,非凭记忆 |
README 静态数字未发现错误,故未做任何顺带修改。
### 8.3 复用既有证据的适用边界(不拿旧证据顶替本版统计)
| 证据 | 是否复用 | 边界 |
| --- | --- | --- |
| 独立验证(全新上下文 verifier,父会话前台派) | 复用结论:**0 阻塞** | 审的是本分支 `b4812e1` 的 1.3.5 变更面;本会话未重复派子代理,也未把它当作 live 覆盖或发布后检查的替代 |
| 本版最终全套件 `make test` | **本版实跑,不复用** | `final-gates/make-test.log/.exit`**1605 passed / 23 skipped / 108 deselected / 95%exit 0** |
| 本版真实 PG 集成 | **本版实跑,不复用** | `final-gates/pg-telemetry-verbose.log/.exit`**30 passedexit 0**(含新增四项列兼容验收) |
| 本版 unit | **本版实跑** | 本轮版本号改动后复跑 `pytest tests/unit -q`**1469 passedexit 0**`release/unit.log/.exit` |
| 1.3.4 模型矩阵/逐型号推理证据 | **按适用条件复用** | 1.3.5 对 `thinking.py`、能力表、wire 片段、缓存 key 公式、重试/限流/熔断语义**一字未改**(§4 已逐文件确认),故 1.3.4 的型号级结论在其原有边界内继续成立——连同它的 FAIL/UNKNOWN/不可达/缺轮**一并继承**,不因本版而升格。它**不能**充当 1.3.5 新增 API/schema 的证据,也不提供本版的测试统计数字 |
| 1.3.4 的全模型矩阵重跑 | **不跑** | 本版未改推理路径;用户已批准不再补全模型矩阵。本会话不发全型号请求 |
本版**改变的公共面**`CallStats` 与四类响应新字段、端口 10 新参、遥测 10 列与 `terminal_failure` 行)全部由新写的离线真链路用例(三个 client + MockTransport 直到落库)、真实 SQLite 断言、真实 PG 30 项承担,见 §2、§2.4、§5.1。
### 8.4 本轮受影响路径的真实现场(并发 1、超时不压、无全模型请求)
| 亲跑 | 命令与证据 | 结果 |
| --- | --- | --- |
| 真实网关冒烟(复用既有 e2e,不另造工具) | `pytest tests/e2e/test_smoke_gateway.py -m slow -v`tmux `pgw135-smoke``release/smoke-gateway.log/.exit` | **4 passedexit 0**(流式/非流式/结构化 json/结构化模型四节点,9.55s) |
| 有界现场探针(**恰 1 次**真实调用,全程走库) | `release/probe_call_stats.py``release/probe-call-stats.log/.exit`tmux `pgw135-probe` | **exit 0**`MiniMax-M3`/源 `minimax_1``call_stats.attempts=1``total_latency_ms=2581` ≥ 单次尝试 `2562``logical_call_id` 与落库行逐字一致;临时 SQLite 物理列 **37**、写入面 **36**、恰 1 条 `event_kind='attempt'` 行、`operation='chat'``scope='LLM'`、逻辑两列与四个诊断列全 NULL |
探针脚本只落在 `tests/outputs/135/release/`(该目录 gitignore,不入库),不新增生产代码、测试或平台设施,未发裸 HTTP,沿用 `.env` 现有超时与单源配置。
### 8.5 提交前收口门
| 门 | 证据 | 结果 |
| --- | --- | --- |
| `make check` | `release/check.log/.exit` | **exit 0**:94 文件格式通过、ruff 通过、import-linter **1 kept / 0 broken** |
| 包单测(两处版本一致) | `release/package.log/.exit` | **6 passedexit 0** |
| 全量 unit | `release/unit.log/.exit` | **1469 passedexit 0** |
23 项 skip 的理由已逐条留档(`final-gates/skip-reasons.log`):17 项 redis 时间语义由 integration 变体覆盖、6 项需实验室语料 `data/soak/chs_images`**skip 不计通过**。conda 启动器既有 `RequestsDependencyWarning` 保留,不宣称零告警。
### 8.6 尚未执行(不得当成已完成)
merge、push、`git tag -a v1.3.5``python -m build``twine check``upload``pip download` 解包验证、Gitea Release 与包页面/仓库关联检查**全部未执行**;`pytest -m slow` 的其余 e2e(本轮只跑了 `test_smoke_gateway.py` 四节点)与 live 覆盖基线同样未跑。§6 的四项缺口除"独立验证"已由父会话关闭外,其余保持开启。