style(server): gofmt 注释规范 + init.sql 补 parent_session_id + 新增 ratelimit 判据

## 主体是 gofmt

`server/` 下 20 个文件的改动里,**142/147 行是注释与缩进** ——
Go 1.19+ 的 gofmt 把块注释里的 `//   x` 规范成 `//\tx`。
功能零变化。

## 一处实质:init.sql 补 parent_session_id

`ALTER TABLE sessions ADD COLUMN IF NOT EXISTS parent_session_id
 TEXT REFERENCES sessions(session_id)`

远端已在 `db/migrate.go` 的 `sqliteAddColumns` 结构迁移通道里加了同一列;
init.sql 是**全新库**的建库脚本,缺这列会让新库少一个字段。

- `IF NOT EXISTS` ⇒ 与结构迁移通道幂等,两者并存无害
- 默认空 = 根节点;**不据历史数据猜父子**(猜错会让线索拓扑失真,比没有更糟)

## 新增 handler/ratelimit_test.go(162 行)

速率限制的判据。此前这一块没有独立判据。

## 验证(本机实跑,非推断)

    go build ./...                              通过
    go vet ./internal/{handler,repo,db,sse}/   通过
    go test -count=1(handler/repo/db/sse/blob)
      handler  41.3s  ok
      repo    101.9s  ok
      db       1.1s  ok
      sse      0.01s ok
      blob     0.08s ok

`-count=1` 是刻意的:第一遍跑出来全是 `(cached)`,那种绿灯不算数。
This commit is contained in:
2026-10-05 09:26:07 +08:00
parent be9bfd3cd1
commit a8d6811cd7
21 changed files with 246 additions and 69 deletions

View File

@ -75,8 +75,10 @@ func TestAgentPingPongCountsConsecutive(t *testing.T) {
// ★ 核心语义:**人类插一句话,计数归零**。
//
// 这一条决定了这个判据会不会误伤正常用法:
// · 人类说一句 → Agent 回十封 → 人类再一句 ⇒ 每次都在阈值以下
// · 没有人说话 → 一直涨 ⇒ 到阈值拒绝
//
// · 人类说一句 → Agent 回十封 → 人类再一句 ⇒ 每次都在阈值以下
// · 没有人说话 → 一直涨 ⇒ 到阈值拒绝
//
// 与 maxRelayHops 的"连续"同构 —— 两条防线在这个语义上必须一致,
// 否则同一个现象在两处得到不同的结论。
func TestAgentPingPongResetsOnHuman(t *testing.T) {
@ -145,13 +147,13 @@ func TestAgentPingPongNotResetByAgents(t *testing.T) {
被 CountTrailingAgentPingPong 一并计入「Agent 互发」计数。它同样满足
「发件方 Agent + 收件方 Agent + 无人类」,于是:
① 退信把计数顶到 8 ⇒ 闸被**插件自己**触发;
② 闸在 send 入口、先于 relay 分支 ⇒ 退信本身也被 403;
③ 退信发不出去 ⇒ 发件人只看到「模型未产生回复」(桥的 sendFailureReply
同样发不出,真因被吞);
④ 被拒的尝试不产生新邮件 ⇒ 计数永不回落 ⇒ **会话永久死锁**:
模型回信 → 403 → 退信也 403 → 发件人重发 → 模型再回 → 再 403。
生产上该会话 09-29 22:01 至 09-30 10:11 被 403 拒了 7 次。
① 退信把计数顶到 8 ⇒ 闸被**插件自己**触发;
② 闸在 send 入口、先于 relay 分支 ⇒ 退信本身也被 403;
③ 退信发不出去 ⇒ 发件人只看到「模型未产生回复」(桥的 sendFailureReply
同样发不出,真因被吞);
④ 被拒的尝试不产生新邮件 ⇒ 计数永不回落 ⇒ **会话永久死锁**:
模型回信 → 403 → 退信也 403 → 发件人重发 → 模型再回 → 再 403。
生产上该会话 09-29 22:01 至 09-30 10:11 被 403 拒了 7 次。
正确形状:relay 邮件归 CountTrailingRelayHops(阈值 5,更严)管,
不挤占「模型主动互发」的额度(阈值 8)。两条闸独立计数、互不侵占。