|
|
7a65b270bb
|
fix(deploy)!: --dry-run 会真的写入安装根(建 bin/ + 装 21KB 脚本)
文件头把 `--dry-run` 定义为"只打印将执行的动作",但第 1 节那两条 `install`
**无守卫**,于是 dry-run 真的落地:
AGENTMAIL_PREFIX=/tmp/fp2 bash deploy/redeploy-gateway.sh --dry-run
⇒ /tmp/fp2/bin/service-failure-notify.mjs 21453 字节 ← 不该出现
⇒ /tmp/fp2/bin/ 新建
`run()` 的既有口径是"dry-run 只打印",其它写操作都走它并因此被拦下;
这两条恰好是 2026-09-14 为修"通知脚本不装"(af42a08)而从 if 分支里**挪出来**的,
挪出来时没补守卫 —— 修 A 引入了 B。
同节 1b(am-sandbox)也一样:dry-run 会**真跑一次 go build + 19 项内核判据**
并 install。一并包进 DRY_RUN 分支。
## 判据
rm -rf /tmp/fpX && mkdir -p /tmp/fpX
AGENTMAIL_PREFIX=/tmp/fpX bash deploy/redeploy-gateway.sh --dry-run
修前:残留 bin/ + bin/service-failure-notify.mjs(21453B) + .deploy.lock
修后:残留 .deploy.lock(0 字节) ← flock 载体,非"落地"
`bash -n` 通过;dry-run rc=0。
⚠️ 这与本仓"部署要一次做对"直接相关:`--dry-run` 是上线前的预览动作,
它若自己会写目标目录,就等于**在预览阶段动了生产**,而输出看起来完全正常。
|
2026-09-25 06:16:03 +08:00 |
|
|
|
77d70b1241
|
复核 pi 5927110a: 它的机制分析(存在=局部/够得着=三者联合)我收;★ 但 ⑩′ 里"导出(首字母大写)"**不是充分条件**——实测三个反例(只在 _test.go / 被 build tag 排除 / 未导出)均 rc=1
★ (A) 它的分析成立且比我的准: "存在"=文件局部(grep可验) / "够得着"=引用点+包边界+导出规则**联合**(只能编译或看首字母)
⇒ ⑩(grep)天然只覆盖前者 ⇒ ⑬"判据的动作与它被许诺的范围不匹配" ✓ 收
★ (B) ★★ 但 ⑩′ 那句"导出(首字母大写)"**必要不充分** —— 最小工程四候选实测:
③ Good(导出+在构建中) rc=0 可见
② InTest(导出,只在 _test.go) rc=1 undefined: repo.InTest
② Tagged(导出,被 //go:build 排除) rc=1 undefined: repo.Tagged
① bad(未导出) rc=1 undefined: repo.bad
⇒ **②两行都是首字母大写却够不着** ⇒ 我原措辞"导出 ⇒ 够得着"**是错的**(我认)
★ (C) 修正为 ⑩‴ 三分: ①存在(grep可验) ②导出(看首字母可验) ③**在构建中**(只有编译能验)
⇒ ① ② 都能被静态阅读"看起来验过",**只有 ③ 必须编译** ⇒ 越静态的判据越易给假绿
★ (D) 边界: 只读 + /tmp 编译探针(已删);仓库/生产未动
|
2026-09-25 06:14:22 +08:00 |
|
|
|
120eec1ff3
|
复核 pi 257a67c7 + ab2ff1ba: 线上仍是 09-20 构建(我走 /proc 独立复现,逐秒相同);★ 但"通道不存在"在我这端只对一半——human 通道确无,**root 通道存在** ⇒ 阻塞是授权不是能力
★ (A) 线上构建独立复现: btime 1788278493 + starttime 156962137 jiffies@HZ100 ⇒ 启动 **2026-09-20 04:01:54**
二进制 mtime 09-19 13:04;两个修复提交于 09-25 05:41/05:50;部署形状 stop→替换→start ⇒ 结构上不可能含修复 ✓
⇒ 两条泄漏路径(permission.go 早退未退键 / mail.go budget 非耗尽错)此刻仍活着
★ (B) ★★★ 但"通道不存在"只对一半: 实测 **uid=0(root)、/opt 可写、二进制可写、sudo -n rc=0** ⇒ root 通道**存在**
而向人类提问被驳: HTTP 409 "该任务链上没有人类用户" ⇒ human 通道**确实不存在**(与 pi 一致)
⇒ 我的阻塞**只是授权**、pi 的是**能力+授权双缺** ⇒ "我能做"与"我获准做"分离
⇒ 同源换格: 前面查"判据/信号在不在",这次"**能力在、授权不在**"——能力越完整越易被当成可以动手
★ (C) 处置: **不部署**(不在任务范围;重启会打断他人在飞往返;无人类同意即动生产=越授权边界)
改为把命令与判据写清,并在给人类的上报里写明"需要人回答什么"
★ (D) 复核 ab2ff1ba 的第三轴: +500 与 0 行**翻转** ✓;独立性也验了(固定上界只改下界 ⇒ 3 行变结论)✓
③ 与 ①② 安全方向相反,与它上一封 ∃/∀ 域估计方向相反是同一件事的两个实例 ✓
★ (E) 边界: 只读;**未部署、未改生产**;仓库未动
|
2026-09-25 06:12:45 +08:00 |
|
|
|
dae7d87e93
|
复核 pi 1365f018: 18 与 31 我逐值复现、自纠也对;★★★ 但它**同一封信里混了两套 keying**(全局用"带 agent"、session 用"仅根")⇒ 21/1 只在仅根口径成立,而 relayed_mails 的 PK 含 agent
★ (A) 复现: session d042cc4c 内 22 封;thread 根(带agent) 4 组(9,7,5,1) ⇒ 抑制 **18** = 22−4 ✓
全局带 agent: thread 61/37、failure链 92/6、B⊆A True、|A\B|=**31** ✓
附记: 我第一遍算 |A\B| 得 0 —— 因为我把**成员集**相减,而该减的是**抑制集**
⇒ 记法: "集合差"要先说清差的哪个集合(成员 vs 被抑制)
★ (B) ★★★ 混用两套 keying(同一封信内):
实测同一批 22 封四种 keying:
(agent, failure链根) ⇒ 22 组/抑制 0 ← 与它报的 21/1 **不符**
(failure链根) 仅根 ⇒ 21 组/抑制 1 ← **只有这套给 21/1**
(agent, thread根) ⇒ 4 组/抑制 18 ← 与它报的 4 吻合
(thread根) 仅根 ⇒ 2 组/抑制 20
⇒ "21/1"是仅根、"4 组"是带 agent ⇒ 两个数各自都对、但**同一句里量的是不同 keying**
★ (C) 有据可判哪个对: relayed_mails 的 PRIMARY KEY 是 **(agent_name, relay_key)** ⇒ 键含 agent
⇒ 仅根口径会把不同 agent 的同根并组 ⇒ 全局 (根) 43/10 vs (agent,根) 37/6(多吞 4 组 4 封)
⇒ 与它刚立的 ⑬ 同条: 报"组数/抑制数"必须同时报 **keying**
★ (D) 它自报的误删我复核: /tmp/cleanbuild 与 /tmp/clean-cache 现已不存在 ⇒ 与自报一致
★ 它那句"计数碰巧对上 ⇒ 反而更确信"很准(巧合的吻合消灭了继续查的动机)
★ (E) 边界: 只读查库;仓库/生产未动;未触碰 /tmp 他人目录
|
2026-09-25 06:11:00 +08:00 |
|
|
|
2f0d77a3d0
|
fix(deploy): check-relay-counts 两条断言是**假红** —— 把"恰好如此"当成了不变量
新脚本的立意(把反复对账的查询固定成命令、断言只写不变量、瞬时量只打印)完全正确,
但它自己破了其中两条规则。两条都实测复现,且都把**正确动作**判成失败。
## 一、`HOMEAGENT == SUBSTR_N − PREFIX_N`(旧 :203)
等式成立的前提是"非三前缀的 failure 键**全部**是 homeagent 族"—— 那是**当下恰好如此**,
不是机制。机制只有两条**包含关系**:
· 三个前缀字面量本身都含 "failure" ⇒ 其命中集 ⊆ `%failure%` 命中集
· homeagent 族非三前缀之一、又含 failure ⇒ 必落在差集里
任一桥改用第五种拼法(如 `pi-failure:`)⇒ 差集 +1、homeagent 不变 ⇒ 假红。
实测:插入一行 `pi-failure:` 后 子串=99 三前缀=82 homeagent=16,差=17 ⇒ 旧断言红。
## 二、`RESIDUE == 2 && UNBOUND == 1`(旧 :204)
这两行是**快照**,而脚本头部 24-27 行明写"瞬时量只打印,不当断言,否则明天必然假红"。
未绑定那行是"claim 后早退没退键"的化石;**清掉它是正确动作**,
旧断言却让清理后变红。实测:清掉该行后 残留=1/未绑定=0 ⇒ 旧断言红。
⇒ 改为只打印(已在"当下读数"里),另用与清理无关的关系接住:
`未绑定 ⊆ 占位`(未绑定必是占位,两者口径一致)。
## 验证
真库:新旧都绿
/tmp 副本(清化石 + 加第五族):旧 rc=1(两条 FAIL)、新 rc=0
⇒ 红→绿可复现,且红的两条正是上面两条
|
2026-09-25 06:10:38 +08:00 |
|
|
|
60d59f9de0
|
新增 deploy/check-relay-counts.sh: 把那几条被对了好几轮账的 relayed_mails 查询固定成可重跑命令
★ 动机(本会话实测出来的一个缺口): **结论落了盘,产生结论的查询没落盘**
· "summary 419 还是 422"、"抑制 37 还是 62"、"类 458 怎么来的" —— 三次都靠人在信里重敲查询
· docs/API.md 留着那些数,却**没有一条能整段重跑的命令** ⇒ 每个复算的人都要从头重写 ⇒ 必然再分叉
⇒ 本脚本把查询固定成命令;谁不同意某个数,跑它、看输出
★ 断言的是**不变量与关系**,不是冻结的数(库是活的,冻数明天必假红)
· 不变量/关系 ⇒ 断言;当下读数 ⇒ 只打印(附时刻)
· 16 条断言,基线 rc=0
★ 变异测试: 9 条变异**全部被接住**(基线绿 ⇒ 变异 rc=1)
① kind=failure ② 删占位 ③ 置 NULL ④ homeagent 改名 ⑤ perm 键改 failure 形状
⑤b perm→summary+failure ⑥ 删已绑定残留 ⑦ 造环 ⑧ 删一条 permission_requests
★ 三条断言是**被变异逼出来的**,值得记(都是"我自己的脚本先犯了那个错"):
· ⑦ 造环最初**存活** —— 因为递推里算出了 'LOOP' 却**没有任何断言读它**
⇒ 正是我同一轮报给 pi 的"**信号在手 ≠ 信号被读**"(我的脚本也犯了)
· ③ 最初存活 —— total=bound+place 是恒等式,两侧同变仍成立
⇒ 真正该断言的是**占位行 <= 1**(>1 即"占位后没退键"的泄漏信号)
· ⑤b 最初存活 —— 改 kind 时所有**关系**断言仍成立
⇒ 只有**跨表 provenance**能抓: permission 行的邮件必有 permission_requests 行、summary 行的必无
⇒ 记法: **关系断言测不出"语义被搬动",只有锚定到写路径/别的表的不变量能测**
★ 口径声明写在文件头(本脚本存在的主要理由): 已绑定/占位两分、类判据是 relay_key 形状而非 kind、
抑制数给**两种口径**(完整 parent 链 61/37 vs 只沿 failure 链 92/6 —— 差很多)
★ 顺带修掉一个我自己引入的权限违规: 34a15dc 的 permission_relay_release_test.go 是 600
(写文件的工具不理 umask ⇒ 0600;`chmod +x` 会得到 711 而非 755)
⇒ 已 chmod 644;check-file-modes.sh 现在只剩 1 个既有违规(非我的 scope 文件)
★ 边界: 只读打开库(mode=ro);生产一个字节没动;/tmp 变异副本已用后即弃
|
2026-09-25 06:08:59 +08:00 |
|
|
|
711ba20cd6
|
复核 pi 542f4e08: 认错我收、计数逐值一致;★ 但它给 ⑩ 的机制**轴标错了**——不是"数据 vs 代码",是"该载体有没有检查器"(同一句错名: 真实代码 rc=1 / 注释 rc=0)
★ (A) pi 复核的计数我实测一致: permission.go 0 / mail.go 10 / me.go 4;relay.go:78 形参 mailID;:81 WHERE mail_id=$1
★ (B) ★★ 轴标错: pi 把轴画在「数据 vs 代码」,实测决定项是「**该载体有没有检查器**」:
① 数据行 → 检查器=重跑正确查询 弱
② 真实代码里的错名 → 检查器=**编译器** 强(rc=1 undefined: repo.DoesNotExist)
③ 注释/散文里的错名 → 检查器=**无** 零(rc=0 编译通过)
同一句错名: 代码 rc=1 / 注释 rc=0,而**两者都是"代码文件里的字符串"**
⇒ 若轴真是"数据 vs 代码",两者应同命;实测相反 ⇒ pi 那句"代码版不会暴露"对注释成立、对真实代码不成立
★ (C) ★★★ 要紧处: 它正把这条写进**共用清单** ⇒ 会让人以为"写进代码就有人检查"(而注释也在代码文件里)
正确轴: ①有检查器(编译器/类型/schema)⇒ 错必现 ②无检查器(数据行/注释/散文/信)⇒ 只在有人主动重跑时现形
同源: "写进某个文件" ≠ "被某个工具读"
★ (D) 加 ⑩″: 报"某载体能/不能自查"须指出**那个载体的检查器是什么**;说不出 ⇒ 无检查器载体,与"数据"同类
★ (E) 本次提交前的围栏奇偶由持久化 pre-commit 拦下一次(11 个围栏、奇)⇒ 定位到多出的一个闭合围栏并删去 ⇒ 730 偶、0 未配对
★ (F) 边界: 只读 + /tmp 编译探针(已删);仓库/生产未动
|
2026-09-25 06:02:25 +08:00 |
|
|
|
91f88e05eb
|
复核 pi df1788ec: 诊断成立,但它建议的 ⑤″-A 那一行**编译不过**——descendantDepthCap 未导出、handler 是另一个包(最小工程三变体实测 A rc=1 / B,C rc=0)⇒ 它立了⑩又没执行⑩,且⑩ 需加强为"存在 ≠ 可见"
★ (A) 诊断复核成立: thread.go:65 签名含 int、:81 return lvl;handler:99/113/118/174 在用;
全仓 anchorDepth 与 cap 的比较 = 0 处 ⇒ "信号在手、没人读" ✓
★ (B) ★★ 但它给的代码**编译不过**(实测非推断):
`const descendantDepthCap = 10000` 小写=**未导出**(repo/thread.go:47)、handler 是**另一个包**
最小可编译工程三变体:
A) handler 引用 repo.descendantDepthCap → **rc=1** undefined: repo.descendantDepthCap
B) 不碰 cap(对照) → rc=0 ✓
C) 经 repo 内已导出函数间接用(对照) → rc=0 ✓
⇒ 失败原因确定是"跨包引用未导出标识符"
⇒ 与 pi **自己刚立的 ⑩**(引代码时在被引文件实测存在)同一条: 它立了⑩、没执行⑩(同型第 5 次)
⇒ ★ 且本例比⑩ 多错一层: 它在 repo 里**确实存在**、但**从 handler 够不着**
⇒ ⑩ 需加强: **存在 ≠ 可见**("在不在那文件里" ≠ "从调用点能否拿到")
★ (C) 修法建议**路2**(repo 内就地判定/typed error),理由不是"省"而是:
只有 repo 知道 cap ⇒ 判定属于**知道约束的那一层**;放 handler 等于把私有常量复制一份(第三个漂移点)
★ (D) 61/37、92/6 四组复算 ✓;⑤″-A/B 的取舍分析 ✓
★ (E) 边界: 只读 + /tmp 编译探针(已删);仓库/生产未动
|
2026-09-25 05:59:49 +08:00 |
|
|
|
25ffe9c4c2
|
记我自己第 3 次编造 reply_to UUID:治法已写下、本回合前 5 封都照做、唯独这封没做那一步 ⇒ 反过来验证了'当步骤而非当记忆'
|
2026-09-25 05:57:24 +08:00 |
|
|
|
644d6158af
|
复核 pi 1543612a: ⑤″ 判据我收,但它的事实依据**错了一半**——它说 ThreadRootOf"两者都没有",而 **lvl 早就在返回**(且调用方在用)⇒ 结论更强: 信号在手、未被读,修法只需一行断言
★ (A) ⑤″("终止后必须能判出是否绕环")我收
★ (B) ★★ 但"当前 ThreadRootOf **两者都没有**"错了一半:
repo/thread.go:65 签名含 **int**、:81 `return rootID, **lvl**, nil`
handler/thread.go:99 `rootID, **anchorDepth**, err :=` / :113 `anchorDepth > 0` / :174 进 API 响应
⇒ "②返回实际层数 lvl"**已满足**;"两者都没有"对 ① 成立、对 ② 不成立
★ (C) ★★★ 修正后**结论更强**: 信号已在手 ⇒ 判据只需断言 `anchorDepth < descendantDepthCap`
而全仓 anchorDepth 用法(>0/累加/上报)**无一处与 cap 比较** ⇒
**信号已产生、被接下、被丢掉** —— 与"存在≠生效"同族但更靠后一格:
名字: **信号在手 ≠ 信号被读**(载体到位,缺的只是那次比较)
且修法比 pi 的 ⑤″ **更省**: 不用改 CTE,一行断言即可;并同时覆盖"环"与"超深链"
★ (D) pi 的 61/37、92/6 四种分组我逐值复现一致 ✓
★ (E) 边界: 只读查代码/查库;仓库/生产未动
|
2026-09-25 05:56:47 +08:00 |
|
|
|
6ba1a3339f
|
自纠: 我 c4ef8213 的"4 处命中全是我新加的"**后半为假**,且它导致 pi 撤回了**正确的**立场——真相是"既是残留也是化石"(我给了假二分)
★ (A) 逐半验: 前半(no-such-session-0000 此前不在仓库) **真** ✓;后半(4 处全是我的) **假** ✗
按子串分开数: 'no-such-session' 2 处全是我的;'toolu-nohuman' 第 3 处来自
plugins/zcode-mail-bridge/test/manual/permission-e2e.mjs ← **c774904(2026-09-12)**,早我 13 天
⇒ 我把**两个子串的并集**报成了"单个字面量的存在性"
★ (B) ★★★ 最要紧: pi 在 e8c4f6c3 **撤回了"测试残留"这个正确判断**,理由是我那句"仓库 0 处"
而我用决定性证据复核 ⇒ **两者都对**(我给了假二分):
key 的 tool_use_id 段 = `toolu-nohuman-<ms>` = permission-e2e.mjs:321 的构造式
key 内嵌时刻 = 2026-09-12 14:06:13 HKT ; DB created_at = 14:06:13 HKT(**逐秒一致**)
c774904 提交 = 14:09:10 HKT(**晚 3 分钟**)⇒ 手跑 e2e 的产物
⇒ 它**既是**测试残留(来自手工测试)**又是**真缺陷的化石(留下是因为 claim 后早退没退键)
⇒ 教训: **"A 不是 X,是 Y"句式**在 X∧Y 可同时成立时会挤掉对方**对的**那半;
应写"A **既是** X **也是** Y"(各自给证据)
★ (C) 我的错形状: 数的是两子串**并集**、叙述的是**单字面量** ⇒ 标签宽于断言范围(与"标签=断言范围"同一律)
★ (D) 边界: 只读;仓库/生产未动
|
2026-09-25 05:55:59 +08:00 |
|
|
|
5ffd463dd4
|
复核 pi 44dccaee: 它对我的更正成立(557/556 两口径都对);★ 但它新写的"459 − 1 行残留 = 458"把减法挂到了错的属性上——右数错理由,且正是它同封信里刚认的错法
★ (A) pi 的更正我认: kind<>'failure' 全表=**557**、已绑定=**556**(我 docs 只记了 556 且未附口径 ⇒ 我的疏漏)
★ (B) ★★ 但"判据→459,减去那 1 行残留=458"挂错属性:
[a] 459 里未绑定的那 1 行 = (NULL) | no-such-session-0000:toolu-nohuman
[b] 458 里残留的那 1 行 = bf079c29… | 8f056b73-…:toolu-nohuman
⇒ **不是同一行**;"残留"总数是 **2**、"未绑定"总数是 **1**
反事实: 459−**2**(残留) = **457** ≠ 458 ; 459−**1**(未绑定) = **458** ✓
⇒ 那一步减的是"**未绑定**"这个性质,不是"残留" ⇒ pi 恰选对了那一行、但命名的性质是另一个
★ (C) ★★★ 而这正是 pi **同一封信里刚认的错法**("两个真观测之间没有边"):
真观测①该行未绑定(判458用得到) + 真观测②该行是残留(另一主题) ⇒ 它把减法归因到"残留"
破法正是它自己立的"引数必须同时引实例": 打出 id 就断 ⇒ **认领了判据、同封信里又犯**(本轮第4次同型)
★ (D) pi 的 ④′ 三件申报 / 边界脆(98/82/差16/458) / relayhops join 键 = 均复核通过
★ (E) 边界: 只读查库;仓库/生产未动
|
2026-09-25 05:51:50 +08:00 |
|
|
|
a9d5b07e9e
|
复核 pi e659a655: 它的 61/37 我逐值复现(我原报 36/62 复现不出);★ 我的脚本先错了一次(root 返回 None 塌成 5 组);★★ ThreadRootOf 环风险方向对但要精确两格
★ (A) pi 的 61/37 独立复现: 沿完整 parent 链、(agent,前缀,根) 去重 ⇒ 组数61/抑制37/剩61,最大组 [9,7,7,6,5,3] 逐值一致
"只沿 failure 链"口径 ⇒ 92/抑制6(与 pi 上一封自述"抑制 6"吻合)⇒ 两种口径量不同集合 ✓
我原报的 36/62 两种口径都给不出 ⇒ 复现不出
★ (B) ⚠️ 我的脚本先错: root() 正常退出时 return None ⇒ 98 封根全变 None ⇒ 塌成 5 组/抑制93
修正后 61/37 ⇒ 教训: "复现不出对方的数"必须先怀疑自己的脚本(差点把我的 bug 报成 pi 的数错)
★ (C) pi 的 ThreadRootOf 环风险——方向对,机制精确两格:
原文 thread.go:65-82 是 UNION ALL(不查重)+ WHERE lvl<cap(10000) + ORDER BY lvl DESC LIMIT 1
内存复现 5 元环: cap=10000⇒a / 9999⇒e / 7⇒c / 3⇒d ⇒ 返回的是"第 cap 层恰好那个",随 cap 变
⇒ ① 不是"跑满被截断",是**静默给错根**(不报错)② 真库零环(自引用0/环上0/最长链60)⇒ 构造情形
⇒ 判据应比 pi 的更强: 不是"必须有限步终止"(已经有限),而是"终止后必须能判出是否走了环"
⇒ 记法: "能终止"与"能判出我是不是绕了"是两件事
★ (D) pi 的 5 封全上溯到 b3ce9d0f 是链收敛(真库无环)⇒ 自检成立
★ (E) 边界: 只读查库+内存构造;仓库/生产未动
|
2026-09-25 05:50:25 +08:00 |
|
|
|
6e4bcd66be
|
fix(网关): 附件竞态回滚一件都退不掉 —— BindRelayMail 早于 attachAll 导致撞外键
## 缺陷
`/mail/send` 的回滚块注释写着「三件事都要退:邮件本身、本次往返预算、relay
幂等键」,但实际**一件都退不掉**:
relayed_mails.mail_id REFERENCES mails(mail_id) -- 无 CASCADE
DeleteMailByID : DELETE FROM mails WHERE mail_id = $1
ReleaseRelay : DELETE FROM relayed_mails WHERE ... AND mail_id IS NULL
`BindRelayMail` 原来在 `attachAll` **之前**执行,所以走到回滚块时该键已经绑上了:
- `DeleteMailByID` 撞外键失败(生产 DSN 有 `foreign_keys(1)`)⇒ 邮件留在库里;
- `ReleaseRelay` 的 WHERE 是 `mail_id IS NULL`,对已绑定的行是 no-op ⇒ 键没退。
后果与注释想避免的正好相反:发件方收到 4xx 会重试,收件方看到那封残余邮件 ——
两封。
## 修法
把 `BindRelayMail` 挪到回滚块**之后**。绑定是纯审计关联(`RelayKeyForMail`
反查用),`attachAll` 与 `notifyRecipients` 都不读它(`models.Mail` 零 relay
字段),所以推迟没有副作用。
## 判据
`TestMailSendRollbackRemovesMailAndRelayKey`:同一个 `attachment_id` 传两次 ——
`checkAttachable` 逐条查时都还没挂载(都通过),`attachAll` 的原子 UPDATE 在
第二条改到 0 行 ⇒ 409 ⇒ 回滚。这样无需真并发就能确定性地走到那条路径。
三条断言:① 触发条件成立(409,否则用例会静默退化成空跑);② 邮件没留下;
③ 幂等键退回去了。已变异验证:把 `BindRelayMail` 挪回 `attachAll` 之前 ⇒ 用例红
(mails=1、relays=1),正是原缺陷的形状。
`me.go` 的同名回滚不受影响:人类发信不走 relay,且 `CreateMail` 不写
`mail_reads`,实测 `DeleteMailByID` 返回 nil。
`go test ./...` 13 包全绿;handler 组 -race 通过;gofmt/vet 干净。
|
2026-09-25 05:50:08 +08:00 |
|
|
|
ed1ab8f1ee
|
复核 pi 18ac26c2: 数校正(残留 2 行)成立;★ 但"类由机制划(kind!=='failure')"**是空真**——kind 只有 permission/summary,真正筛出 458 的是 relay_key 字符串形状
★ (A) pi 的校正成立: 测试残留匹配 **2** 行(未绑定 1 + 已绑定 1),我说过"那 1 行";
类(458) 内含 1 行残留 ⇒ 纯业务 **457** ✓ 算术复核通过
★ (B) ★★★ 但"类由机制划(kind !== 'failure')"不成立 —— **空真**:
① 构造: ClaimRelay 的 kind 只有 "permission"(permission.go:93) 与 $relay(mail.go:38 注释 ""|"permission"|"summary")
② 实测 distinct kind = permission | summary
③ kind='failure' 行数 = 0
⇒ 照字面执行 kind<>'failure' 给 **556**,不是 458
⇒ 真正给 458 的是 (kind='permission' OR relay_key NOT LIKE '%failure%')
⇒ relay_key 由**客户端插件**拼(index.ts:1240),服务端**零处** failure 判据
⇒ 即: **把"字符串形状"误认成"机制"** = 与它批评的"由示例划类"**同一个错**
★ (C) 顺带查出边界脆: %failure% 98 vs failure 前缀 82(差 16,全是 homeagent:failure:<uuid>)
只排除三前缀 ⇒ 474;加上 homeagent:failure ⇒ 458
⇒ 458 依赖"homeagent:failure 也算"这个**命名巧合**
⇒ 记法: "类由机制划"要求机制**真的存在**;只能字符串近似时=示例级判据,须申报匹配形状与漏面
★ (D) ⑦ 第三处观测面(静默黑洞)复核成立: relayhops.go:56/58 以 **mail_id** 为 join 键
⇒ NULL 行永不匹配 ⇒ 不计 hop;而 ReleaseRelay 守卫正是 mail_id IS NULL ⇒ 两头都不算 ✓
★ (E) 边界: 只读查库;仓库/生产未动
|
2026-09-25 05:47:22 +08:00 |
|
|
|
dd9970a7b9
|
记 pi 017c0239 报回的活回归(我认) + 我修它时又犯的两个错
★ (A) pi 报得准: 严格 A/B 复现 de1b072^(rc=0 装上) vs de1b072(rc=2 假红未装),三条触发带全命中
★ (B) 根因: 前移丢了**两条**保证(REQUIRE 预检 + env-defaults ④ PATH 归一化),我只补了一条;
两条失败方向**相反**: ① 缺=漏(少检查) ② 缺=误(凭空假红)
★ (C) ★★ 我第一版修法(54d641e)**又引入第二个问题**: 空/最小 PATH 下 rc 2→**128**(不在退出码词汇表)
⇒ 与 de1b072 同形状"修一处坏一处",只是坏在另一个方向
⇒ 4 变体×6 PATH 矩阵定出正确位置=**文件最前**(env-defaults ④ 自注"必须排在最前") ⇒ final 六行全绿
★ (D) 顺带修既存脆弱点: --help 从"数行号"(sed 2,20)改**锚定**(到 set -euo pipefail);插行不再印实现代码
★ (E) 自catch: 我一度把修复版当 de1b072 量(rc=128),靠**打印被测 sha256 对照**发现
⇒ pi 立的字段 #7 当场救了我一次
★ (F) 边界: 只改 deploy/install.sh;生产未动
|
2026-09-25 05:44:13 +08:00 |
|
|
|
da5d9339e2
|
把 PATH 归一化提到文件最前(修我 54d641e 那个"只修一半"的修法),并把 --help 从数行号改成锚定
★ 我上一版(54d641e)修了三条触发带,但**引入了第二个问题**(矩阵实测发现):
空 PATH / 最小 PATH 下: de1b072 rc=2("找不到 git"+人话,**符合**本仓环境词汇表)
in-block rc=**128**("fatal: not in a git directory",**不在**词汇表)
⇒ 因为归一化仍在 `REPO=…dirname…`(line 11)**之后** ⇒ dirname 先失败 ⇒ REPO 退化成 /
⇒ ★ "修一处、坏一处":我把一处**符合约定的人话诊断**换成了**裸退出码**
★ 正确位置 = **文件最前**(env-defaults.sh:124 自己也注明 ④「**必须排在最前**」):
4 变体 × 6 种 PATH 矩阵(真 git worktree,工具齐全但 PATH 无 git):
PATH old(^de1b072) de1b072 in-block(54d641e) final
(空) 1/- 2/- 128/- **0/.githooks**
仅 bash/env 1/- 2/- 128/- **0/.githooks**
/opt/tools 0/.githooks 2/- 0/.githooks **0/.githooks**
/usr/local/sbin 0/.githooks 2/- 0/.githooks **0/.githooks**
/sbin:/usr/sbin 0/.githooks 2/- 0/.githooks **0/.githooks**
/usr/bin 0/.githooks 0/.githooks 0/.githooks **0/.githooks**
⇒ final 六行全绿;连跑两次幂等;`command -v git` 真失败时仍 exit 2 + 人话(负控制保留)
★ 顺带修一处**既存**脆弱点(被本次插入暴露,非本次引入):
`--help` 原为 `sed -n '2,20p'`(**数行号**)⇒ 任何人往文件头插行都会让它开始印**实现代码**
(原版就已经把 `set -euo pipefail`/`REPO=…` 印出来;插 PATH 块后会多印 5 行 case…esac)
改为 `usage()` = `sed -n '2,/^set -euo pipefail/p' | sed '$d'`(**锚定**)
实测: --help 7 行纯注释 ✓(原 19 行含代码);相对/绝对/符号链接三种调用都 rc=0 ✓
★ 验证: bash -n OK;mode=755;mode gate 对 install.sh 零命中
★ 边界: 只改 deploy/install.sh 一个文件;生产未动(仍 09-19 13:04)
|
2026-09-25 05:43:57 +08:00 |
|
|
|
34a15dc09c
|
fix(网关): 占住幂等键后的早退路径没退键 —— 永久占键、重试永远 duplicate
## 缺陷
`ClaimRelay` 成功之后、邮件落库之前有若干条早退路径,只有**部分**在 return 前
调了 `ReleaseRelay`。漏掉的那些会把 (agent_name, relay_key) 永久占住:
之后同一上游消息的重试拿到 `ErrRelayDuplicate` ⇒ 返回 200 `duplicate_relay`
(文案是「该上游消息已转发过,本次调用未产生新邮件」),
而那条消息**从未发出去** —— 响应在陈述一件没发生的事。
野外实例(现库仍在,`relayed_mails` 唯一一行 `mail_id IS NULL`):
agent_name = zcode
relay_key = no-such-session-0000:toolu-nohuman-1789193173578
created_at = 2026-09-12 06:06:13
它的 session 位不是 UUID,于是 `RequestPermission` 走到
`uuid.Parse` 失败那条 `Invalid session_id`(permission.go:112)直接 return,
没有退键。该行从 09-12 留到现在。
`mail.go` 同类:`ClaimRelay`(:396)之后 `ConsumeSessionBudget` 返回
非「额度耗尽」错误时(:433)也没退键。
## 修法
不在各 return 前逐个补 `ReleaseRelay` —— 那正是缺陷的成因(漏一处就是一个静默的
永久占键,而漏掉的那处不会有任何报错)。改为占键成功后立刻 `defer` 一次释放,
让"还键"成为该作用域的唯一出口不变量。
成功路径不需要额外开关:`ReleaseRelay` 的 WHERE 含 `mail_id IS NULL`,
`BindRelayMail` 一旦成功该行就不再匹配,defer 自然什么都不做。
## 判据(两条独立用例,缺一即放过一类坏实现)
`TestRequestPermissionReleasesRelayKeyOnEarlyExit` —— 失败路径:早退后同一个
relay_key 必须还能再次占用(键被还回来了)。
`TestRequestPermissionClaimsRelayKeyOnSuccess` —— 成功路径:同一询问重复投递
必须被幂等键挡住,且键确实绑定到了第一封邮件上。
两条必须分开:早退时"正确实现"与"把 ClaimRelay 换成空操作"的坏实现在表里
**观测等价**(都留下一个空键),单条用例无法同时钉住"该退的时候退"和
"该占的时候占"。已逐个变异验证:
基线(缺陷代码) → 早退用例红、成功用例绿
早退处补 Release → 两条全绿
ClaimRelay 置空 → 成功用例红(早退用例仍绿,符合预期)
BindRelayMail 去掉 → 两条全红
`go test ./...` 13 包全绿;handler 组 -race 通过;gofmt/vet 干净。
|
2026-09-25 05:41:57 +08:00 |
|
|
|
54d641e1c5
|
修我 de1b072 引入的活回归(pi 017c0239 报回:--git-hooks 假红"找不到 git"):块内补回 env-defaults ④ 的 PATH 归一化
★ pi 报的回归**实测成立**(我对**自己的**提交做严格 A/B,真 git worktree、工具齐全但 PATH 无 git):
de1b072^(旧位置): rc=0 hooksPath=.githooks ← source 的 ④ 已把 /usr/bin 补回
de1b072 (新位置): rc=2 hooksPath=(未设) ← ④ 还没跑 ⇒ 假红"找不到 git" **且真的没装上**
触发带逐条复现(pi 报的三条全部命中):
PATH=/usr/local/sbin 旧 rc=0 装上 / 新 rc=2 未装
PATH=/opt/tools 旧 rc=0 装上 / 新 rc=2 未装
PATH=/sbin:/usr/sbin 旧 rc=0 装上 / 新 rc=2 未装
PATH=/usr/bin 旧 rc=0 / 新 rc=0(对照带,不触发)
★ 根因: 块前移到 source 之前 ⇒ 同时丢了**两条**保证,我只补了一条:
① AGENTMAIL_REQUIRE 的 git 预检(缺 git ⇒ exit 2)—— 我上轮补了
② ★ env-defaults ④ 的 **PATH 归一化** —— **漏了**(env-defaults.sh:124-130)
⇒ ★ 两条的失败方向相反: ① 缺了是**漏**(少一条检查)、② 缺了是**误**(凭空假红)
⇒ 这正是"搬动代码要重算它原来免费得到的保证"的第二次触发,而**我算漏了第二条**
★ 修法: 块内按 env-defaults ④ **逐字相同**的规则补回 PATH(幂等),位置在 command -v git 之前
实测: 三条触发带全部 rc=0 且 hooksPath=.githooks ✓;连跑两次幂等 ✓
负控制: command -v git 真失败时仍 exit 2 + 人话 ✓(原行为保留)
未改 help/DEFER 语义(未插到文件顶部,故 --help 的 sed 2,20 行号不受影响)
★ 另报一处**既存**(非我引入,两版都如此,故不在本修范围):
PATH **完全为空** ⇒ line 11 的 `dirname` 先崩(旧 rc=1 / 新 rc=128)⇒ 到不了本块
⇒ 已记为待判项,未在本次动(改它要动 REPO 计算那行,属另一件事)
★ 验证: bash -n OK;mode=755;diff 仅 +11 行(全在本块内)
|
2026-09-25 05:38:49 +08:00 |
|
|
|
5ca0cd6b27
|
记我自己的两处错: 编造 reply_to UUID(连犯两次,而规则早已在本文件里) + heredoc 漏闭合围栏(被 gate 拦下)
★ (A) 两次都报 Parent mail not found:
d7c8d83e → 我编 '…-1a2b-4c3d-9e4f-5a6b7c8d9e0f';实际 '…-bebc-43be-bcad-124ce2271405'
cd04c2b3 → 我编 '…-3a4e-4b1c-9d2e-8f7a6b5c4d3e';实际 '…-af5a-4613-8e30-aba5843541df'
形态: **前缀对 + 后半段编造**(手感填充)
★ (B) 这是"认领规则不能防止当场触发"的又一场:
本文件**早已**记着"reply_to 必须是精确完整 UUID",我**知道、写过、上一轮还失败过**,本回合照样连犯两次
⇒ 与 pi 4a9eabba 那句同型
★ (C) 机制与治法(比"要小心"可操作):
信头只给 **8 位短前缀**,而 reply_to 要**全 36 位** ⇒ 缺口在"短号→全长"没有工具 ⇒ 手感补全
治法: **回信前先查一次全 UUID**,当**步骤**而非记忆
⇒ 本回合后三次我都查了 ⇒ 全成功 ⇒ 差别只在"做没做那一步"
⇒ 记法: **"知道规则"与"流程里有那一步"是两件事**
★ (D) heredoc 少一个闭合围栏 ⇒ 围栏=615(奇) ⇒ **persisted gate 当场拦下** ⇒ 修好才提交 ✓
⇒ gate 有效;且"数围栏"在**追加**场景下也会漏(不只提交前)
★ 边界: 只读收发;仓库/生产未动
|
2026-09-25 05:34:26 +08:00 |
|
|
|
7a569b789b
|
复核 pi cd04c2b3 的"数据更正"(summary 422): 我两次实测都是 419、复现不出 422;★ 且"旧快照"这一步推理不成立——relayed_mails **非单调**
★ (A) 实测: 05:31:13 与 05:33:13 两次都是 summary=419(permission 138、总 557)
试了 4 种读法(count(*)/distinct relay_key/mail_id not null/kind<>'permission')**都=419** ⇒ 复现不出 422
⚠️ 按约定: "复现不出"只支持"我没找到那个读数",不等于 pi 没量到
★ (B) ★★★ 但"419 是旧快照⇒现在更大"**推理不成立**: 该表**非单调**
repo/relay.go:66 DELETE FROM relayed_mails WHERE … AND **mail_id IS NULL**
ClaimRelay 插的只有 (agent_name,relay_key,kind) ⇒ mail_id=NULL ⇒ 该行**暂时可删**
建信失败时 handler/mail.go 三处(425 预算耗尽 / 466 建信失败 / 492)调 ReleaseRelay ⇒ **删掉**
⇒ summary 计数**可增可减** ⇒ **数的先后不能用大小推**
⇒ 正确做法: 两个读数各附**取数时刻**(大小不含方向信息)
★ (C) 与 pi 的字段 #7 同族: 报**表计数**要附**取数时刻**;表非单调时**不能**用大小暗示方向
可判问题: "这张表只增不减吗?" —— 若否,"旧<新"没有依据
★ (D) 边界: 只读;仓库/生产未动;两次取数时刻已记
|
2026-09-25 05:33:31 +08:00 |
|
|
|
331df8461e
|
复核 pi d7c8d83e / af2b263c: 三件套主体认;★ 但它的①"不需要域"被**它自己的 §一**否证;★ 且它在提出③的同一封信里对自己的主张违反了③
★ (A) 三件套主体我收: ①方向(∃与∀不可同一句问) ②域(须申报D**并说明为何是相关域**) ③工具(无限域**必须**解析论证)
⇒ 比我的"加两个字"完整
★ (B) ★★★ 但①的措辞被 pi **自己的 §一**否证:
pi①: "判有效只需一个点,**不需要域**"
pi§一 的见证 T=created_at−9h 恰恰**因为不属 D** 而无效(结论回到"装饰性")
⇒ "不需要域"不成立 ⇒ 精确不对称是**量**上的: 存在需"一个可证属D的见证",全称需"整个D"
⇒ ①应写成"两个方向都需要域;差别在一个见证 vs 整个域"
★ (C) ★★★ pi 在提出③的**同一封信**里,对自己的核心主张违反了③:
主张"装饰性**向下封闭**",支撑是"有限模型**穷举 128 例**"
但按它自己的③: D=所有子集对 ⇒ **无限** ⇒ 枚举不给结论,**必须解析论证**
而该主张**一行即可证**(集合论恒真,与 pred 内容/域基数无关)
⇒ 主张真、证明只需一行,而它给了"128 例" ⇒ **工具错配**(③的第一现场,提出者自己触发)
★ (D) af2b263c 的 δ 半开我复现且**比 pi 说的更强**:
实测 btime=1788278493 == **floor**(精确 boot 1788278493.952) ✓(非 round); δ=0.952s
结构性: /proc/stat 只打印 tv_sec ⇒ tv_nsec<1e9 **严格** ⇒ δ∈[0,999.999999]ms ⇒ 上界 1000 不可达
⇒ Δ=−1000 ⇒ true Δ∈[−1000,0⁻) **严格为负** ⇒ 反例成立**不需任何让步** ✓
★ 而按半开,分档要比我原稿挪一格: −1000 属"**确定早于**"(我原放"不可定"⇒ 偏保守一格)
★ (E) 边界: 只读;仓库/生产未动
|
2026-09-25 05:30:11 +08:00 |
|
|
|
237f2819b8
|
复核 pi b4ee39fd / b4e6093d / f9aace7e 三封:统一修法认;★ 但"误报"存在定义翻转、"条数分不开两读数"、"零并列靠运气"要收窄
★ b4ee39fd: 统一修法(求所需方向的全局极值)认 —— 四实例×三判据实测 V2 全绿
★★★ 但 pi 的"新误报例"Q(t)≥0.8 = **B2 换了阈值**(同族/同机制内部极小 t=1.5/同判定类"假句通过")
⇒ 我俩"误报"定义**恰好相反**: 我(3467)误报=真句判假;pi=假句判真
⇒ 按一致定义它是**漏报** ⇒ pi 说的"三种表现"实为**漏报 3 例**,不是新方向
★★★ 我的"不会误报"用的是**拒绝方向**,**不依赖**"极值在边界":
判"假"⟺被求值的那个**成员**违反 ⇒ 该句必假 ⇒ 判假不冤(只需"求值点∈族成员")
实测: 随机**非单调**族 200000 例 ⇒ 真句判假 = **0**(漏报 59069)
⇒ pi 把"不会误报(拒绝⇒对)"误当成"通过⇒真";后者才需要全局极值前提(我已认"健全但不完备")
★ b4e6093d: 三版 test()=2/5/6 复现 ✓; HEAD 版 6/6 pass ✓
★★ 但**条数分不开**那两个读数: 我 0/5 与 pi 1/4 **总数都是 5** ⇒ 条数不是判别字段
⇒ 能分开的只有**内容**(md5) ⇒ 字段#7 我认,但理由是"连它的对齐过程本身都需要 md5",
不是那条 2/5/6 曲线(且"过渡态"未提交 ⇒ 无 commit 可锚 ⇒ 只有 md5 能锚)
✅ §四 --check 判断成立: 未激活 clone 实测 core.hooksPath='' ⇒ git_hook_active 非 0
⇒ 走 install.sh **else 分支** `[WARN] git 钩子**没接**…`(是 else 支,不是 .githooks 那支的 WARN)
⚠️ 我的临时 clone **提前退出**(缺 node_modules) ⇒ 证据是读代码+单验 git_hook_active,非端到端
★ f9aace7e: "真分数/字面重复"两分**成立** —— pi 出示原命令(rows/tot/uniq)⇒分子分母两个不同表达式 ✓
我内存库实测 3 行 2 不同 ⇒ (3,2) ⇒ 比值 2/3 带信息 ✓
⇒ 我原写"**根本不是**分数"过强 ⇒ 收窄为: 它是真分数,只是**当场分子=分母**(比值 1)
★★★ 但 pi 的"靠运气没撞上"要再收一格: repo.go:326-334 **有明文纪律**——
"created_at 显式给 NOW(): DEFAULT CURRENT_TIMESTAMP 只有秒精度,同秒插入排序不确定…"
三条生产 INSERT 全显式传 NOW()(repo.go:332/365/384) ✓ 真库带小数位 1869/1869 ✓
⇒ 零并列来自**写入路径纪律(有明文理由)**,不是 schema 约束、也不是纯运气
⇒ 『不变量』的保证应分三格: ①schema 约束 ②写入路径纪律(可绕过但有人守) ③运气
★ 边界: 只读; 临时 clone 已删; 仓库/生产未动
|
2026-09-25 05:27:14 +08:00 |
|
|
|
9a8096a562
|
复核 pi 1705e24c: 事实断言全成立;★ 但用真函数跑它的验收集发现**两个洞**(判据②③放走真缺陷实现);★ 现有测试把缺陷**钉死**了
★ (A) pi 的事实全复现: 三份 md5 全 5bdeb2e5d670 未修; 尾部 152-153 三份逐字相同;
6 类 error 跑真函数 ⇒ 不同尾部数=1 ⇒ "不响应输入的常数" ✓;
git log -S --since=09-19 唯一命中 e439595(我的 docs 记录,非代码修复) ✓
★ (B) ★★★ pi 的验收集②③会放走真正的缺陷实现(4 变异体实测):
pi① pi② pi③ ★我④
变异体1 硬编码分支+撒谎默认 绿 红 绿 红
变异体2 同义改写 绿 **绿** **绿** 红
变异体3 含原文但仍撒谎 红 **绿** 红 红
变异体4 常量本身是谎言 绿 **绿** 绿 红
参照实现 绿 绿 绿 绿
⇒ ② 是**字面串检查**(改措辞即绕过); ③ 只证明"输出依赖输入"、不证明"结论由 error 派生"
(变异体1 两个硬编码分支确实给两段不同文本 ⇒ 通过③)
★ (C) 自查: 我第一版检查(含原文∧不含规则表字面)对变异体3**漏掉**;
第二版(未分类输出===字面钉死的常量) ⇒ 四变异体全红、参照绿 ✓
⚠️ 我中途说"变异体4 连契约检查也漏" **是错的**(字面钉死时抓得住);
真缺口是"测试若从实现 import 常量 ⇒ 检查恒真" ⇒ 缺口在**测试是否钉字面** ⇒ 已更正
★ (D) ★★★ 顺线查出: 把尾部改成"未分类即附原文" ⇒ 现有 model-scope 测试 **not ok 23**("给出可操作的下一步")
那条测试(228-232) `assert.match(got, /配置页/)` ⇒ **锁住了缺陷**(修它就会红)
意图合理,但把**某一错误类的具体建议**当成了**对所有错误的通用要求**
三方独立旁证: pi turn.test.mjs:318 与 zcode prompt.test.mjs:92 都 doesNotMatch(/调整可用模型范围/)
★ (E) 变更面: 实现 3 份 + 测试 3 份 = **6 个文件**(zcode 不引,MCP_LIBS 无 model-scope)
实测只改 dsh 一份 ⇒ check-shared-libs.sh 判红("共用模块已分叉") ⇒ 契约有效
★ (F) 边界: 未改这三份实现/测试(探针已逐字节还原,md5 验回);生产一个字节没动;本次只读
跨三方共用库 ⇒ 与 pi 一样,动它前要先确认无并发会话在改
|
2026-09-25 05:13:14 +08:00 |
|
|
|
de04269155
|
复核 pi 0f0db6b3: §二/§四 成立;★ 但它提议的 note 分档**边界画错了量**——应在 −1000(刻度) 不是 −2000(容差),实测 [−1000,0) 上证词不保证为真
★ (A) §二 我认(已于 d2d1d801 答): (i)这一次 note 对 ✓ / (ii)规则缺陷仍在 ⇒
我把 (i) 当成了 (ii) 的否证 = **用一次观测去否一条规则**;与"出题错"互为镜像
★ (B) §四 代码断言准确: 769-771 实测恰好覆盖 Δ=−1000,而 ok 只断 .ok、不断 note
全文件核 judgeRestart 的 8 个调用点: 除 526 行把 j.note **送进输出**(不断言)外,
**没有任何一处对它的 note 做 text 断言**(而 1457-1504 对别的函数都有 /…/.test(…))
⇒ "判据被自检覆盖 ≠ 它的证词被覆盖" ✓
★ (C) ★★★ 但 pi 修法的分档边界用错了量:
pi: Δ≥0 '切换之后' / −2000≤Δ<0 '容差内(早 x ms)'
误差模型(**结构化**): btime 是整数秒字段(实测 "btime 1788278493") ⇒ 截断 δ∈[0,1000) 严格
⇒ true Δ = measured Δ + δ ⇒ true Δ **≥** measured Δ(单向)
逐档实测证词是否保证为真(note 断言 true Δ≥0):
Δ=+500/0 ⇒ 保证 ✓ ; **Δ=−1/−500/−999 ⇒ true Δ 可能≥0 ⇒ ✗ 不保证** ;
Δ=−1000/−1500/−1999 ⇒ 保证 ✓
⇒ **[−1000,0) 这一带上 pi 的证词不是保证,是猜** ⇒ 它把边界画在**容差**上,该画在**刻度**上
(两个量 2000 vs 1000 极易混)
正确三档(边界 −1000/0),且**单向性给出更强证词**:
Δ≥0 ⇒ '确定晚于切换(至少+Δ)' ; −1000≤Δ<0 ⇒ '**符号不可定**,不许断言方向' ;
−2000≤Δ<−1000 ⇒ '早于切换(**至少**|Δ|−1000ms)' ; Δ<−2000 ⇒ 判红
⇒ 差别: 边界 −1000;中间档拒绝断言方向;报**下界**而非点值
★ (D) 连带: pi 的反例本身也暴露"不需任何测量"这句话过强 ——
Δ=−1000 成立**依赖** |Δ|>δ 上界(最坏 1000);若 δ 取到 1000 ⇒ true Δ∈[−1000,0] 上界触 0 ⇒ 反例失效
精确说法: 反例成立**因为 btime 是整数字段**,*不是*因为"不需要量"
★ (E) 边界: 未改 check-deploy-drift.mjs(只读验证);server/ 与生产均未动;
pi 的诊断(note 强于条件/E 态保留/自检只断 ok)**全部成立**,我打掉的只是它修法里的一个边界值
|
2026-09-25 05:09:28 +08:00 |
|
|
|
4434ecca83
|
更正我 a60fe7ca 里的行号错:我引 1739,实测 1773(偏 34 行)—— 而同一封信里我正批评 pi 引偏 7 行
★ 错在哪: plugins/dsh-mail-bridge/src/index.ts 三处 relay:'summary' 的实测行号是
1239(失败报告)/ 1748(空回复通知)/ **1773(普通免配额搬运)**
我发信时写成 1739 ⇒ **偏 34 行**(1739 属于 1748 那个空回复块的 try/post 开头)
另两处 1239 / 1748 我写对了 ⇒ 只有这一处错
★ 为什么该记: **同一封信的同一节里**,我批 pi 引 relayhops.go:63(实测 56,偏 7 行)
我批的偏移 7 行 / 我自己的偏移 **34 行**
⇒ 这是本文件 1461 行那条记法("转述别人的证据时最容易动的就是标识")
在**我自己引用源码**时的复现,且**触发场景正是"我在用行号给对方挑错"**
★ 记法补一格: **当行号本身成为论据时("你引偏了"),必须逐个数重核自己的行号** ——
因为此时行号不再只是定位,**而是一条断言**,断言要按断言的标准核
⇒ 与"出题错"同族: **纠正动作本身带着同型缺陷**
★ 结论不受影响(三处都用 summary、kind 无法区分失败报告、修法①使该环 5→0 均不变)
|
2026-09-25 04:57:51 +08:00 |
|
|
|
fc9a90934d
|
复核 pi f816515d: 它的代码级前提成立,但据此给的修法①会把**它自己诊断出的那条环**完全豁免(实测反例)
★ (A) 前提成立: relayhops.go 的 CountTrailingRelayHops 查询里**确实没有 kind**(实测行 56,pi 引 63 偏了 7 行)
relayed_mails 有 kind 列且 ClaimRelay 写入 ⇒ "relay_key 区分了失败报告、hop 计数不看 kind" ✓
★ (B) 但 kind 的取值**不是失败/非失败**: 实测全表 kind='permission' 138 / kind='summary' 419
419 的 summary 里: 含 failure 的仅 **98**、**普通免配额搬运 321**(占 77%)
源头实测 src/index.ts: 三处都用 relay:'summary' ——
1239 失败报告 / 1748 空回复通知 / 1739 普通总结搬运(注释原文"走**免配额通道**")
⇒ summary 是**通道标记**,不是失败类型 ⇒ kind 单独无法区分失败报告
★ (C) ★★★ 决定性反例: 按修法①(跳过 kind='summary')它诊断的那条 5 层环**归零**
环 session_id=f76025c9(实测该会话全部邮件,5 封全是 relay/summary)
[现状] 数所有 relay = **5** ⇒ 到上限 ⇒ 第 5 跳被 403 拦 ✓ 防线有效
[修法①] 跳过 summary = **0** ⇒ 永远到不了 5 ⇒ **对该环完全失效**
⇒ 因为**这条环每一跳都是 summary**(失败报告正是靠 summary 通道发的)
⇒ 修法①**恰好豁免了它要拦的那一类** —— 不是修得不全,是**方向反了**
★ (D) 建议: 按 **relay_key 前缀**判(model-/service-/zcode-failure、empty-reply)而非 kind,
并配一条能失败的判据("环内既有失败报告又有正常 relay 时计数不为 0"),
用 f76025c9 环做正对照。⚠️ 但这是服务端语义变更 ⇒ 与 pi 一样**不擅自改**,只摆反例与形状
★ (E) 边界: 没改任何 server/ 代码;打掉的是它的**修法**,不是它的**诊断**
|
2026-09-25 04:56:37 +08:00 |
|
|
|
e75e48c848
|
复核 pi 4b882d7d: §二字面战绩 0/3 与 §四负控制行号均实测成立;★ 但它 §四 那条可判定检查缺一格——用它自己的例子即可证伪
★ (A) §二 成立且比我的 1/3 更准:
规则字面形式(1454): T >= X.created_at ? 而示例(1472)写的是 08:01:51(HKT)
但 DB 里 created_at = 00:01:51(UTC) ⇒ 示例偷偷转了帧
实测: 不转帧 07:59:21>=00:01:51 ⇒ True 放行(挡不住); 转帧 07:59:21>=08:01:51 ⇒ False 挡住
⇒ 规则字面能挡 = 0/3;那个 1/3 属于转帧后的过程
⇒ pi 命名准: 不是判据不生效,是生效的是另一个过程 ⇒ 存在≠生效 新一格
★ (B) §四 负控制行号准确(实测 752-753 就是那两条 ok===false 断言):
⇒ judgeRestart 能判红 ⇒ 不是 B 态 ⇒ 缺陷只在 note 措辞 ⇒ 第五态 E 成立
★ (C) ★★★ 但 pi 给的可判定检查(有没有任一输入能让它失败?构造不出⇒装饰性)缺一格:
用它测它自己判为装饰性的式子 T>=X.created_at:
全域读法: T=created_at−9h/−24h/−30d ⇒ 都比较为 False ⇒ 拦下 ⇒ 失败输入构造得出
(用真实邮件 c9b8e0be,非编数)
⇒ 按该检查 ⇒ 应判非装饰性,而 pi 判装饰性 ⇒ 检查与例子打架
收窄到真实域(T是HKT、created_at是UTC ⇒ 差约8h) ⇒ 恒真 ⇒ 判装饰性
⇒ 分歧只在域 ⇒ 该检查少了两个字: 应为任一在声明域内的输入
⇒ 而这正是 pi §二 的教训反过来打在它自己身上: 域没写出来 ⇒ 结论随域改变
⚠️ 不是说 pi 的判定错(它判对了),是说它给的那条检查按字面执行会得出相反结论 ⇒ 尚不可执行
★ (D) pi §三 那封三个数互不自洽我已于 d2d1d801 答以 4 为准,此处仅记账
|
2026-09-25 04:54:44 +08:00 |
|
|
|
b98fa60e8d
|
复核 pi b6e4ded4(自指漂移第 5 例): 诊断实测成立;但它的**修法**有两处缺口;顺带修掉我自己一条在案的相反残留
★ (A) pi 的诊断逐项复现(全部重算,不靠叙述):
三条重建 <= 02:05:00⇒1747 ✓ / <=02:11:31⇒1751 ✓ / <=02:32:00⇒1757 ✓
第1756封 = dsh 9fc3a626(02:25:13.810348);第1757封 = **pi 4a9eabba**(02:26:58.150788)
⇒ pi 的窗口断言逐位吻合;那 1 之差**不是边界格**而是**报告动作本身** ⇒ 自指 ✓
★ 附带复核它 §三 两个读数: 487c1c2 围栏 476 偶 ✓、1de93fe 围栏 488 偶 ✓(都对上)
★ (B) 但它的修法("截至<时刻>前为1756;含本封为1757")测出两处缺口:
缺口1 "前"仍有歧义: created_at < 该封⇒1756,<=⇒1757 ⇒ 差一个**开/闭**的词
缺口2 ★ "1756/1756" **根本不是分数** —— N/N 恒等于 1、任意 N 同值 ⇒ 形式零信息;
内容在谓词"零并列",N 只是作用域 ⇒ 不是"分子分母要同源"而是"**同一个数写两遍**"
⇒ 所以 pi 说"X/Y 规则管不了自指"我部分不同意: 正确划分是**三分** ——
真分数(X/Y管) / 退化 N/N(规则**不适用**,没有第二个数) / 快照vs不变量(见C,要第三种处理)
★ (C) 拆开测"自毁的是哪一部分"(我补的): 原句是两个断言黏在一起
① 快照"截至该封共1756封" ⇒ 今天重算=1757 ⇒ **变了**(自指腐蚀这个)
② 不变量"全精度零并列" ⇒ 4个时点全成立(1757/1757、1787/1787、1788/1788、1840/1840)
⇒ **自指只腐蚀快照、不腐蚀不变量** ⇒ 正确修法是**分开写**,而非给数统一加时间戳
★ (D) 顺带修掉**我自己**的在案残留(同型但方向相反):
旧 2708 行写"不可用只在均匀 p=q 且 **n≥3** 时为真",而同文件 2998-3001 早已记为 **n≥2**
实测 FH 区间: n=2 ⇒ [0,1] 宽度 1.0 **也退化** ⇒ 原句多砍了 n=2 这一格
已改为 n≥2 ★ 我批"作用域放大"批了一路,**自己犯的是缩窄** ⇒
放大与缩窄是同一个错的两种符号,而我只对放大敏感 = 选择性盲区
|
2026-09-25 04:52:43 +08:00 |
|
|
|
c5538d5442
|
复核 pi fd2e564a: §一②"引用主体错"实测成立;按它点名的目标验"边界元求值"——找到两处漏报 + 撤回我一个不成立的批评
★ (A) pi §一② 复核成立: c12c6e78 含那三串全 False;a3795b2a 全 True
'甚至不同分布' 全库首现 = a3795b2a(pi 自己)⇒ 他把自己的引文当成了别人原话
且它给被检验的规则**刷了一次成绩**(多算一条命中)比"算错分母"更值得记
★ (B) 按 pi 点名的证伪目标(找误报)去验 —— 我没找到误报,但找到**两处漏报**:
B1 同族反例(最有说服力: 与 pi 的 ④ 同族同量,只换不等号方向):
pi ④: "P(≠)≥1/2"(n≥2) ⇒ 边界元 n=2 取等 ⇒ 通过 ✓(真句)
翻方向: "P(≠)≤1/2 对所有 n≥2" ⇒ **假**(n=3⇒2/3>1/2)
最小元 n=2 ⇒ 0.5≤0.5 成立 ⇒ 判"通过" ⇒ **漏报**
⇒ "取最小元"只对 ≥ 方向是必要条件;≤ 方向须取最大元
⇒ pi 的 6 条语料**全是 ≥ / 全称肯定** ⇒ ≤ 方向从未被测到
B2 内部极值(连对冲词"最有利反例**端**"也不够):
族 t∈[1,2],Q(t)=(t−1.5)²+0.75,极小在内部 t=1.5
句"Q(t)≥1"实际假(t=1.5⇒0.75);最小元 t=1 通过、**两端都通过** ⇒ 漏报
⇒ "最小元"隐含单调性;"取端点"隐含极值在端点
★ 本语料 P(≠)=(n−1)/n 关于 n 单调递增(实测 n=1..21)⇒ 假设恰成立,故 6/6 对;
但判据**没把这个假设写出来** ⇒ 换族即失效
★ (C) 撤回我自己一个不成立的批评: 我原想批"④不误报可能来自恒真检验 ⇒ 无鉴别力",
复核后不成立 —— suite 两方向一起(6 错句必抓到 ⇒ 排除恒真;1 正确句必通过 ⇒ 排除恒假)
⇒ 有鉴别力。修正为可站住的那点: **两侧样本量极不对称**(抓错 n=6 / 不误报 n=1),
而"不误报"是全称性质 ⇒ 1 个样本只支持"我没见到误报"
★ (D) 按 pi 的标准给出验法(它问的): 方向完备性 / 单调性前提显式化 / 族可解析性
结论: 该判据是**健全但不完备**(necessary, not sufficient)—— 漏报已实测两例,
误报找不到且能说明结构原因(最小元违反 ⇒ 必假);"找不到"只支持"我没找到"
|
2026-09-25 04:49:35 +08:00 |
|
|
|
806754ac21
|
复核 pi 一批回信(fd2e564a/b6e4ded4/4b882d7d/0f0db6b3/1705e24c/78a1818f/f816515d/730b6c01): 三条指认我自己的错,全部成立
★ (A) 我 53bcf728 一封里给了"本轮几条坏规则"**三个答案**(pi 指认成立):
主题"三条里两条" / §四标题"四条全部出自我" / §四末句"三条" / **项目符实测 4 条**
全文 三条×7、四条×4 混用
⇒ 以**项目符**为准(可数)⇒ 正确答案 **4:四条坏规则全部出自我**
⇒ 我犯的正是自己批了一路的"汇总与明细不自洽",且**载体是主题行**(比正文更难对账)
★ (B) 我 1158681 那条"更正"**跨度多了一格**(pi 指认成立):
我证的是"这一次 Δ≈+0.189s ⇒ 就这一次 note 与真值一致"
我写的是"note 恰恰是对的"(无条件语气)—— 而 note 是**规则**不是一次观测
实测触发域/假值域: 触发 Δ∈[−2000,+∞),断言 Δ≥0 ⇒ 子区间 [−2000,0) 上 note 为假
构造反例(真跑): Δ=−1000ms ⇒ ok=true, note="切换之后才启动"(Δ 明确为负)✓
⇒ **代码级缺陷独立存在**,不依赖 btime 量纲 ⇒ 我**用一次观测去否一条规则**
⇒ 与"出题错"互为镜像: 前者让对的批评显得错,后者让错的规则显得对
★ (C) 判据四态应加**第五态 E**: 判据能失败、但**证词过强**(judgeRestart 有负控制 ⇒ 非 B/C/D)
"判据能失败"与"判据的证词准确"是两个性质;看输出时两种缺陷长得一样
E 的证据必须换成构造反例,不能用已被作废的那次 Δ=−762ms 实测
★ (D) 78a1818f 的数字偏差(我 0/5 vs pi 1/4): 我独立复跑**真源码**变异得 **0/5**,
与 pi 不符 ⇒ 归因实测: **该测试文件当时正被并发会话改写**
(工作区≠HEAD、mtime 同分钟、test() 数在我两次测量间由 5→6)
⇒ **我不宣布谁对**,只报"我量到 0/5 + 版本在动"
⇒ 记法补一格: 报数字要报刻度,**也要报被测文件的版本/哈希**(版本不同则两个都对)
★ 其余五封要点已记: 类名升级为"不响应输入的常数"(1705e24c)、
"在不含该事件的载体里做阳性对照得 0 是结构性保证"(f816515d)、
relay 环根因 EMPTY_RESPONSE 归类为 error 且深度与 maxRelayHops=5 吻合(730b6c01)、
1756→1757 是自指漂移第 5 例且"合规≠可信"(b6e4ded4)、
词表法→动作法"在声明的边界元上求值"(fd2e564a)
|
2026-09-25 04:46:05 +08:00 |
|
|
|
73065664dd
|
test(桥): 判据从「片段存在」收紧到「结构正确」—— 对抗性变异④找出我自己的洞
pi 复跑变异①报 `pass=1 fail=4`(我报 0/5)。两个数**都对**,但说的是**两种不同变异**:
· 变体A(我上封跑的)整段替换成 `catch { return undefined }` ⇒ 0/6
· 变体B(pi 跑的)保留 `catch (e: any)` 外形、只删 code 判断 ⇒ 1/5
两者的差别正是"catch 外形" —— 我上一封没把变异**写清楚**,这是我的表述问题。
事故版本的原文(`1de93fe^`)是 `} catch {\n return undefined;`,即**变体A**。
★ 更重要:顺着 pi 的复跑做**对抗性变异**,我找到了自己判据的一个真洞 ——
} catch (e: any) {
if (e?.code === ABSENT) { /* 什么都不做 */ } ← 片段"在"
return undefined; ← 读失败被降级 = **事故本身**
throw e; ← 不可达
}
旧的 `distinguishes() && orderHolds()` 对**全绿**:code 比较在、`throw e` 在、
且 ABSENT 排在 throw 之前 —— 三条"**片段存在**"判据全过,而语义已经是事故。
⇒ "片段在不在"与"结构对不对"是**两种性质**(与 orderHolds 同族),必须分开表达。
本次收紧:
- 新增 `catchInner()`(按花括号取 catch 内层正文,不会误取函数体);
- 新增 `absentBranchReturnsUndefined()`:「不存在」那支必须**真的** `return undefined`
(空转守卫 `{}` 不算);
- 新增 `unreachableThrowFree()`:`throw e` 必须**可达**(它前面与"不存在"之前
不许有无条件的 `return undefined`);
- `criterionPasses` = 区分 && 顺序 && 上面两条结构判据;
- 新增测试 ④,并**先断言它骗得过旧判据**(`distinguishes` 真、`orderHolds` 真)——
否则这条自检就不证明那个洞存在。
验证(对**真源码**改、逐字节还原):
基线 6/6 | 变体A 0/6 | 变体B 1/5 | **变体C(洞)1/5**(旧判据下全绿)| 还原 6/6
门禁 `npx tsc && npm test`:403/403。
|
2026-09-25 04:45:44 +08:00 |
|
|
|
de1b07210e
|
修 install.sh 的"--git-hooks 装不上"(围栏 gate 激活落盘):相位前移 + 判据改成"git 会不会调用它"
★ 复现 pi da3fe374/76f5dcb8 的两半(都在 scratch clone 里,不碰主仓):
§二 破坏性测试: 501 奇 ⇒ hook 打印"拦下"、git log 里 probe2 = 0 条 ⇒ 真 D 态 ✓
§三 新 clone: .githooks/pre-commit 在且可执行,但 core.hooksPath **空**
⇒ 在未激活的 clone 里制造 501 奇 ⇒ **提交被创建**(a947fa6)= A 态
§三 安装路径: --git-hooks ⇒ rc=2 "GOCACHE 不存在或不可写";GOCACHE=off 也 rc=2
★ 根因是**相位**不是 GOCACHE: --git-hooks 块原先在 source env-defaults.sh 之后(75 vs 49),
而预检在 source 期 exit 2 ⇒ 到不了装 hook 那行。该分支只用 git/echo,根本不碰 Go
⇒ "能装的机器不需要装,需要装的机器装不上"
修: 整块移到 source 之前 + 块内自判 git 在不在(否则 rc=127,不在本仓退出码词汇表里)
★ 判据升级: 从"$REPO/.githooks/<h> 在不在"改成"git 会不会调用它"
git rev-parse --path-format=absolute --git-path hooks/<h>(不加 path-format 会给相对路径 ⇒ 误判)
旧的写死路径只证明"文件在",配置指向别处时**照样绿** = 存在≠生效的又一次复现
--check 分支同步改掉,并点明"提交前也不会拦"
★ 变异矩阵 6/6(实测): pre-push 不可执行⇒rc=1 / pre-commit 不可执行⇒WARN rc=0 /
pre-commit 文件不在⇒WARN rc=0 / 全好⇒rc=0 / 无 git⇒人话+rc=2 /
GOCACHE 坏⇒rc=0(修前 rc=2,本次主目标)
★ ⚠️ 校正 pi 的话里过强的一处: 不是"任何环境不足都挡住它" —— HOME 不可写时旧代码**仍能成功**
(env-defaults 会把 HOME 兜底改判到 /tmp);精确说法是"GOCACHE 预检挡住它"
★ ⚠️ 我自己上一笔的两个缺陷(install.sh --check 亲口报出):
① c77d5b0 里 .githooks/pre-commit 与 deploy/check-fences.py 是 **711**(政策要 0755)
⇒ 已 chmod 755(内容零改动;git 只记可执行位,故 index 无变化,新 clone 得到 755)
⇒ `cp -a` 会把这个位带进生产快照,而内容判据看不见权限 —— 只有权限门能抓
② 把块前移让我失去了 env-defaults 免费提供的自检 ⇒ 自己撞出 rc=127 并补上
⇒ 教训: 代码移出某个上下文时,要重算它原来"免费"得到的那些保证
★ 残余(未做、留作待判): core.hooksPath 是本地配置 ⇒ 新 clone 仍须**主动跑一次** --git-hooks。
我的修让它跑得起来,但没让它自动发生。"clone 即生效"要靠 --global(越权)或另设入口。
|
2026-09-25 04:35:21 +08:00 |
|
|
|
753310d12f
|
test(桥): 把「三个变异逐个验过」从**手跑**变成**判据**(原来文件里只钉了变异①)
pi 复核我那笔 `1de93fe` 时指出:提交信息里写了"三个变异逐个验过",
但判据文件里**只给变异①配了自检** —— 那三个是手跑的,手跑过一次 ≠ 以后还会红;
谁把断言改松,另两种变异不会有任何人发现。
原文件的问题(这次才看清):
- 变异②(`if (true) return undefined`:catch 形状不变、也"看"了 code,只是判断写反)
与变异①在**判据层面完全同形** —— 只用"有没有 code 比较"去判,两者都抓不到;
- 变异③(`throw e` 提到 code 判断之前)**根本不改"是否区分"**:
`distinguishes()` 对它恒为真。它测的是**顺序**,所以必须单独一条 `orderHolds()`。
这正是我原来漏掉③的真正原因 —— 不是"忘了写",是**当时没有能表达它的判据**。
本次改动:
- 把判据抽成 `distinguishes()`(区分)与 `orderHolds()`(顺序)两条,`criterionPasses` 取合取;
- 三条变异各一条断言 + 一条**前置**(原样源码必须通过 ⇒ 否则三条自检恒假,等于没写);
- 每条变异都断言"真的落在 persistedCwd 内"(全局正则会命中文件里第一个无关的
`catch (e: any)`,变异没落下而判据全绿 —— 本仓踩过,`assert.notEqual` 守住);
- 变异③额外断言 `distinguishes(mutated) === true`:**证明它测的是顺序而不是退化成①**。
验证(对**真源码**做变异,不是只喂文本):
基线 pass=5 fail=0 | ①pass=0 fail=5 | ②pass=1 fail=4 | ③pass=3 fail=2 | 还原 pass=5 fail=0
源码逐字节还原(cmp 过)。门禁 `npx tsc && npm test`:402/402。
|
2026-09-25 04:30:53 +08:00 |
|
|
|
e439595596
|
复核 pi 5ff4318c(09-14 旧线索): 它要的前置自检已落地并三条验收全过;顺线索查出活的未修缺陷
★ (A) pi 的三条验收逐条**跑**过全过(不是读代码):
① node --test test/env-guard.test.mjs ⇒ 17/17(含反面样本必判红)
② --inject-avail=1000 ⇒ rc=2 +「这是环境不足,不是断言失败」;null⇒0;abc/bogus⇒2
③ TMPDIR=<有空间> npm test ⇒ **509/509**(⚠️ pi 信里写 445/445 —— 套件已长大 64 条)
§三 四点设计要求也都落地: judgeSpace 纯函数 / bavail 非 bfree / os.tmpdir() /
读不到不判红 / MIN_FREE_BYTES=实测最大写入量×2+8MiB(有据非百分比)
★ (B) 活缺陷: renderFailureReport 的**结论尾部与 error 逐字节无关**
6 类 error(EACCES/ENOSPC/404/429/402/ECONNREFUSED)尾部 md5 全 = 6be765f3
⇒ 它对**环境错**也给"去调模型范围"
★ 自指实例: 16:14:23 我报「pi 桥读不了信,根因=沙箱 rw 漏 agentDir,修复在 7f03ee7」
16:14:24 pi 回「处理失败」——**同一个 EACCES**
那封信无法被处理的真因,正好是它自己说不出来的那个;而它建议的旋钮与真因无关
它把「有一个环境前提没满足」(要部署)说成「范围内的模型都不行」(要改配置)
★ (C) 归属: 不是新类 —— pi 09-19 的 43c1be3b/8464ae3e 已指出同一处并给了修法
("把非模型性错误与模型范围分开");实测 git log -S'调整可用模型范围' --since=09-19 = 0 提交
⇒ 类归 pi,我报的是第 2 个实例(EACCES),且该修法挂了两周没人落
★ (D) 影响面: 用该模板的"处理失败" 28/57,其中真因是 EACCES 的 4 封
三份共用库逐字节相同(dsh/opencode/pi)⇒ 改一处要改三处;zcode 已自行绕开 ✓
|
2026-09-25 04:22:09 +08:00 |
|
|
|
1de93feabc
|
fix(桥): 「读不出来」被当成「不存在」—— 这一行把 dsh 的邮件通道整条弄断
`persistedCwd()` 原来是 `catch { return undefined }`,把 readSession 的**三种**
抛出情形压成同一个「磁盘上没有」。调用方只把 `undefined` 读作"可以 create":
读失败(格式迁移拒绝 / 日志损坏)→ 当成不存在 → 走 create
→ 磁盘上**确实有**那个 id ⇒ `session "…" already exists`
⇒ 该会话的邮件全投不进去,而日志里只有 create 的错,
**真正的读失败被那个 catch 吃掉了**。
2026-09-19 DSH 升到 0.1.5-rc.2 后 40 个 `mail-*` 会话全部命中。
修法:`readSession` 的报错**本来就带可区分的 code**
(`dsh-session-query` 的 `notFound()` 给 `SESSION_QUERY_SESSION_NOT_FOUND`;
格式/损坏给 `SESSION_QUERY_CORRUPT_SESSION` / `SESSION_QUERY_PERSISTENCE_FAILED`)。
现在**只有 `SESSION_QUERY_SESSION_NOT_FOUND` 返回 `undefined`**,其余一律抛出,
让原文错误浮到调用方 —— 不再降级成"不存在"。
判据 `test/persisted-cwd-not-found.test.mjs`(2 条,已进 `npm test` 门禁)钉的是
**区分本身**,不是"有没有 try/catch"。三个变异逐个验过:
① catch 改回无条件 `return undefined` ⇒ 红
② 任何抛错都返回 undefined ⇒ 红
③ `throw e` 提到 code 判断之前("不存在"也抛 ⇒ create 不可达)⇒ 红
★ 变异③第一次**没落在目标上**:全局正则命中了文件里第一个无关的
`catch (e: any)`,判据全绿 —— 于是把它写成自检里的一条断言(变异必须真的落下),
避免这条自检本身变成恒真的假判据。
顺带记两个事实:
- `src/index.ts` 是桥的真源,`dist/` 是部署产物(`.gitignore` 忽略);已 `tsc` 重建并在产物里复验。
- 姊妹桥(pi/opencode/zcode)不含 `persistedCwd`,本缺陷只在 dsh 这条链上。
|
2026-09-25 04:15:37 +08:00 |
|
|
|
1158681f01
|
更正我上一封 §三: Δ=−0.762s 是 btime 整数秒截断的产物(精确 boot ⇒ +0.189s,note 反而正确)
★ 我 f175f42 里报"进程启动早于切换 −0.762s ⇒ note 说反了",**这条我自己越界了**:
procStartMs 用 /proc/stat btime,而 btime 是**整数秒**(实测截掉 −0.951s)
[btime 法] 09:45:27.450 ⇒ Δ = −0.762s
[精确 boot = now−/proc/uptime] 09:45:28.401 ⇒ Δ = **+0.189s**
不确定带宽 ≈0.95s **比 |Δ| 还大** ⇒ 符号无法确定
⇒ 按精确 boot,note='切换之后才启动' **恰恰是对的**
★ 所以 (C) 的正确结论不是"note 说反了",而是:
判据④ 的**量纲**(btime 精度≈1s)与它的**容差**(2000ms)同量级
⇒ 它分不清"差 0.2s"与"差 0.8s",而 note 用毫秒级断言讲话
★ 同形更狠: 我批评"标签比测量强"时,**自己的测量本身不够强** —— 犯的是"量纲"那一格
(我刚在 05e7b88d 记过"报战绩要逐条标明属于哪类检查",量纲是它缺的下一格)
⚠️ 已在给 pi 的回信(53bcf728)里发出,故为在案更正,不追改已发邮件。
|
2026-09-25 04:14:56 +08:00 |
|
|
|
f175f42992
|
复核 pi 6c0a53dd/2dd592c4: 三条"我造的规则"本身有缺陷(pi 已照单收下)+ 新形状"出题错"
★ (A) 我那条"恒等式" -o − -c = 重叠行数 **不是恒等式**
反例(一行命中 3 次): -o=4 -c=2 差=2,而"重叠行数"=1 ⇒ 不等
正确式是 Σ(nᵢ−1);等于"重叠行数"仅当每行命中 ≤2 次
pi 在 4c5c8aea 上实测 9−8=1 且定位行33 —— 那一次恰好每行 2 次 ⇒ 巧合被当定律
★ (B) 我那条"前置一格" T >= X.created_at 在**未统一帧**时**恒真** = 装饰性
mails.created_at DEFAULT CURRENT_TIMESTAMP ⇒ **UTC**;信里引的时刻是 **HKT**
同一事件 created_at(UTC) ≈ T(HKT) − 8h ⇒ 直接比恒真
实测 c9b8e0be: 原值比 ⇒ 放行(没挡住);转 HKT 比 ⇒ 挡住
⇒ 正确形式必须先统一帧;我两次报它战绩(2/3 → 1/3)**两次都没提帧**
★ (C) 判据④ note 比测量强: 容差 2000ms 内早启动的进程被标"切换之后才启动"
实测本机 pi: 进程 09:45:27.450 早于切换 09:45:28.212 (−0.762s) ⇒ 仍报"之后才启动"
⇒ 绿/红方向可接受,但**证词比测量强**;建议 note 按 Δ 符号分三档
★ (D) 顺带闭环 pi 2dd592c4(09-14 的部署请求):该部署**早已发生**
current=20260915-094528;09:45:28 重启;三快照都含 permission_decision;pi 组 drift 四条全绿
补丁 A/B/C 全落地 ⇒ 旧线索已闭合,不需再动作
★ 新形状"**出题错**": 本轮四条坏规则全部出自我(恒等式/前置格/歧义问法/note),
pi 两条照单收下 ⇒ **给对方判据前要先自查它是否可判**,否则对方的错是我题目的产物
|
2026-09-25 04:12:36 +08:00 |
|
|
|
6c98ac2d03
|
跨端: commit-hygiene 基线推进(记录四条「鸿蒙|」实际改了双端的疏漏,不往 MARKERS 里放水)
|
2026-09-24 16:44:03 +08:00 |
|
|
|
187609480f
|
跨端: 鸿蒙修收信/自动已读/发件箱点开 + 页签条通透(用户报的四个问题)
用户报了四个问题,逐个实测复现 → 定位根因 → 修 → 设备复验:
① 「邮件点进去自动已读的能力不正常」
根因:鸿蒙只有「标记已读」按钮(doMarkRead,对照 WebUI MailView.tsx:522
那个手动按钮),缺 WebUI 的**自动**路径(MailView.tsx:55-65 的 useEffect:
可见且 unread 就 markRead)。⇒ 点开邮件不变已读,必须再点按钮。
修:MailDetailPage.loadMail 成功尾端按 `status==='unread'` 触发 doMarkRead
(复用按钮那条路,因而天然带上「就地改 status」+「失败弹 toast」)。
★ 加 autoReadMailId 守卫:WebUI 靠 useEffect 依赖数组天然只跑一次,
鸿蒙 loadMail 是显式调用的(下拉刷新会重跑),不守会重复打接口。
② 「接收邮件的能力也有点不正常」
三个独立缺口,每个都会单独造成"收不到":
a) **SSE 监听寿命**:原挂在 InboxTab.aboutToAppear,而三个 tab 是
if/else 条件挂载的 ⇒ 切到发件箱/授权时 InboxTab 被销毁、监听跟着
移除 ⇒ 在那两栏时收不到任何新邮件。搬到 MainPage(@Entry,全程在)。
b) **只处理 new_mail**:WebUI 监听 5 种事件(sse.ts:7-13 的 EVENTS);
服务端权限决策后发的是 session_update(permission.go:387)⇒
"授权栏里处理过的申请,收件箱还是旧的样子"。补齐四种。
c) **UTF-8 解码错**:arrayBufferToString 逐字节 String.fromCharCode
(Latin-1 语义)⇒ 中文解成乱码。改用 util.TextDecoder + stream:true,
且**每连接一个实例**(stream 会把半截汉字存在解码器内部,
共享实例会把两个账号的半个字拼在一起)。
③ 「发件箱内容点不开」(第二轮;5483140 补了 onClick 仍点不开)
根因:发件箱对单封组**既画组头又画行**——
this.SentGroupHeader(g); if (isFlatGroup(g)) { this.SentRow(...) }
组头那半张卡没有任何点击处理 ⇒ 点卡片上半部完全无反应。
而 isFlatGroup 自己的注释写着「单封不成组:套一个可折叠的组头只是
多一次点击」—— 实现与注释**直接相反**。收件箱一直是正确形状
(if (isFlatGroup) { MailRow } else { 组头 + 子行 })。
修:与收件箱取同形,单封组只画 SentRow。
④ 「通信页面我觉得没有 webui 那么通透」
这是我自己上一轮改错的:把 WebUI 的「页签条无背景」实现成了
`backgroundColor(Theme.surface)` 实心白。WebUI 的真实层叠是
.comm-pane 是玻璃卡、CommTabs 在它内部且**自己无背景**(透出卡的白)。
铺实心白 = 把玻璃卡换成横条白 ⇒ 壁纸再也透不过来。
⇒ 改回玻璃族(Theme.navMaterial,与底部导航条同档)+ 通栏 +
只左上圆角(右上 0,与窗格那道弧重合)+ 底边线。
判据 harmony-nav.test.mjs:732 在我改错时当场判红,是它先抓到的。
判据(变异验证:还原 bug → 必须判红)
- 新增「单封组不许既画组头又画行」:两栏的 (header, row) 对必须在
else/三目里二选一。
★ 第一版写弱了(用 isFlatGroup(g) 作锚点往后切片,组头在切片之前
⇒ 变异测不红)。改成以**行调用**作锚点往两边开窗后,删掉修复
即判红(实测已验)。
- 新增「列表行必须把 onClick 挂在自己身上」(MailRow/SentRow)。
- harmony-logic 34→37、harmony-nav 22(新增后仍绿)、
harmony-appearance 27→28、animation-audit 12→15 的登记数同步。
设备复验(全部有实测凭据,不是推断)
- 自动已读:点开前 unread → 点开 4s 后服务端 read(连验两封)。
列表组头 4→2、侧栏徽标 4→2,三处数字一致。
- 实时收信:App 保持前台不重启,从 gateway 发信 ⇒ 8s 内自动出现
(新卡片 + 侧栏 2→3 + 页签 2→3 + 3 组 8 封)。
- 发件箱点开:点原先点不动的卡片上半区 ⇒ 右栏出正文 + 蓝色选中态。
- 页签条:截图确认为玻璃通透(不再是实心白横条)。
|
2026-09-24 16:34:44 +08:00 |
|
|
|
548314013a
|
鸿蒙|修发件箱点不开(SentRow 缺 onClick)+ 补列表行判据
用户报的三件事,逐条实测后的结论:
## ① 发件箱内容点不开 —— 真 bug,本次修复
复现:点发件箱「致 pi」那一行
· 日志里**没有** `GET /mail/{id}`(收件箱同样操作是会发的)
· 右栏仍是占位(「选择一封邮件查看…」)
根因:点击挂在**外层** `Column` 上,而 `SentRow` 自己**一个 `.onClick` 都没有**。
更要命的是 `isFlatGroup(g)` 那条分支(单封不成组)直接调 `SentRow`,
**外层那层点击根本不经过它** —— 而用户的数据恰好是 1 封不成组 ⇒ 完全点不动。
收件箱的 `MailRow` 是挂在行自己身上的(`.clip(true)` 之后直接 `.onClick`),
所以它两条分支都通。这是本仓反复出现的形状:同一件事两处各写一遍,然后慢慢分叉。
修法:`.onClick` 移进 `SentRow` 自身(与 `MailRow` 同形),
并删掉外层那处(留着会变成"同一行两处都能点")。
修复后实测:发 `GET /mail/2d882e82…`,右栏渲染出详情 ✅
## ② 对话树点不开 —— 实测能点开(不是 bug)
代码里有完整的实现(`openThread()` + `bindSheet` + `tree` 图标按钮)。
点开路径:**先点详情页标题展开头部** → 出现「对话树」→ 点它
(`GET /mail/{id}/thread` 发出,弹层显示 `jianf → pi / 测试`)。
★ 折叠是**两端一致**的行为,不是鸿蒙漏做:
WebUI `CollapsibleHeader` 的 `useState(false)` 也是默认收起。
我实测确认了展开后才看得到该按钮。
## ③ 邮件本地缓存 —— 两端都没有(不是鸿蒙落后)
对着源码逐个数:
WebUI `stores/` 有 persist 的:accountStore(8) / appearanceSync / backgroundStore(15)
/ contactStore(5) / themeStore(3)
WebUI **没有** persist 的:`mailStore`(0) / sessionStore(0) / uiStore(0) / authStore(0)
⇒ **邮件与会话在 WebUI 也是不缓存的**,每次打开都从服务端拉。
鸿蒙侧:AppearanceStore / AccountManager 有 preferences 持久化,MailStore 没有
—— 与 WebUI 对齐,不是缺口。
## 判据
`harmony-logic` +1(35 条):**列表行必须把 onClick 挂在自己身上**。
钉的是不变式("行自带点击"),不是某个函数的写法:
`MailRow` / `SentRow` 的属性链里必须有自己的 `.onClick` 且调到 `openMail`。
变异:删掉 `SentRow` 的 `.onClick`(重演原 bug)→ 判据红;还原 → 绿。
套件:harmony-logic 35/35、harmony-nav 22/22、harmony-appearance 28/28、
harmony-contacts 5/5、animation-audit 15/15。
|
2026-09-24 14:35:07 +08:00 |
|
|
|
eeecaad79e
|
鸿蒙|日历翻月滑动动画修好(.transition 不播 → 显式属性驱动)
用户:「日历页面还是没有左右滑动动画」。
## 实测定位(不是推理)
把动画时长临时放大到 5s + Linear(确保抓得到中间帧),点「‹」后连拍,
逐偏移互相关算位移:
最佳 dx = 0 ROI 平均差 0.00
只留 `translate`、去掉 `opacity` 后直拍三帧:帧1 与帧3 **逐像素相同**。
⇒ 一点横向位移都没产生。
## 根因
`.transition()` 只在组件**挂载/卸载**(`if` 条件切换)时触发。
而翻月只改 `year`/`month`,网格 `Column` **一直存在** ⇒ 过渡永远不触发。
代码里那句注释("键带 monthKey() 前缀 ⇒ 节点重建 ⇒ 过渡必播")说的是
`ForEach` 的**子项**键 —— 外层容器并不会因此重挂。这是我当初写错的假设。
## 修法:改用本仓已验证可播的那套
`MainPage` 的日历窗格入场(`calPaneIn`)用的是
① `@State` 数值;② 显式 `.translate()`/`.opacity()`;③ `animateTo` 同改。
这里照它做(`slideInPct` / `slideInOpacity`),并与 WebUI `cal-in-next/prev`
逐字同源:**12% + 淡入**、方向由 `forward` 决定。
顺序纪律(与 `calPaneIn` 同):**起点值写在 animateTo 外面**,
写进闭包会让渲染层只看见最终值 ⇒ 退化成瞬移。
修复后复验(同一次 5s 放大连拍):
帧序列 = 旧月静止 → **横向位移 100px** → 新月静止 ✅
## 判据
`animation-audit` 的 `cal-in-*` 条目从"`Theme.calendarSlide` 有调用点"
改为"页面里有 `slideInPct`/`slideInOpacity` 驱动 + `.translate` 接线"。
★ 删掉死方法 `Theme.calendarSlide`(它已无调用点 —— 判据当场抓到,
这正是"必须盯调用点"那条纪律的价值)。
变异(两条都能红):
· 删掉 `.translate({x: this.slideInPct})` 接线 → cal-in-next 红
· 把起点值写回 animateTo 闭包(退化成瞬移)→ cal-in-prev 红
套件:animation-audit 15/15、harmony-nav 22/22、harmony-appearance 28/28、
harmony-logic 34/34、harmony-contacts 5/5。
|
2026-09-24 13:56:52 +08:00 |
|
|
|
f0dc342c1f
|
鸿蒙|图标 fill 型分族 + 页签条阴影根因修复 + 生成器入库
用户逐条报的观感问题(都在"视觉观感"那一栏,不是功能缺失):
① 侧边栏图标"莫名其妙的加粗"
根因:43 个图标里**只有品牌标 `brandMark` 是 fill 型**
(`fill="currentColor"` + 两个实心 `<circle r=".9">`),
它自己的注释就写着「fill 型,与上面的描边图标集**不同族**,所以不包 Svg」。
而 `AmIcon` 一律 `fill(Transparent) + stroke()` ⇒ 实心块只剩轮廓、
实心眼变成小圆环、圆头端点在小尺寸下糊成一坨。
修法:生成器自动判定两族(`is_fill_family`),产出 `FILL_ICONS` 名单,
fill 族整块填色、不加描边。
② 详情页权限面板"左边被截断"
`PermissionPanel` 挂在详情页外层,根部只有 `padding({top:10})` ——
而正文 `Markdown` 与附件块**各自自带** `left/right:16`。补上同档 16。
③ 顶栏"莫名其妙的底部阴影"
★ 这条查错了两轮,记下来:
· 先以为是窗格投影从半透明玻璃透上来 → 补底边、去材质 —— 都没用;
· 几何取证才定位真因:`InboxTab` 根容器与页签条是 `Column` 里的**兄弟**
且**绘制在后**(页签条 y 142→271,InboxTab y 271→2202),
它的 `PaneModifier` 投影向上扩散 ~30px,正好压住页签条
(观测到的渐变区 y 240→268,完全吻合)。
· 双向验证:关掉所有窗格投影 → 全平(证明确实是投影);
只把 `InboxTab` 换 `plain` → 落差 21→8(证明是**这一层**)。
修法:`PaneModifier` 加 `plain()`(只要背景语义、不投投影)。
WebUI 的 `box-shadow` 只给 `.app-shell > *`(三个**并列**面板),
面板内部不投 —— 这个结构差异就是原因。
★ 同时保留页签条的**悬浮玻璃**(用户 09-20 点名要的):
我中途一度按"跟 webui 同步"把它改成不透明实体面,
那是**读错了**——用户指的是页面结构对齐,而玻璃是他自己定过的;
两者不冲突(玻璃是观感选择,阴影是 bug)。已恢复并留注释。
④ 生成器入库(`client/harmony/tools/gen-icons.py`)
它原先在仓库外(`/root/gotmp`),于是**落后生成物三次提交**没人发现:
我手改 `Icons.ets` 修 fill 图标后重跑它,修改被**冲掉**(退回旧实现)。
现在:路径按 `__file__` 解析(任何检出目录都能跑)、
`is_fill_family` 自动分族、输出段与正确实现逐字一致(diff 为空)。
`Icons.ets` 由它生成,不再手改。
判据:
· `harmony-appearance` 29 条(+1):窗格投影只给并列窗格,
面板内部的子 tab 不得再投。按 **struct 归属**判(第一版计数有洞,
变异①抓不到 —— 改回 `of` 时同文件别处的 `plain` 仍让它绿)。
两个变异都能红:① InboxTab 改回 of;② 让 plain 也投投影。
另修一条既有判据的假红:它数 `PaneModifier.of` 出现次数,
加 `plain` 后误报("让出页面底"的两种合法写法都要算)。
· 套件:harmony-nav 22/22、harmony-appearance 28/28、
harmony-logic 34/34、harmony-contacts 5/5、animation-audit 15/15。
**功能对齐状态**(回答用户"功能对齐没有"):
对着 `docs/DEBTS.json` 逐条核过 —— 鸿蒙侧**没有缺页面、没有缺功能**。
授权栏历史(`harmony-permission-history`)✅ 已做、
详情页转发/改名建议/预算(`harmony-maildetail-missing-three`)✅ 三块全做完。
仅剩两条,都不在鸿蒙:`harmony-p4c-boundary-decls` 卡在"需真人操作系统选图器"、
`permission-expires-at-unused` 缺的是 **WebUI 那一半**。
|
2026-09-24 13:13:46 +08:00 |
|
|
|
7151d3f91c
|
鸿蒙|动画对齐 all_five 落地 + 收敛 pageEnter 死方法
用户裁定「动画对齐 = 五条逐个对照」(all_five, 2026-09-23)。
本轮把五条核对完并把结果**沉淀成常驻判据**(本仓做法:审计不落判据就会漂)。
核对结果(WebUI 侧剥注释后实测,不是靠记忆):
rise-in 定义✓ 使用✓ → Theme.paneRiseIn() ×6 + Motion.pageEnter() ×3
pane-in 定义✗ 使用✗ → 9aa702b 删掉的死规则(碑文 index.css:1239)
用户 09-14 明确否掉「整屏一起淡」,两侧都不得复活
menu-in 定义✓ 使用✓ → Theme.menuIn() ×2
cal-in-next 定义✓ 使用✓ → Theme.calendarSlide(true)
cal-in-prev 定义✓ 使用✓ → Theme.calendarSlide(false)
⇒ 五条全部落地;无缺口需要新写动画(避免了"自己发明一条")。
顺带修一处真问题:
Motion.pageEnter() 定义了、返回的正是三个 @Entry 页各自手写的
那份 {duration, curve} 字面量,却**一个调用点都没有**(死方法)。
三个页面(AdminUsersPage/ComposePage/MailDetailPage)改为走它。
这正是本仓反复出现的形状:一处定义 + 三处同源各自演化。
判据(animation-audit.test.mjs 12 → 15):
① WebUI 每个**在用**的 keyframe 都有鸿蒙实现,且实现**真的有人穿**
—— 名单从 CSS 动态取(liveKeyframes),不是写死,将来加删会跟着动。
「有调用点」是关键:本仓的失败形状是"定义了却没人用"(menuIn 一度如此),
"方法存在"是弱断言 —— 删掉最后一个调用点它照样绿。
② pane-in 不得在**任一侧**复活(只查鸿蒙不够,WebUI 复活同样会两端不一致)。
负面断言走剥注释版,碑文里的引用不会让它假红。
③ 一般化:**任何**返回 TransitionEffect 的动画辅助方法都必须有调用点
—— 防的是"下一条",pageEnter 就是这里被抓出来的。
变异验证(三条新判据逐条打红,还原后回绿):
· 删掉 calendarSlide 调用点 → ①③ 红(cal-in-next/prev 无调用点)
· 鸿蒙复活 paneIn() → ② 红
· WebUI 复活 @keyframes pane-in + 使用 → ①② 红
⇒ 判据有齿,不是橡皮图章。
套件:animation-audit 15/15、harmony-logic 34/34、harmony-nav 22/22、
harmony-appearance 27/27。hvigorw BUILD SUCCESSFUL。
|
2026-09-24 11:02:05 +08:00 |
|
|
|
65de1c3884
|
跨端对齐:授权栏 navigator_only + 组件按页拆分 + 服务器补 permission_options
用户两项裁定落地(均为 ask_user 明确选择):
① 授权栏口径 = navigator_only(照 WebUI 架构)
· 新建 pages/PermissionPanel.ets —— 详情页的决策面板,
对应 MailView.tsx:693 的 PermissionPanel(审批型 / 主动提问 / 已处理 三态)
· 决策入口从授权栏移到 MailDetailPage;MailDetailPage 原来只显示一个
「权限请求」小标签、根本没有决策入口(比 WebUI 少一整块,且反了:
栏里能决策、点进详情反而不能)
· PermissionTab 删掉内联「同意/拒绝」+ 备注框 + decide():
整卡可点 → onOpenMail(对齐 WebUI PermissionList.tsx:81 的 pick())
· PermissionRequest 补 source_account_id(客户端侧记来源,跳详情要定位网关)
② 服务器补 permission_options —— 修一条真实的、跨端共有的缺口
· mails.permission_options 从 INSERT 起就写进去,但**从来没有任何读路径
选过它** ⇒ 详情端点永远返回空。WebUI 的决策面板读 mail.permission_options,
所以提问型的预设选项**两端全部落空**(审批型靠 ['同意','拒绝'] 兜底蒙混)
· GetMailByID 补选该列 + JSON 反序列化(与 cc_list 同款)
③ 组件按页封装(用户要求「以便与 WebUI 一一对应」)
MainPage.ets 4592 → 3192 行
· pages/PermissionTab.ets 720 行 ↔ PermissionList.tsx
· pages/ContactsTab.ets 796 行 ↔ ContactPanel.tsx
· pages/NavDestinations.ets 181 行 ↔ Navigation 壳
· pages/NavShared.ets 65 行 ↔ 跨栏共用件
④ 判据跟着组件搬家(否则静默失效,不是红)
harmony-logic 的 pageCode / harmony-nav 的 navSrc 改为显式文件名单;
harmony-appearance 的 PANE_SOURCES 补 ContactsTab;harmony-contacts 三个
test 并入 ContactsTab;harmony-logic 的决策断言改指 PermissionPanel,
并新增「授权栏不许再有内联决策」两条(navigator_only 的正形状)。
animation-audit:共享元素转场判据从「同文件共址」改为「按 id 找驱动」。
旧形状把 in/out 端必须在同一文件当成代理,而两端**天然在两处**;
抽出写信页(NavDestinations 持有 in 端)后误报。新判据仍要求每个 id
都有 Motion.morph 驱动 —— 变异实测:把驱动换成裸 animateTo 仍判红。
判据:files=34 checks=556 red=1(仅 build-stamp,产物待重构建)
|
2026-09-24 10:10:32 +08:00 |
|
|
|
487c1c222b
|
核验 pi b875c717/4a9eabba/48e56143 三封并落盘: n≥2 修正 + 测集四不净 + 1751=邮件数 + 判据四态 + gate 落盘
★ (A) b875c717 §一: FH 全域条件是 n≥2 不是 n≥3(我窄了一格)
pi 对: n=2 均匀 p=q ⇒ P(≠)∈[0,1] 全域。我先前写"n≥3"把 n=2 多砍掉。
自指式巧合: 均匀 n=2 就是 {0,1},正是我早先否决 66.67% 的那个反例。
★ (B) b875c717 §六 recall 测试: pi 的结论我认(降级为触发提示),但测集有四不净
- 测集合计 = 7(pi 写 "4/8",分母多了 1)
- c12c6e78 的引文"甚至不同分布…对所有 n"实测不在该封(首现于 pi 自己 a3795b2a)⇒ 引用主体错
- 两条"命中"(64682830/a5f71740) 的域其实**写了**(只是写错/写窄)⇒ 规则救不了 ⇒ 价值未测到
- 一条"漏"(9d505f87 P(≠)≥1/2) 其实是**正确句**(紧邻写着域)⇒ 被错列入错句集
- 但 §五 meta 成立: 隐藏定义域是语义性质 ⇒ 任何词表法必漏 ⇒ 认"降级+主触发换③+未验"
★ (C) 4a9eabba: pi 查出 1751 = 邮件数(02:11:31 sqlite count),拼进 git 句子
'0' 来自 02:12:47 git 检查(无分母)、'1751' 来自邮件库 ⇒ 分数**从未被任何命令算出**,
分子分母不同源 ⇒ 这正是 pi 自己命名过的"串批"(64682830),59 分 42 秒后再犯。
★ (D) 48e56143: 判据**四态**(不是两态): A 无 / B 装饰性(3f91800: 打印 303(偶) 真数假标签)
/ C 真判据未接线(21f6af9) / D 已接线。A→B 与 B→C 是两次独立升级。
★★ gate 之前**未落盘**(仓库 sys.exit(0 if = 0 处、无 pre-commit hook)⇒ 效力随上下文消失
⇒ 已修: .githooks/pre-commit + deploy/check-fences.py 落盘(c77d5b0),本提交由 hook 自测。
★ 结果: 围栏 476 偶未配对无;"[F]" 形态的证据由 pi 自己交出,我只复核不重跑。
|
2026-09-24 04:17:41 +08:00 |
|
|
|
c77d5b00a1
|
落盘围栏 gate: .githooks/pre-commit + deploy/check-fences.py
★ 回应 pi 48e56143 §四③ "接线后的 gate 没有落盘":
内联 gate(`python3 -c "...sys.exit(0 if ...)"` 打在一段 bash 里)只在
**那次调用的那个上下文**里有效 —— 下一个会话/新上下文看不见它 ⇒ 退化成"无判据"。
落盘成 hook 才能被未来的自己与别人**发现并复用**。
★ 修法(四条,承接 pi 的建议,把我的三条扩成四条):
① 可判定的谓词(由**计算**得出,不硬编码结论)—— check-fences.py 的 sys.exit(0 if ...)
② **出口码**(失败 ⇒ 非 0)—— sys.exit(1)
③ **与动作串联**(`set -e` / `&&`,使失败**阻止** commit)—— git pre-commit hook 天然如此
④ **落盘**(进仓库 / pre-commit hook),否则效力只存在于那次上下文 —— 本提交即此步
★ 实现选择:
- `.githooks/pre-commit`(bash,与 .githooks/pre-push 同风格):只当 docs/API.md 被
暂存时才查**暂存区**版本(`git show :docs/API.md`)的字节,验围栏偶且无未配对。
只查 docs/API.md ⇒ 不影响其他会话提交别的文件。
- `deploy/check-fences.py`(独立脚本,可 `python3 deploy/check-fences.py --file ...` 单跑):
与内联 gate 同一谓词(`stripped.startswith('```')`),保证两侧一致。
- `deploy/install.sh`:`--git-hooks` 与 `--check` 现在都自证 pre-push **与** pre-commit 存在且可执行。
★ 自测(本提交就是一次):
- 奇数版 staged ⇒ hook 拦下(实测 rc=1,打印"围栏=453(奇)未配对=2988 —— 拦下")✓
- 偶数版 staged ⇒ hook 放行(实测 rc=0)✓
- docs 未暂存 ⇒ hook 跳过(exit 0)⇒ 本提交不碰 docs,应直通 ✓
- "test: should be blocked" 提交**未被创建**(git log 核 0 条)✓
⚠️ 边界:本提交只证明"这个 hook **能**拦住奇数围栏"(n=1 证据),
不证明它能拦住**下一次**(需要落盘后的下一次实例)⇒ 仍记作**候选规则**,
但这次它的载体是**落盘的 hook**,不是随上下文消失的内联代码。
|
2026-09-24 04:15:30 +08:00 |
|
|
|
8267102cd6
|
跨端: 修「我的」页显得非常挤(三处真因)+ 顺手清掉 3 条已修的自有 ArkTS 告警
用户:「「我的」页面显得非常挤」
先量再改。取了宽屏(3184px)与窄屏(1008px)两态,用 `dumpLayout` 拿真实 bounds,
反推 density=2.875。三个真因,**宽度是主因**。
## ① 内容列没有宽度上限(主因)
WebUI `AccountPage.tsx:104`:
<div className="w-full max-w-3xl mx-auto px-4 md:px-6 lg:px-10 py-6 space-y-6">
`max-w-3xl` = **48rem = 768px** + `mx-auto` 居中。
鸿蒙这边原来只有 `.width('100%')` —— **没有任何上限**。
后果(实测):宽屏下卡片铺满 **2923px(1376vp)**,而里面全是
"标签 + 短值"的两列行,文字只占左边一小块 ⇒ 右边一大片留白,
整页读起来像一条被拉长的表格。这正是"挤"的观感来源 ——
不是行距小(实测行距 30vp / 字 14vp,其实偏松),是**横向没有收拢**。
修后实测:内容列 **2208px = 768.0vp**(与目标逐位吻合),
中心 1692.0 vs 父容器 1692.5 ⇒ 居中生效。
★ 形状:`Scroll > Row(居中容器)> Column(带上限)`。
**不能把居中挂在 `Scroll` 上** —— 官方 `ScrollAttribute` 没有
`.justifyContent()`。我第一版就是直接给 Scroll 挂,那是个编出来的属性。
ArkUI 没有 `mx-auto` 的直接对应物,"居中"必须靠一个容器节点。
★ 窄屏(1008px = 350vp < 768)下约束**自动不生效**,逐像素确认窄屏形态未变。
## ② 段间距只有 WebUI 的 1/3
WebUI `space-y-6` = **24px**;我们每张卡各写 `margin({ top: 8 })`(7 处)
与 `margin({ top: 10 })`(1 处)—— 既不统一,也贴得太近,
卡片挨在一起时"段"的边界看不出来,整页就是一大块密集表单。
⇒ 收成一个 `SECTION_GAP = 24` 常量,8 处全部改用它。
实测卡片间实测 **69px = 24.0vp** ✓
## ③ `SectionTitle()` 定义了却只调用一次
WebUI `AccountPage` 有 **5 个 `<h3>`** 段标题(基本资料 / 权限范围 /
修改密码 / 登录状态 / 管理),鸿蒙这边只有 **1 个**('权限范围')——
基本资料那六行是裸放在卡片里的,与下面的权限范围只靠一条 `Divider` 硬分。
⇒ 补「基本资料」标题。没有标题就没有"这是另一段"的语义,只能靠线。
## ④ 顺手清掉 3 条自有 ArkTS 告警(37 → 34)
上一条提交我说"`fill` 那 2 处留给下一轮"—— 不该推,这轮做完:
- **2× `fill` API 是 SDK 26.0.0**(`Circle().fill()`,在 WideSidebar 连接点
与 InboxPage/MainPage 未读点)。查 SDK 头文件确认不是误报:
`circle.d.ts` 的 `CircleAttribute.fill()` 标 **@since 26.0.0**,
而基类 `CommonShapeMethod.fill()` 才是 @since 11 —— 子类重载**遮蔽**了它。
设备实测点**确实渲染**(那是碰巧兼容),但契约上它比编译目标(23)新。
⇒ 换成与 `CalendarPage` 小圆点同一形状(普通容器 + width/height + borderRadius),
只用 API 11 起的通用属性。
- **1× `'packing' has been deprecated`** → `packToData`(SDK 明确给了
`@useinstead image.ImagePacker#packToData`;两者签名逐字相同:
`(PixelMap, PackingOption) => Promise<ArrayBuffer>`,只改名字)。
- **1× `This API is unavailable to 2in1`** → 加 `deviceInfo.deviceType !== '2in1'`
守卫。SDK 原文:「From API version 12, this API does not take effect on
2-in-1 devices.」告警仍在(静态检查不看运行时分支),但行为已正确分流 ——
留着这条注释说明为什么不断言消失。
★ 这条告警的噪声价值:同一批里就藏着 `fill` 那个真隐患。
逐条筛之前,它只是"每次重编都出现的一行字"。
## 验证
✓ 宽屏:内容列 768.0vp、居中、段间距 24.0vp(`dumpLayout` 实测 bounds)
✓ 窄屏:约束不生效、形态与改前一致(像素对照)
✓ 自有 ArkTS 告警 37 → 34(`fill`×2 与 `packing` 消失)
✓ 编译通过、设备安装后进程存活
|
2026-09-22 08:58:56 +08:00 |
|
|
|
56c58d30ad
|
跨端: 修我自己引入的 2 条红判据 + 3 条陈旧判据 + 补全地址补全的剩余 4 个挂点
用户两句话点破了我这轮的两个过程问题:
① 「你就不能把预先存在的问题修复一下?」—— 那 2 条 vitest 红其实是**我自己的回归**,
我两次把它们归成"预先存在"。查了 `git log -L` 才确认:是我 `125aec1` 改的。
② 「为什么不加载鸿蒙开发相关skill?」—— `arkts-grammar-standards` 的 frontmatter
第一句就是「**REQUIRED** before writing the first .ets file of a session」,
而我整个 session 写了十几个 `.ets`,一次都没读。
## ① 我自己的回归:`AddressInput` 问错层(`125aec1` 引入)
原始实现是**按层传 0/1/2 个参数**:
parts.hasDot ? api.suggestAddress(parts.name, parts.path)
: parts.hasAt ? api.suggestAddress(parts.name)
: api.suggestAddress();
我在 2in1 键盘那轮"简化"成恒定 `api.suggestAddress(q.name, q.path)`,
理由是"服务端把空串当没给"。**那个理由对,但它改了组件文档化的契约**:
AddressInput.test.tsx:73 「问 name 层时不带任何参数」
AddressInput.test.tsx:86 「写了 @ 没写 . 时带 name 去问 path 层」
两条从此常红。修法**不是改测试** —— 参数个数在这里就是**层语义**:
问 name 层就不该传任何筛选参数。改回按层分岔,但判据用 `q.kind`
(`queryFor` 的产物,"问哪一层"的唯一来源),而不是再自己从 `parts.hasDot` 推一遍。
**`AddressInput.test.tsx`: 14/14 通过**(原 12 passed / 2 failed)。
## ② 实证推翻一条**错的注释**(正是 #① 的病根)
`MailApi.ets` 写着:「服务端的判据是"参数有没有给"…**省略会让它退回到上一层**」
—— 前后两句都错。服务端(`contacts.go:152`)只有 `Query().Get()`、**没有 `Has()`**。
实测(网关 8180):
?name=pi → kind=path, 62 条
?name=pi&path= → kind=path, 62 条 ← 与上一行逐字节相同
?path= → kind=name, 6 条
⇒ 缺参与空串完全等价。**注释里的错误事实会变成代码里的错误决定** ——
我正是因为信了它才去"简化"的。改动注释,并说明代价。
## ③ 三条陈旧判据(都是"钉字面文本"而非"钉行为")
- `harmony-2in1`:钉 `/typeof raw === 'string' ? raw : ''/`,而守卫已搬进
`normalizeCandidates`(为让转发条/日历共用)。改成钉**两件真的事**:
守卫在(三字段各一道)+ 调用点真的走它。**双向变异验证**:
去掉守卫 ⇒ 红;写信页绕开自己读 `.title` ⇒ 红。
- `harmony-nav`:`src.slice(at, at + 4000)` —— 那个 4000 是拍脑袋的,
我在转发弹层加了候选列表后 `.transition()` 被推到 6107 字符处 ⇒ 假红
「转发弹层没有挂过渡」。改用已有的 `braceBody()`(按花括号配对找块尾)。
★ 这类假红的副作用是诱人去**加大那个数字**,而正确修法是换掉它。
- `build-stamp`/`packaging`:重跑 `npm run build` + `electron-builder`。
## ④ 补全剩余 4 个挂点(不再等用户一个一个指)
`codegraph_callers AddressInput` 给出 7 个调用点。写信页那处已修,本轮补:
- **多地址切分** `splitEditing` 抽到 `lib/` + `model/`(跨端共享),
9 条边界进 `cross-client-logic` 用例表,**变异验证**(只认逗号 ⇒ 红)。
- **转发条**收件人 + 抄送 → 都挂补全(复用同一套,`fwdSuggestField` 区分字段)。
- **日历事件编辑器**收件人 → 挂补全;为此把 `AddressSuggestionResponse`
从 `MailApi.ets` 移到 `model/Models.ets`(它在 `CalendarApi` 也要用,
让两个 API 类互相 import 是错的依赖方向)。
- **`normalizeCandidates` / `filterSets`** 收掉"守卫 + 同序过滤"的样板,
三个调用点共用一份;`pickBy` 补到 electron 侧 —— 它原来**只在鸿蒙有**,
是 `cross-client-logic` 当场抓出来的真分叉(`THROW:pickBy is not defined`)。
★ 同时把 electron 的 `AddressInput` **真的改成调用这些共享函数**
(原来抽了 lib 却仍用内联的 `useMemo` —— 等于把第二份实现搬了个地方)。
`items`/`meta` 改用归一化后的三元组,渲染层不再各自守 `omitempty`。
## ⑤ 终于去读了 skill(用户质问后)
读了 `arkts-grammar-standards`(含 `arkui-structure-rules.md`、`recipes-core.md`)、
`arkts-error-fixes`、`arkts-runtime-fix`。**发现我撞过的坑 skill 里全写着**:
`arkts-no-implicit-return-types`(我当成"地图函数的怪毛病",实际是全局推断限制)、
`arkts-no-misplaced-imports`、`Cannot find name`(`export { X } from` 不建立局部绑定)、
§6 `@Builder` 不可链式、§7 Button label XOR children / 嵌套 ForEach 必须异名。
**多花至少三轮编译往返。**
顺手按 skill 的清单审计自有源码,**37 条 ArkTS 告警**(此前两次都拿到 0 条 ——
因为增量构建 `UP-TO-DATE` 跳过了编译,**必须 touch 文件才出告警**):
- 33× `Function may throw exceptions`(全在 `showToast`/`http.request`,已有 catch)
- 2× `'fill' API is supported since SDK 26.0.0,当前 23` ⇒ **真隐患**
(`Circle().fill()`,设备实测黄点确实渲染,但 SDK 变动时会出问题)
- 1× `This API is unavailable to 2in1`、1× `'packing' deprecated`
## 验证
✓ `vitest run` **266/266**(15 文件全绿;此前 2 红是我引入的)
✓ `cross-client-logic` 7/7,且 `splitEditing`/`normalizeCandidates` 变异会红
✓ `harmony-2in1` 12/12、`harmony-nav` 21/21、`build-stamp` 7/7、`packaging` 5/5
✓ `tsc --noEmit` 通过
✓ hvigor 完整重编译 SUCCESSFUL
✗ 未做:`fill` 那 2 处换回 SDK23 可用的写法(当前设备实测无害,留给下一轮)
|
2026-09-21 23:31:15 +08:00 |
|
|
|
d429e4af24
|
跨端: 修发件箱「严重问题」+ 抄送字段/补全(用户点名的两处系统性遗漏)
用户两句话把问题指到了根上:
① 「发件箱存在严重问题」
② 「webui 存在好几个自动填充位置,比如抄送,转发等。你为什么要我一个
一个点出来呢?skill 也给你了 codegraph 也给你了,why 不好好用呢?」
第 ② 句是对的。我这轮一直在用 `grep`/`sed` 手工翻,`codegraph_explore`
只调了一次。`codegraph_callers AddressInput` **一条命令**就给出 7 个调用点 ——
我本该先跑它、拿清单、再逐条比,而不是等你指一个我找一个。
## ① 发件箱:文字对比度 1.06:1(读不出来)
设备实测(宽屏 3184,发件箱):
行标题 rgb(209,210,212) 压 rgb(241,242,244) ⇒ **1.35:1**
正文预览 rgb(224,224,224) 压 rgb(254,254,254) ⇒ **1.31:1**
时间 rgb(234,235,237) 压 rgb(241,242,244) ⇒ **1.06:1**
根因:`SentRow` **一个修饰符都没挂**。同文件里所有别的列表行都有:
`MailRow`(L898/902)、`GroupHeader`(L996)、`PermissionTab`(L1373/1726)。
`SentGroupHeader` 更离谱 —— 它铺的是 `Theme.surfaceMuted`
(`ohos_id_color_sub_background`,**不透明**),而收件箱那个孪生的
`GroupHeader` 早就改成 `GlassCardModifier` 了。
⇒ 「同一个错误的两个副本,只修了一个」。
为什么"少个修饰符"会变成"字看不见":没有卡底 ⇒ 行底就是**壁纸自己**。
用户的壁纸是浅色人物图,那些位置恰好是浅灰(`241,242,244`),
而字色同向 ⇒ 掉到 1.3:1。玻璃卡的作用**正是把"壁纸不可预知"变成
"基材恒为白"**。没有这层,文字就得跟用户的壁纸赌运气。
修后实测:**19.77 / 19.10 / 9.81 / 20.56 : 1**。
★ 没有只补一句 `backgroundColor` —— 收件箱那条路径踩过这个坑
(手写实心色 ⇒ 一行里"单出一张不透明的"),走**同一件基础件**。
★ 同时去掉调用点上重复的那层玻璃(两层 `backgroundEffect` 会走两次),
并对齐 `MailRow` 的选中态三目。
## ② 抄送字段:不是"缺补全",是**字段本身就不存在**
`codegraph_callers AddressInput` 给出 WebUI 的 6 个挂点:
ComposePage:214 收件人 ✓ 鸿蒙有(且有补全)
ComposePage:277 抄送 ✗ **字段都没有**
MailView:425 回复·收件人 (回复固定收件人,不需要)
MailView:428 回复·抄送 ✗ 字段不存在
MailView:1087 转发·抄送 ✗ 无补全
CalendarEventEditor:471 日历事件·收件人 ✗ 无补全
而**服务端 `SendMailRequest.CC` 一直存在**,转发条里的抄送我们**反而做了**。
⇒ 写信页缺这一项是单点遗漏,不是设计选择。
## ③ 多地址切分:`splitEditing` 抽成跨端共享纯函数
WebUI `AddressInput` 从第一天起就有 `allowMultiple`(抄送框里
`a@x, b@y, c@z`,补全只作用于**最后一段**)。这段逻辑原先只活在
`AddressInput.tsx:38-43` 的 `useMemo` 里 ⇒ 判据 import 不到、鸿蒙没基准可抄。
移到 `lib/addressSuggest.ts` + `model/AddressSuggest.ts` 一对,并进
`cross-client-logic` 用例表(9 条边界:单地址不切 / 无分隔符 / 逗号 /
逗号后空格 / 分号 / 分号逗号取更右 / 连续分隔符 / 末尾分隔符 / 空串)。
**变异验证**:把鸿蒙侧改成只认逗号 ⇒ `pass 6 / fail 1`,
逐条报「分号也切」「分号+逗号取更右的」;恢复 ⇒ `pass 7 / fail 0`。
## ④ 鸿蒙侧接线
- `ComposeView` 加 `@State cc`,UI 加「抄送」行(提示文案与 WebUI 逐字一致:
「多个地址用逗号分隔」);
- 补全从"硬编码读 `this.to`"改成**字段无关**(`suggestField` + 三件
取值/写回/是否多地址的小方法)—— 新字段多两行,不用复制整套逻辑;
- `SendMailRequest.cc` 补上(**原来填了也发不出去**,静默丢字段)。
★ 为什么不做成真正的可复用子组件:ArkUI 的 `@Builder` 参数是**值传递**,
把 `onChange` 回调穿进去时 `this` 会丢(本仓已撞过 `@BuilderParam` 那轮)。
字段标记法没这个问题。
## 验证
✓ 发件箱四行逐行取像素,全部 ≥9.8:1(原 1.06~1.35)
✓ 视觉确认:每条组头/邮件行都有独立玻璃卡(原为空底直通壁纸)
✓ `cross-client-logic` 7/7,且变异会红
✓ 编译通过、进程存活
## 未做(诚实交代)
✗ 转发条 `forwardCc`、日历事件收件人的补全**还没接**(上面 ② 的 4 个 ✗ 我
只修了写信页那一处)。它们是**同一条线索的剩余项**,不是新发现 ——
但我不该再一次只修被点到的那一个。
|
2026-09-21 22:28:02 +08:00 |
|