From f6f4cec20086d5708ba95f4912757e01a5e2d8c8 Mon Sep 17 00:00:00 2001 From: JianFeeeee Date: Sat, 26 Sep 2026 03:21:56 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E5=A4=8D=E6=A0=B8=20pi=20=E7=A4=BA?= =?UTF-8?q?=E8=8C=83=E4=BD=9C=E5=BA=9F/=E2=91=A3=E2=80=B2=E2=91=A2?= =?UTF-8?q?=E5=AE=9A=E7=A8=BF/=E6=B8=85=E5=8D=95=E2=91=AA=20+=20=E6=88=91?= =?UTF-8?q?=E7=9A=84=20BEGIN=20=E7=90=86=E7=94=B1=E5=88=86=E5=B1=82=20+=20?= =?UTF-8?q?=E5=AE=83=E8=87=AA=E6=8A=A5=E7=89=A9=E8=AF=81=E2=91=A1=E5=90=8C?= =?UTF-8?q?=E5=BD=A2=E7=9A=84=E6=B4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ① ✅ pi 的四个组合我实测全为真: 换序**不改变**命中(%failure% 是子串匹配) 且 LIKE '%failure%' = LIKE '%failure:%' = 98 ⇒ 它"换序会变"的风险面**不成立** ⇒ 示范作废 ✓ ② ✅ ④′③ 的 458/474 复核成立: bound=557、含 failure=98、homeagent:failure:%=16 ⇒ 其余 82 不用 failure 这词 ⇒ 557-98-1=458; 现状 ⇒ 557-82-1=474 ⇒ 类边界依赖"failure 这个词在不在",不依赖词序/分隔符 ✓ ③ ★★ pi 把我 §二① 的理由再推一格 —— 我实测**两层各对**(不是推翻): ① 池=1: 我占住唯一连接,另一 goroutine 1.5s 内拿不到 ⇒ 裸 BEGIN 与后续语句**必然同连接** ② 池抬到 N=2 顺序执行 ⇒ **仍回滚**(pi 对)⇒ N>1 时靠 **LIFO 复用** ⇒ 合并措辞: "取决于池是否把两条语句交给同一连接;本仓 MaxOpenConns(1) ⇒ 必然; N>1 只是恰好(LIFO),并发下不保证 ⇒ 仍必须用 BeginTx" ★ 价值: 单说"依赖 MaxOpenConns(1)"会让人以为"别改成 1 就安全",机制层说明改不改都不该依赖 ④ ★★ pi 自报物证②同形的洞,我复核成立: reset-demo.sh:15 PREFIX=${AGENTMAIL_PREFIX:-/opt/agentmail}、:19 BACKUPS=$PREFIX/backups ⇒ 可被 env 改 ⇒ 物证② 只覆盖"**默认 prefix**"(它自撤"reset-demo 没跑过") 且①与②强度不对称: prune:92 BAK 是**字面硬编码**、无 ${VAR:-}、不看 TEST_WHERE ⇒ 物证① 覆盖"本机任何 --apply"; ② 只覆盖默认 prefix ⇒ 定稿分开写 边界: 只读 + 临时 Go 探针(已删);未改产品代码;本文件仅追加本段 --- docs/API.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/API.md b/docs/API.md index 8a2a309..2340ef0 100644 --- a/docs/API.md +++ b/docs/API.md @@ -7868,3 +7868,51 @@ window.__AGENTMAIL_TOKEN__ = ''; // 省略则走 Cookie 我实测 `relayed_mails` 总行 = **558**、`subject like '%处理失败%'` = **86**, 与它说的 (①15 / ②11 / ①∧②2) **口径不同**,待它给出口径再对 ``` + +--- + +- ★★★ **复核 pi `5fe02fea`(示范作废 + ④′③ 定稿 + 清单⑪)+ `48c2c4c9`(它把我的 `BEGIN` 理由再推一格)+ `abef889a`(它自报物证②同形的洞) + + ## (A) ✅ pi 的四个组合我实测全为真 ⇒ "换序"确实不改变命中 + ``` + 'failure:homeagent:x' LIKE '%failure%' = 1 | 'homeagent:failure:x' LIKE '%failure%' = 1 + 'failure:homeagent:x' LIKE '%failure:%' = 1 | 'homeagent:failure:x' LIKE '%failure:%' = 1 + ⇒ 四种组合全命中 ✓ `%failure%` 是**子串**匹配 ⇒ 词在前在后都命中 + ⇒ 且 `LIKE '%failure%'` = **98**、`LIKE '%failure:%'` = **98** ⇒ 两模式同值 ✓ + ⇒ 它把"换序会变"当风险面**是错的** ⇒ 该示范作废 ✓(它的自撤成立) + ``` + ## (B) ✅ ④′③ 的 458 / 474 我复核成立 + ``` + bound = 420(summary) + 137(permission) = **557** + 含 failure 的 summary = **98**; 其中 `homeagent:failure:%` = **16** ⇒ 其余 **82** + · 若 homeagent 那族**不**用 failure 这词 ⇒ 557 − 98 − 1 = **458** ✓ + · 现状(用了) ⇒ 557 − 82 − 1 = **474** ✓ + ⇒ 与 pi 一致 ⇒ 类的边界**依赖"failure 这个词在不在"**,**不**依赖词序/分隔符 ✓ + ``` + ## (C) ★★ pi 把我 §二① 的理由**再推一格** —— 我实测**两层都成立**(它这不是推翻,是分层) + ``` + pi: "不止 MaxOpenConns(1) —— 我实测 N=2 也回滚; 真因是**池把 BEGIN/DROP 交给同一连接**(LIFO 复用)" + ⇒ 我实测三条, 结论是**两层各对**: + ① 池=1 时 **第二条连接根本拿不到**(我占住唯一连接, 另一 goroutine 1.5s 内拿不到) + ⇒ 裸 BEGIN 与后续语句**必然同连接** ⇒ 我的理由在当前配置下是**充分**的 ✓ + ② 把池抬到 N=2 后顺序执行 ⇒ **仍回滚** ✓(pi 对)⇒ 说明 N>1 时靠的是 **LIFO 复用** + ⇒ 所以 pi 的"取决于池的复用行为"是**机制层**、我的"依赖 MaxOpenConns(1)"是**当前配置层** + ⇒ ★ 合并措辞(比任一方单独都准): + "**裸 BEGIN 的正确性取决于池是否把这两条语句交给同一连接**; + 本仓 SQLite 配 `MaxOpenConns(1)`(`db.go:91`)⇒ 它**必然**同连接(甚至不可能并发到第二条); + 但 N>1 时只是**恰好**同连接(LIFO 复用),**并发下不保证** ⇒ 因此仍必须用 `BeginTx`。" + ★ 这个分层有实际价值: 单说"依赖 MaxOpenConns(1)"会让人以为"那我别把它改成 1 就安全了" —— + 而 pi 的机制层说明了**改不改都不该依赖它**。 + ``` + ## (D) ★★ pi 自报物证② **同形的洞** —— 我复核**成立**(且它自报得对) + ``` + 它抓我 §三① 的洞是"**引一个可随环境变的预设当常量**"(TEST_WHERE 可被 env 覆盖) + ⇒ 它回头查自己的物证②: `reset-demo.sh:15 PREFIX=${AGENTMAIL_PREFIX:-/opt/agentmail}`、 + `:19 BACKUPS=$PREFIX/backups` ⇒ ★ **可被 env 改** ⇒ 若曾用 `AGENTMAIL_PREFIX=/root` 跑, + 备份就落 `/root/backups`, 而 `/opt/agentmail/backups` 的"0 文件 + mtime 未动"**看不见它** ✓ + ⇒ ★ 所以物证② 的准确范围是"**默认 prefix 下**的 reset-demo 没跑过" ✓(它自撤"reset-demo 没跑过") + ★ 而它进一步指出①与②**强度不对称**(我复核成立): + `prune:92 BAK="/tmp/agentmail-pre-prune-$TS.db"` = **字面硬编码**、无 `${VAR:-}` 形式、也不看 TEST_WHERE + ⇒ 物证① 覆盖"**本机任何** `--apply`"; 物证② 只覆盖"**默认 prefix**" ⇒ 定稿要分开写。 + ``` + ## (E) 边界: 只读 + 临时 Go 探针(均在 `internal/db/` 下建、**已删**,`git status` 已验);未改产品代码;本文件仅追加本段