Files
PolyGateway/research-wiki/reviews/2026-09-11-138-verifier.md
T
iomgaa a2a00c0ef2 test: sync PG telemetry integration helpers with 1.3.8 stall columns
_minimal_fields and the pre_135 acceptance dicts missed the two new
columns, KeyErroring against migrated 39-column physical tables
(caught by the pre-merge integration gate; production path is
signature-gated and unaffected). Verifier gaps P2-1/P2-2 closed:
full suite 1764 passed, PG 30 passed, lint green.
2026-09-14 03:01:40 -04:00

10 KiB
Raw Blame History

1.3.8 streaming-stall 合并前独立验证报告(verifier,全新上下文)

  • 仓库: /home/iomgaa/Projects/PolyGateway;分支 feature/1.3.8-streaming-stall
  • HEAD 核实: .git/HEAD → ref: refs/heads/feature/1.3.8-streaming-stall;.git/refs/heads/feature/1.3.8-streaming-stall = 8489b8ea50308ca75e0876d293fef7feddf9b086(即 8489b8e)✓
  • 能力声明(重要): 本 verifier 会话只有 read/grep/find/ls 四类只读工具,无 shell 执行能力(角色纪律: 禁止 shell、禁止写文件)。因此动作 b(套件重跑)、c 的重跑部分、d(行为抽测)、e(make lint 重跑)无法由我本人执行;我已亲自读取实现方落盘的全部日志与全部相关源码做静态+证据核实,运行时必须由 supervisor 按文末清单补跑后方可合并。pi-lens/LSP 诊断未作为任何判据(指令 g)。

结论

有条件通过(OK with notes): T1–T5 全部声明在源码与落盘证据两个维度均核实为真;红绿证据链真实(红日志含真实失败因、绿日志计数单调递增且与提交序一致)。两个证据缺口(见发现 P2-1/P2-2)必须补跑: integration 套件无任何落盘证据、且本会话无法亲跑任何运行时命令。

逐声明验证证据

a) 提交序列(静态替代 git log)

.git/logs/HEAD reflog 末端逐行亲读,分支自 main a28d944(1.3.7)拉出后序列:

提交 主题 对应
03cbf4c / 97de410 / 929d60e / 6f3a620 docs: 设计 + 评审勘误 + 计划 文档前置
901a193 feat: content event + stall accounting to transport port T1
53bcf43 feat: generation_ms 净口径重定义 T2
e0b0be0 feat: stall_ms/stall_count 遥测列 T3
2ac05a2 feat: opt-in 中段沉默对冲 T4
ca8743b docs: 净口径与中段对冲文档 文档
8489b8e(HEAD) docs: resolve branch review note(content_event 接线终态注释) 文档

与声明序列(901a193/53bcf43/e0b0be0/2ac05a2/ca8743b/8489b8e + 文档提交)逐项一致 ✓

T1 端口契约 —— 成立

  • src/polygateway/ports.py Transport.complete: kw-only 参数列含 content_event: asyncio.Event | None,无默认值(与 first_token_event 同一约定,docstring 明示 1.3.8 #27/#28)✓
  • src/polygateway/types.py:619-628 TransportResult.stall_ms: float|None=None / stall_count,docstring 钉死 None(无从观测)vs 0.0(观测过零沉默)区分、尾部不计 ✓
  • types.py:683-689 SourceConfig.stall_threshold_ms: int = 1000,docstring 明示"严格大于才计";types.py:734-743 _validate_stall_threshold 拒 bool/非 int/≤0 ✓
  • transports/openai_compat.py:88-109 纯函数 _stall_from_chunk_times: len<2 → (None,None)(L98-99);gap_ms > threshold_ms 严格大于(L106);无超阈 → (0.0,0.0)(L109);只遍历相邻对(zip times/times[1:])→ 尾部天然不计 ✓
  • openai_compat.py:608-618 流式循环: 每增量 chunk chunk_times.append(now) + content_event.set();L634 由 _stall_from_chunk_times(chunk_times, source.stall_threshold_ms) 派生两值进 TransportResult(L643-644)✓
  • 非流式 _complete_once(L698-769): 收两事件永不置位,TransportResult 不传 stall 字段(默认 None)✓

T2 净口径 —— 成立

  • types.py:331-336 CallStats.stall_ms/stall_count: int|None = None;types.py:322-328 generation_ms docstring 重定义为净口径(含 clamp、还原公式、排除条款)✓
  • 唯一换算点 middleware/retry.py:671-681 _record_winner_sample: net_ms = max(0, wall_ms - (stall_ms or 0))(clamp 到 0 ✓);None 透传不退减 ✓
  • 取整唯一处 retry.py:376-378(墙钟/stall 同点 int 化,墙钟=generation_ms+stall_ms 可还原成立)✓
  • embedding/ocr 路径:record_generation 的 stall 参数默认 None(types.py:386-387),既有调用点不传 → 恒 None ✓

T3 遥测 —— 成立

  • telemetry/schema.py COLUMNS 亲数 38 项(末两项 stall_ms/stall_count,L227-228);SQLITE_DDL(L66-67)与 PG_DDL(L110-111)均含两列;SQLITE_BACKFILL(L146-147)与 _PG_BACKFILL_DECLS(L175-176)列序对齐 ✓
  • ports.py TelemetryRecorder.record_llm_call 38 个 kw 参数(末两个 stall_ms/stall_count,无默认值)✓
  • 迁移幂等: SQLite sqlite.py:125-143 先 PRAGMA table_info 探测、column in existing: continue 跳过、duplicate column 竞态视为成功;PG postgres.py:352-369 同款先探测后 ALTER ✓
  • 取消输家行 stall 恒 None: retry.py CancelledError 分支 self._emit(..., error=label) 不传 stall → _emit 默认 None(L774-775);emitter emit_cache_hit/emit_terminal_failure 显式 None(middleware/telemetry.py:515-516,570-571)✓

T4 中段对冲 —— 成立

  • config.py:201 hedge_stall_after_s: float|None = None(默认关闭);_load_hedge(L801-807)读 {SCOPE}__HEDGE__STALL_AFTER_S;_load_stream(L756-774)读 {SCOPE}__STREAM__STALL_THRESHOLD_MS;STREAM 已进 _RESERVED_SEGMENTS(L57)✓
  • 三守卫唯一事实源 check_hedge_assembly(L810-912): stall ≥ 有效最小值(inter_token_timeout_s or timeout_s 回落)→ ValueError(L888-894);stall ≥ call_deadline_s → ValueError(L895-899);单源 warning(L900-906);两阈值皆 None 才早退(审查 B2)✓;GatewaySettings.__post_init__ 与 GatewayClient.__init__ 共用同一份(config.py:391-401 / client.py:255-263)✓
  • Embedding/OCR scope 对冲键 warning: _warn_hedge_keys_on_non_stream_scope(config.py:915-927),被 EmbeddingSettings(L950)与 OcrSettings(L996)__post_init__ 调用 ✓
  • 编排 retry.py: 仅 stall+非流式 → 逐字单路(L290-301);两阶段触发窗 _past_hedge_window(L591-652):阶段 1 首 token 前走 AFTER_S、仅 stall 时须等首 token(防 pre-TTFT 误吞);阶段 2 循环 asyncio.wait(timeout=stall_after_s, FIRST_COMPLETED) + N3① 超时后复检 content_event.is_set() 防误触发 + N3② check→clear 无 await;触发后 FIRST_COMPLETED 竞速(L538-539);对冲路恒 content_event=None(L533);输家 cancel() 前置 _HEDGE_LOSER_ATTR 标记(L568-571),_attempt 取消分支据此标 hedge_cancelled 且不 record_failure(不喂熔断,L414-429)✓
  • 三层可调: env 键 → GatewaySettings.stall_threshold_ms(config.py:206)→ from_settings 恒分发(client.py:543);直传 GatewayClient(stall_threshold_ms=...) 经 dataclasses.replace 分发且重跑 __post_init__ 校验(client.py:264-270)✓

T5 默认关闭回归 —— 静态成立 + 落盘证据

  • 代码门: retry.py:279 if self._hedge_after_s is None and self._stall_after_s is None: → 单路 first_token_event=None, content_event=None(注释明示"逐字 1.3.7 单路路径,默认关闭回归门据此成立")✓
  • 落盘: tests/outputs/138/t4/green-unit.log 末行 1688 passed, 17 skipped in 54.90s(与声明 5 的 1688 一致)✓

c) 红绿证据真实性抽查 —— 真实

  • t1 red: ImportError _stall_from_chunk_times(功能不存在); green: 260 passed ✓
  • t2 red: 6 failed(CallStats 无 stall_ms 属性、record_generation 拒 stall kw、净口径 5000≠1800、clamp 5000≠0 等精确断言); green: batch 40 passed / unit 1608 / unit+contracts 1669 ✓
  • t3 red: 14 failed(COLUMNS 缺两列、$ 占位符 36≠38、真实 SQLite no such column: stall_ms、幂等补列断言红); green: batch 238 / unit 1673 ✓
  • t4 red: 16 failed(RetryMW 拒 stall_after_s kw、settings 无 hedge_stall_after_s、STREAM 段造出假源 stream_1、三守卫 DID NOT RAISE);green: batch D 169 passed、contracts 61 passed ✓
  • t4/t5 lint: ruff All checks passed! + import-linter Contracts: 1 kept, 0 broken ✓
  • 计数链单调: 1602(T1)→1608(T2)→1673(T3)→1688(T4),与逐批新增用例一致;红日志失败因均为"功能尚不存在"类,非环境噪声 ✓

f) 文档一致性 —— 一致

  • CHANGELOG 未发布段: 行为变更(口径重定义+还原公式+None 条款+取消输家恒 NULL)、遥测两列(36→38,旧行不回填)、中段对冲(默认关闭/独立生效/三守卫/19.0% 触发率诚实标注)✓
  • README §"1.3.8 生成时间口径与中段沉默对冲"(L115-140): generation_ms 定义句"净生成时间 = 赢家墙钟 − Σ(> STALL_THRESHOLD_MS 的相邻增量 chunk 沉默)"与代码 max(0, wall-stall) 一致;能力表"遥测必录 38 字段"(L26)、逻辑调用统计段(L27)、配置表两键(L137-138)与代码一致 ✓
  • .env.example L83-89: STREAM__STALL_THRESHOLD_MS=1000 与 HEDGE__STALL_AFTER_S 两键注释与守卫文案一致 ✓
  • ARCHITECTURE.md §7.2(L504/506)净口径+两阶段触发窗登记段、§7.5(L569/571)38 字段与 stall 两列登记段,与代码行为一致 ✓

发现列表

  • P2-1(证据缺口,合并前必须补): tests/outputs/138/ 下无任何 tests/integration 运行证据;动作 b 要求 pytest tests/unit tests/contracts tests/integration。本 diff 直接触及 PG 遥测列(tests/integration/test_postgres_telemetry.py 的 _EXPECTED_COLUMNS 已含 stall 两列、占位符/列序断言只在真实 PG 上有效)与治理栈,integration 未跑 = PG 侧补列/写入路径只有静态与 SQLite 证据。
  • P2-2(流程限制): 本 verifier 无 shell,动作 b/c 重跑/d/e 全部无法亲跑;现结论中"套件绿"的依据是我亲读的实现方落盘日志(文件真实、失败因真实、计数链自洽),非我本人执行。合并前须由 supervisor 补跑下方命令清单,输出落 tests/outputs/138/verify/。

supervisor 必须执行的命令(全部 conda run -n PolyGateway,禁管道,落 tests/outputs/138/verify/)

  1. git log --oneline main..HEAD(核对序列;我已于 reflog 静态核实,仍建议权威跑一次)
  2. pytest tests/unit tests/contracts tests/integration -q(记 exit code 与计数;unit 期望 1688,contracts 期望 61)
  3. 关键用例重跑(现绿确认): pytest tests/unit/test_retry.py::TestGenerationMs tests/unit/test_stall_hedge.py::TestStallHedgeTrigger::test_stall_recheck_prevents_false_trigger tests/unit/test_stall_hedge.py::TestStallHedgeWatchdogAndFailure::test_watchdog_kills_primary_hedge_leg_survives tests/unit/test_telemetry.py::TestStallColumns -q
  4. 行为抽测 d①-③(流式假传输端到端 stall/净 generation_ms;仅 STALL_AFTER_S 下沉默触发对冲且 hedge_won;两键不设时 hedges=0/hedge_won=False)
  5. make lint

合并判定: OK with notes —— 静态与落盘证据全绿;P2-1/P2-2 补跑通过后即可合并,若补跑出现任何红则转 BLOCK。