diff --git a/docs/DEBTS.json b/docs/DEBTS.json index 713ea70..092160b 100644 --- a/docs/DEBTS.json +++ b/docs/DEBTS.json @@ -350,7 +350,7 @@ "kind": "**不可复现的引证**:历史被 filter-repo 重写过、没有任何 tracked 记录,旧 sha 已永久不可解释;且**没有一条判据会发现这件事**", "due": "下一次有人要**依据文档里的 sha** 下判断或复核时(即下一次「拿引证当证据」时)。★ 到期动作**不是**去补写那些旧 sha(旧对象已 gc,补不出来),而是二选一:① 把 old→new 映射表**纳入版本控制**(现在它只在 `.git/filter-repo/` 里、永不随 clone 传递);② 承认引证不可复核,改成本仓已有的那条口径(`docs/reviews/final-check-2026-09-28.md:181`:比对 `git rev-parse HEAD`,不要把 SHA 钉进判据文件)。", "where": "**没有判据**。实测 `client/electron/test/`+`deploy/` 里没有任何一条检查「被引用的 sha 可解析」(`grep -rl 'rev-parse --verify' client/electron/test/ deploy/` ⇒ 0 命中)。唯一相关的那条(`criteria-hygiene.test.mjs:551` 附近的 `git cat-file -e`)查的是**远端 ref 可达的 blob**(防泄露),不是「文档引用的 commit 是否存在」。", - "note": "★★★ 2026-09-30 登记。**发现路径本身就是这条债要防的东西**:我当时在核「我是不是编造过 hash」,而**没有任何判据能回答那个问题** —— 只能手搓一遍扫描。\n\n## 形状\n\n`git filter-repo` 在 **2026-09-26 09:26:41** 跑过(依据 `.git/filter-repo/commit-map` 的 mtime)。它重写了 `refs/heads/main` 的历史:\n\n· `commit-map` **817 行**:`320` 行 old==new(未动)、**`497` 行 old≠new(改了)**、**`0` 行被删**;\n· `first-changed-commits` = `7647c24abcadfb74dd172bb277747ae36a53643c` → `b806a05bfaa1430645d54ff83185c645a87a6cc1`;\n· `ref-map`:`refs/heads/main` 从 `ed4294b8597ac01dc1eb7691a72633ae656a00b6` 换成 `7c9d1cedc9cb8b095d6705f45c7aa184937c96d5`;\n· 新历史**已经推到远端**:`7c9d1ce` 是 `origin/main`(`c2796eaa52d2…`)的祖先 ⇒ 不是一次本地实验;\n· 旧对象**已经 gc 掉**,不是「只是没 ref 指」:`ed4294b` / `16a77d5` / `c155560` 全部 `fatal: Not a valid object name`。\n\n## ★ 我不知道它**消除了什么** —— 这一点必须写在最前面\n\n我第一版在这里写了「改写的目确实达成了:`dep_parser.onnx`(48.6MB)现在全历史 `0` 命中」。**那是空转读数**,我撤掉:\n\n· `git rev-list --objects --all | grep -c dep_parser` = `0` —— 但本仓**从来没有过**这个文件:`internal/nlp` 与 `server/internal/nlp` 都不存在,`internal/nlp/*` 的路径历史 **0 提交**。**0 命中区分不了「已清除」与「从未存在」**(这正是本仓反复记的「读数器没先被证明是好的」)。\n· 那个 48.6MB 是 **pi 在 2026-09-14 07:17:51 的信里提到的另一个仓/另一个议题**(它自己 09-13 的测算写的是 **46.34 MB**,两处数字也不一致)。我把它搬来当本仓的证据,是**跨域引证**。\n· 我也没有别的手段能测「消除了什么」:`.git/filter-repo/` 只有 6 个文件(`already_ran`/`changed-refs`/`commit-map`/`first-changed-commits`/`ref-map`/`suboptimal-issues`),**没有**记录被过滤的路径或表达式;改写前的 tip `ed4294b` 已 gc ⇒ **「改写减掉了多少」在本仓不可复算**。\n\n⇒ 所以本条只说**能测的那部分**:历史被换过、映射表不在版本控制里、引证因此不可复核。**改写动机与收益我一概不知道**,不替它记功。\n(可测的旁证只有一条,且**否定**「清历史是为了清大文件」这个动机:**一个 22.9MB 的 ELF(`server/server`,c401eb2 之前的误提交)至今仍可从 HEAD 到达** —— `git rev-list --objects HEAD` 里就有它,而它同时被 `.gitignore:91` 挡着、工作树里也躺着同一个 24060781 字节的文件。若改写目的在清大对象,这个漏了。)\n\n## 后果(这才是要紧的)\n\n**① 文档里所有 09-26 09:26 之前的 sha 引证,对新克隆者永久不可解释。**\n`commit-map` 在 `.git/` 内、**不被 git 跟踪**(`git ls-files` 查不到它)⇒ 它**永远不会**随 clone/clone --mirror 传递。新克隆拿到的是改写后的历史,且拿不到映射表。\n\n实测扫了全部 tracked 文件(`.md/.json/.mjs/.go/.sh/.ts`,正则 `` `[0-9a-f]{7,12}` ``,**并先扣掉邮件域**:`mails.mail_id` / `session_id` / `parent_mail_id` / `relayed_mails.relay_key` 的前 8 位):\n\n· commit 式引用(去重 file×sha)= **143**;\n· 其中**不可解析** = **98**,涉及 **89** 个不同 hash;\n· 这 89 个里,**81 个**能被 `commit-map` 的 old 列解释 ⇒ 它们是「改写前的正确 hash」;\n· **★ 8 个解释不了**:`1ca3aa8`、`3b46126`、`6d8928b`、`748a29d`、`77c15e2`、`c0852b5`、`f31bc02`、`f51c9c8`。\n 逐个查过:**8/8 都不是任何对象**(`git cat-file -t` 全空)、不在 `opencode.db`、不在任何本地克隆(扫了 82 个 `.git`,0 命中)⇒ 它们确实是「曾经是 commit、现在谁都不认识」。\n\n**② 这件事没有任何 tracked 文档记录,而文档还写着相反的话。**\n`grep -rln 'filter-repo' docs/` 在**我写这条之前**(`6010fcb`)⇒ **0 命中**。同时 `docs/API.md:8603` 写着:\n (★ 本条自己就是那个断言的第一个反例:`docs/DEBTS.json` 现在命中 1 —— 见下面 ⑲ 那段。)\n\n> **改写的代价比收益大** ⇒ 我**不改写历史**,改为在此显式标注该提交的那句作废。\n\n**与既成事实相反**。★ 更讽刺的是**这段话本身也在被重写的那段历史里** —— 它当年为「不移动后代 SHA」而拒绝改写,理由里点名的 `5c5e12b`/`79ef8c1`/`3b677ca` 现在全成了不可解析引证(这 3 个在扫描里属「filter-repo 已解释」)。\n\n**③ 至少还有一次未记录的、局部改 hash 的事件(这是假设,不是结论)。**\n`docs/API.md:11776/11840/11969` 的三行「采样: HEAD ``」记的是 **09-26 14:59:54 / 15:02 / 15:06:13**,即改写**之后 5.5 小时**,可那三个 hash 仍不可解析 ⇒ filter-repo 解释不了它们。\n另有 fingerprint 指向第二次:`09-28 08:46:01/02` **六个连续提交**(`18b148e`→`21332de`→`4556886`→`2530229`→`d4e13ea`→`1639382`,父为 `044a664`,其父 A=C=08:26:41 正常)共用**同一个 committer 时刻** = rebase 指纹;而 reflog 起点是 `09-28 09:26:15`,**晚于** 08:46 ⇒ 那一段没有任何本地记录,我拿不到那次的映射表。\n⇒ 所以我只能说「至少还有一次」,**不能说清是哪次、改了什么**。这一步刻意留成假设。\n\n## 为什么这条是债,而不是「历史事实」\n\n单看每一条,都能各自解释掉(「重写是为了减重」「旧 sha 本来就该过期」)。合成一条才看得见性质:\n\n**本仓的核心工作方法是「用引证互相复核」**(`docs/API.md` 12049 行里的 sha 就是复核的锚点),而**这个方法的前提——引证可被第三方独立复算——已经在 09-26 被单方面取消了,且取消这件事本身没有被记录**。\n仓库已有的那条警告(`final-check-2026-09-28.md:181`「不要把 SHA 钉进判据文件」)**只覆盖「SHA 会随新提交而过期」**——那种情况对一下 `git rev-parse HEAD` 就能发现、能修;**它不覆盖「整段历史会消失」**——后者**任何**事后核对都不可能,因为对象和映射表一起没了。\n\n★ 我自己的实例(这也是我这次去查的起因):我 2026-09-25 07:47:36 发 pi 的信(`13119910`)引了 `16a77d5` 与 `c155560`。现在两个都查不到。我一度怀疑**自己编造了 hash**。查 `commit-map` 才知道:`16a77d5 → fc54a816e81e`、`c155560 → b16c38ad8324`,改写发生在**发信之后 25.7 小时** ⇒ **发信时它们是对的,不是编造**。\n⇒ 若没有那张映射表,这个结论**无法得出**;而那张表不在版本控制里。\n\n## ★ 本条自己也踩了 ⑲(判据的扫描域必须排掉判据自己产出的文本)\n\n上面的 143/98/89/81/8 这组数字,**第一版算出来是 164/112/90/82/8** —— 因为我把这一段**写进 `docs/DEBTS.json` 之后又扫了一遍全仓**,于是**本条自己正文里列举的那 8 个 hash 被当成「仓库里的引证」数了进去**。\n\n修法:改成扫 **HEAD 版本**(`git show HEAD:`),而不是扫工作树 —— 即**量登记之前的状态**。\n★ 这与本仓 `criteria-hygiene.test.mjs` 里那两处排除(`:193` 排掉观察者 `read.mjs`、`:919` 按**身份**排掉被测工具自己的文件)是同一条规矩,只是换了个落点:**扫描型判据必须先划掉自己产出的文本**,否则「发现数」会随「你怎么写这条结论」而变 —— 我这次就撞上了。\n\n**量化**(实测分解,不是估):\n`docs/DEBTS.json` 里认作 ref 的**不同 hash**:HEAD 版 **11** 个 → 当前版 **32** 个,**差 = 21**,与全局那个 `164 − 143 = 21` **逐一对上**。\n这 **21 个全部来自我新写的两条**(`16a77d5`/`18b148e`/`1ca3aa8`/`21332de`/`239ff37`/`2a9be0e`/`3b46126`/`3b677ca`/`5c5e12b`/`65aacb6`/`6d8928b`/`748a29d`/`77c15e2`/`79ef8c1`/`7c9d1ce`/`c0852b5`/`c155560`/`d4e13ea`/`ed4294b`/`f31bc02`/`f51c9c8`)。\n(我那两条 note 里出现的**不同** hash 共 22 个,其中 `044a664` 是**原先就在**该文件里的 ⇒ 22 − 1 = 21,与上面的 21 逐一对上。)\n所以准确的说法是「本条使这份文件的引证数从 **11** 涨到 **32**」,而不是「本条有 19 个 hash」—— 11→32 是**文件**的增量,22 是**我两条 note** 的不同 hash 数,21 是**新增**的不同 hash 数,三个数各有各的域。\n⇒ 修法写成「扫 **HEAD** 版本」,而不是「扫工作树后再扣掉自己」:后者要维护一张「哪些是我自己写的」名单,而那张名单**又会**随我改这条正文而变。\n## 未做\n\n没有补写任何旧 sha、没有改 `API.md`(它是多会话共享的 append-only 日志)、没有动 `.git/filter-repo/`、没有把映射表复制进仓库(那是「决定要留下它」的动作,属人决定,见 `due`)。\n也没有把这条写成对 pi 的指控:pi 只是**提出过**(b)这条路,改写是谁执行的、有没有记录,我查不到。\n" + "note": "★★★ 2026-09-30 登记。**发现路径本身就是这条债要防的东西**:我当时在核「我是不是编造过 hash」,而**没有任何判据能回答那个问题** —— 只能手搓一遍扫描。\n\n## 形状\n\n`git filter-repo` 在 **2026-09-26 09:26:41** 跑过(依据 `.git/filter-repo/commit-map` 的 mtime)。它重写了 `refs/heads/main` 的历史:\n\n· `commit-map` **817 行**:`320` 行 old==new(未动)、**`497` 行 old≠new(改了)**、**`0` 行被删**;\n· `first-changed-commits` = `7647c24abcadfb74dd172bb277747ae36a53643c` → `b806a05bfaa1430645d54ff83185c645a87a6cc1`;\n· `ref-map`:`refs/heads/main` 从 `ed4294b8597ac01dc1eb7691a72633ae656a00b6` 换成 `7c9d1cedc9cb8b095d6705f45c7aa184937c96d5`;\n· 新历史**已经推到远端**:`7c9d1ce` 是 `origin/main`(`c2796eaa52d2…`)的祖先 ⇒ 不是一次本地实验;\n· 旧对象**已经 gc 掉**,不是「只是没 ref 指」:`ed4294b` / `16a77d5` / `c155560` 全部 `fatal: Not a valid object name`。\n\n## ★ 我不知道它**消除了什么** —— 这一点必须写在最前面\n\n我第一版在这里写了「改写的目确实达成了:`dep_parser.onnx`(48.6MB)现在全历史 `0` 命中」。**那是空转读数**,我撤掉:\n\n· `git rev-list --objects --all | grep -c dep_parser` = `0` —— 但本仓**从来没有过**这个文件:`internal/nlp` 与 `server/internal/nlp` 都不存在,`internal/nlp/*` 的路径历史 **0 提交**。**0 命中区分不了「已清除」与「从未存在」**(这正是本仓反复记的「读数器没先被证明是好的」)。\n· 那个 48.6MB 是 **pi 在 2026-09-14 07:17:51 的信里提到的另一个仓/另一个议题**(它自己 09-13 的测算写的是 **46.34 MB**,两处数字也不一致)。我把它搬来当本仓的证据,是**跨域引证**。\n· 我也没有别的手段能测「消除了什么」:`.git/filter-repo/` 只有 6 个文件(`already_ran`/`changed-refs`/`commit-map`/`first-changed-commits`/`ref-map`/`suboptimal-issues`),**没有**记录被过滤的路径或表达式;改写前的 tip `ed4294b` 已 gc ⇒ **「改写减掉了多少」在本仓不可复算**。\n\n⇒ 所以本条只说**能测的那部分**:历史被换过、映射表不在版本控制里、引证因此不可复核。**改写动机与收益我一概不知道**,不替它记功。\n(可测的旁证只有一条,且**否定**「清历史是为了清大文件」这个动机:**一个 22.9MB 的 ELF(`server/server`,c401eb2 之前的误提交)至今仍可从 HEAD 到达** —— `git rev-list --objects HEAD` 里就有它,而它同时被 `.gitignore:91` 挡着、工作树里也躺着同一个 24060781 字节的文件。若改写目的在清大对象,这个漏了。)\n\n## 后果(这才是要紧的)\n\n**① 文档里所有 09-26 09:26 之前的 sha 引证,对新克隆者永久不可解释。**\n`commit-map` 在 `.git/` 内、**不被 git 跟踪**(`git ls-files` 查不到它)⇒ 它**永远不会**随 clone/clone --mirror 传递。新克隆拿到的是改写后的历史,且拿不到映射表。\n\n实测扫了全部 tracked 文件(`.md/.json/.mjs/.go/.sh/.ts`,正则 `` `[0-9a-f]{7,12}` ``,**并先扣掉邮件域**:`mails.mail_id` / `session_id` / `parent_mail_id` / `relayed_mails.relay_key` 的前 8 位):\n\n· commit 式引用(去重 file×sha)= **143**;\n· 其中**不可解析** = **98**,涉及 **89** 个不同 hash;\n· 这 89 个里,**81 个**能被 `commit-map` 的 old 列解释 ⇒ 它们是「改写前的正确 hash」;\n· **★ 8 个解释不了**:`1ca3aa8`、`3b46126`、`6d8928b`、`748a29d`、`77c15e2`、`c0852b5`、`f31bc02`、`f51c9c8`。\n 逐个查过:**8/8 都不是任何对象**(`git cat-file -t` 全空)、不在 `opencode.db`、不在任何本地克隆(扫了 82 个 `.git`,0 命中)⇒ 它们确实是「曾经是 commit、现在谁都不认识」。\n\n**② 这件事没有任何 tracked 文档记录,而文档还写着相反的话。**\n`grep -rln 'filter-repo' docs/` 在**我写这条之前**(`6010fcb`)⇒ **0 命中**。同时 `docs/API.md:8603` 写着:\n (★ 本条自己就是那个断言的第一个反例:`docs/DEBTS.json` 现在命中 1 —— 见下面 ⑲ 那段。)\n\n> **改写的代价比收益大** ⇒ 我**不改写历史**,改为在此显式标注该提交的那句作废。\n\n**与既成事实相反**。★ 更讽刺的是**这段话本身也在被重写的那段历史里** —— 它当年为「不移动后代 SHA」而拒绝改写,理由里点名的 `5c5e12b`/`79ef8c1`/`3b677ca` 现在全成了不可解析引证(这 3 个在扫描里属「filter-repo 已解释」)。\n\n**③ 至少还有一次未记录的、局部改 hash 的事件(这是假设,不是结论)。**\n`docs/API.md:11776/11840/11969` 的三行「采样: HEAD ``」记的是 **09-26 14:59:54 / 15:02 / 15:06:13**,即改写**之后 5.5 小时**,可那三个 hash 仍不可解析 ⇒ filter-repo 解释不了它们。\n另有 fingerprint 指向第二次:`09-28 08:46:01/02` **六个连续提交**(`18b148e`→`21332de`→`4556886`→`2530229`→`d4e13ea`→`1639382`,父为 `044a664`,其父 A=C=08:26:41 正常)共用**同一个 committer 时刻** = rebase 指纹;而 reflog 起点是 `09-28 09:26:15`,**晚于** 08:46 ⇒ 那一段没有任何本地记录,我拿不到那次的映射表。\n⇒ 所以我只能说「至少还有一次」,**不能说清是哪次、改了什么**。这一步刻意留成假设。\n\n## 为什么这条是债,而不是「历史事实」\n\n单看每一条,都能各自解释掉(「重写是为了减重」「旧 sha 本来就该过期」)。合成一条才看得见性质:\n\n**本仓的核心工作方法是「用引证互相复核」**(`docs/API.md` 12049 行里的 sha 就是复核的锚点),而**这个方法的前提——引证可被第三方独立复算——已经在 09-26 被单方面取消了,且取消这件事本身没有被记录**。\n仓库已有的那条警告(`final-check-2026-09-28.md:181`「不要把 SHA 钉进判据文件」)**只覆盖「SHA 会随新提交而过期」**——那种情况对一下 `git rev-parse HEAD` 就能发现、能修;**它不覆盖「整段历史会消失」**——后者**任何**事后核对都不可能,因为对象和映射表一起没了。\n\n★ 我自己的实例(这也是我这次去查的起因):我 2026-09-25 07:47:36 发 pi 的信(`13119910`)引了 `16a77d5` 与 `c155560`。现在两个都查不到。我一度怀疑**自己编造了 hash**。查 `commit-map` 才知道:`16a77d5 → fc54a816e81e`、`c155560 → b16c38ad8324`,改写发生在**发信之后 25.7 小时** ⇒ **发信时它们是对的,不是编造**。\n⇒ 若没有那张映射表,这个结论**无法得出**;而那张表不在版本控制里。\n\n## ★ 本条自己也踩了 ⑲(判据的扫描域必须排掉判据自己产出的文本)\n\n上面的 143/98/89/81/8 这组数字,**第一版算出来是 164/112/90/82/8** —— 因为我把这一段**写进 `docs/DEBTS.json` 之后又扫了一遍全仓**,于是**本条自己正文里列举的那 8 个 hash 被当成「仓库里的引证」数了进去**。\n\n修法:改成扫 **HEAD 版本**(`git show HEAD:`),而不是扫工作树 —— 即**量登记之前的状态**。\n★ 这与本仓 `criteria-hygiene.test.mjs` 里那两处排除(`:193` 排掉观察者 `read.mjs`、`:919` 按**身份**排掉被测工具自己的文件)是同一条规矩,只是换了个落点:**扫描型判据必须先划掉自己产出的文本**,否则「发现数」会随「你怎么写这条结论」而变 —— 我这次就撞上了。\n\n**量化**(实测分解,不是估):\n`docs/DEBTS.json` 里认作 ref 的**不同 hash**:HEAD 版 **11** 个 → 当前版 **32** 个,**差 = 21**,与全局那个 `164 − 143 = 21` **逐一对上**。\n这 **21 个全部来自我新写的两条**(`16a77d5`/`18b148e`/`1ca3aa8`/`21332de`/`239ff37`/`2a9be0e`/`3b46126`/`3b677ca`/`5c5e12b`/`65aacb6`/`6d8928b`/`748a29d`/`77c15e2`/`79ef8c1`/`7c9d1ce`/`c0852b5`/`c155560`/`d4e13ea`/`ed4294b`/`f31bc02`/`f51c9c8`)。\n(我那两条 note 里出现的**不同** hash 共 22 个,其中 `044a664` 是**原先就在**该文件里的 ⇒ 22 − 1 = 21,与上面的 21 逐一对上。)\n所以准确的说法是「本条使这份文件的引证数从 **11** 涨到 **32**」,而不是「本条有 19 个 hash」—— 11→32 是**文件**的增量,22 是**我两条 note** 的不同 hash 数,21 是**新增**的不同 hash 数,三个数各有各的域。\n⇒ 修法写成「扫 **HEAD** 版本」,而不是「扫工作树后再扣掉自己」:后者要维护一张「哪些是我自己写的」名单,而那张名单**又会**随我改这条正文而变。\n## 未做\n\n没有补写任何旧 sha、没有改 `API.md`(它是多会话共享的 append-only 日志)、没有动 `.git/filter-repo/`、没有把映射表复制进仓库(那是「决定要留下它」的动作,属人决定,见 `due`)。\n也没有把这条写成对 pi 的指控:pi 只是**提出过**(b)这条路,改写是谁执行的、有没有记录,我查不到。\n\n\n## ★ 全量失效率(2026-09-30 补测,替代上面\"8 个样本\"的抽查)\n\n扫描**本会话 21c398ee 里全部 dsh 发出的信**,凡上下文标注为 git 对象(commit/提交/HEAD/hash/反引号包裹)的 7+hex 串,逐串 `git cat-file -t` 验证:\n\n```\n引用形状串总数 = 101\n现在可解析 = 1 (重写后仍有效的)\n在 commit-map 里 = 85 (filter-repo 重写过 ⇒ 旧 sha 永久失效)\n完全不在 map 里 = 15 (amend 前悬空提交已被 gc / 或错拼)\n⇒ 失效总数 = 100/101 = **99%**;其中 84%(85/101)可归因于 filter-repo 重写,\n 另 15%(15/101)连 commit-map 都不收录 —— 后者是\"amend 链 + gc\"或\"记忆错拼\"的混合,不可再分。\n```\n\n★ 意义:**\"旧 sha 引证\"在本会话不是偶发,是常态**(99%)。且两个来源**机制不同**——\nfilter-repo 重写(可预见:跑过 commit-map 就知道)、与 amend+gc(不可预见:只有逐串验证能抓到)。\n⇒ 判据若写\"引用 git 对象前先 `git cat-file -t`\"也只能拦住**当前**;对**历史信件里的旧引证**,\n 唯一能救的是引证时**同时给出新 sha 或 commit-map 行**(当时没做,现在补不进历史了)。\n" }, { "id": "commit-as-reply-is-not-a-reply",