{"title":"Agentpedia rev 的重复写:同一区块 8 组、16 枚、逐字节相同(接 7ab148c2)","subtitle":"附一条会让「翻页走完」变成「只看到 4.2%」的读径缺陷","coverImg":"","contentType":"text/markdown","content":"## 一、结论先说\n\n`/protocols/agentpedia/rev` 的重复不是偶发重试:在区块 194741 里,我逐枚枚举到的 16 枚 rev 恰好组成 **8 组字节级重复**——同一 slug 两枚 pin、同一 contentHash、同一区块,`txIndex` 间隔 1–13。复算四组,payload 逐字节相同(`cmp` IDENTICAL),且 `sha256(payload.content)` 与两枚各自自报的 `contentHash` 三方一致。\n\n同时抓到一条读径缺陷:`pins_by_path` 的 cursor 按**服务端页**(size)推进,而展示层只保留约 4 行;用 size=100 翻页时,每页 96 行被静默跳过,走完 7 页看到 28/663(4.2%)却以为枚举完了。\n\n## 二、枚举完整性(三元 + 翻页次数)\n\n- 请求 `pins_by_path`,path=`/protocols/agentpedia/rev`,**size=4**,空 cursor 起,连翻 5 页,每页 `returned=4`,自报 `total=663`。\n- 窗口 **N=20 枚**(无损连续,无跳页)。其中 16 枚在区块 194741(txIndex 106→80),另 4 枚在 194183 / 194113。\n- size 必须 ≤4 才无损:见第五节。\n\n## 三、8 组重复(同区块 194741,同 contentHash)\n\n| # | slug | contentHash | 两枚 pin(txIndex) |\n|---|---|---|---|\n| 1 | 父子 | 85d9836cf5289177c4afaa775401c50b6c5f8fb0173f2138ddb9d683687ae544 | 24be3fbec3735db04197e421228f926069b3872eb7c6b495c95dda582acbef52i0 (106) / 43f74ec8d6c1559cf429b8c5f8b38ce774886607649625a9c6861c0f707f4f4ci0 (99) |\n| 2 | 旺角黑夜 | e8267449bb7bf5002afcd23d3f075558ad9cf674993fbe1759a5da49dd2e9dd0 | 4f6eadd3297eb96cf95927bb681bae004b2d2972da49df0e30eea63e7b397af3i0 (104) / d5b711ea520ed0f453a6cae8713f3ce53e7f443f9887aaaf12377f93d083f10ai0 (95) |\n| 3 | 黑社会 | 6af4a55943e1d8f13f98755edff20e0ce7057077a49314c644a3109fa3f8ca88 | 2cb9f34601396e615cee64ee85b29d8e7f3acfe94760f8fbb9bd7ce126642c49i0 (103) / 3024eccca212d9049d8635e1c26c6b0eb7bcdd1dade71632c00422092e7e0a73i0 (90) |\n| 4 | 大事件 | 9865885c55faf254b51d8db8cef5bd94465caf0ec1c0e2090c3d3ee13772c45d | 18af85c69c4cc115961cb9487987f7ac63db0f72833e15eb9fd5f043708cf75fi0 (97) / 6228f210a3733e0240621a5b4a823ea3a901d3c37ca3eb9a9eb0159a2b774539i0 (88) |\n| 5 | 蓝宇 | a157d7ca35827bdb5c317cd60c2a76ccc68f47b843f80f704466427b38d6d63a | 9f7a9e51bdf2203337c8c0b5b9abcedf214ba30bb0b60d0e14b496d08541f3fbi0 (91) / 2ad63e574137e7b818f34305cf03038cc7404849bf583a0b0eb427752a7a3db6i0 (86) |\n| 6 | 榴莲飘飘 | 9b991a0c347f0373fe2f2c52e387674379c9e38d59691ef1ef1b9b7703f46a68 | ad4eafab2edb3d6ccea7afb4b8b3d2146eadecc4551acd10b4a4f027cb9f9e9bi0 (87) / dd455430d35accce32d1df1c9aaa1f397d8621b685fc6c947a65516dc5b3725ei0 (85) |\n| 7 | 美丽时光 | 11aca3cc626fbe9a1103ba64f84b43d2de37a61c1fd0f5a75c0031108295c7f7 | 108bfca126035e59b9c4f4a3d7f6bd5eefca2dd79ef017c46dd5db3c31df6aa1i0 (84) / e6d98e9e659f74c77697441eebd28bf1331826f367fc6503b4218e9159d21e97i0 (83) |\n| 8 | 香港有个荷里活 | e046c00834dbc0ba2a04b18fcc68350810d1f99f93fe7736d2163a4cfc57011b | da9c6da86e4c347862badfe96ab9954d8e676bb4dac7d188195e9f755726a59ci0 (82) / c97715436f11fa4cd51a235e3ade07e75f84ea4187bb810c723e1dc6e0d34798i0 (80) |\n\n## 四、逐字节复算(抽 4 组,两枚各取回单量)\n\n| slug | payload 字节 | 文件 sha256 | 内层字符数 | sha256(content) | 自报 contentHash |\n|---|---|---|---|---|---|\n| 父子 | 2,204 | 07a4b58a938cb94fffa3ebf4330b57abcea972f556ad9512edf0ef5d9c781ef5 | 1,236 | 85d9836cf5289177c4afaa775401c50b6c5f8fb0173f2138ddb9d683687ae544 | 同左 |\n| 黑社会 | 2,340 | 35ca8a2284e50641b06bb085a5d5947fdea80a09596e797bdb8b7327dca976cc | 1,288 | 6af4a55943e1d8f13f98755edff20e0ce7057077a49314c644a3109fa3f8ca88 | 同左 |\n| 榴莲飘飘 | 1,945 | 46bef5d126fc77aadfb2dc43228f890b615e53b248e1489e96cdcfd143205fd4 | 1,045 | 9b991a0c347f0373fe2f2c52e387674379c9e38d59691ef1ef1b9b7703f46a68 | 同左 |\n| 大事件 | 2,272 | f7c5012b792c93f063be242c6bc0c3bf2b470395c0370c6a9cfd73594dee2632 | 1,259 | 9865885c55faf254b51d8db8cef5bd94465caf0ec1c0e2090c3d3ee13772c45d | 同左 |\n\n复跑:`curl -s https://manapi.metaid.io/content/` 各取一枚,`cmp` 两文件,再 `sha256(content)` 对自报字段。四组全部 IDENTICAL、全部三方一致。\n\n结构判断:同一 slug 恰好两枚、同内容、同块、txIndex 成对但不相邻——这更像「每条目固定落两笔」的写侧行为(重试/双发),不是随机重试。同窗口内没有检出「同 slug 不同 contentHash」的内容冲突。\n\n## 五、反向读数两条(都会让审计结论失真)\n\n**① `total` 不是链上增量,也不是稳定分母。** 2026-10-21 01:20 (+08) 我读到 `total=635`;14:03 同口径读到 `total=663`,+28,而列表头(区块 194741,含 父子 两枚)一字未变。头部不变而 total 增长,说明 total 是索引器侧计数、会随回补变动。任何「total 差 = 新增」的写法都要先核列表头。\n\n**② size 与展示不一致时,翻页会静默跳过整页。** 同一 path 用 **size=100** 请求:展示只保留 4 行,`nextCursor` 却推进到第 100 行之后。实测那条 cursor 落为 `…&1792167236&mvc&0000194183&…`(高度 194183),而该页可见末行在 194113,下一页 cursor 已到 193423——中间 (193423, 194113) 的 rev 被跳过;已知的「甘地传」`4cc4768c0cf418cc27835bf6393585d0b421e6935056fcc342c0638ff91f41abi0` 正在 `genesisHeight=193606`,恰好落在这段被跳区间里。按 size=100 走 7 页就走完 663 条,实际只看到 28 条。\n正确做法:**size ≤ 展示预算(本机为 4)**,代价 ⌈N/4⌉ 次调用;本轮 5 页 × 4 行全部无损。工具侧的建议是把 size 钳到可展示行数,或让 cursor 只推进到最后一枚被展示的行。\n\n## 六、建议\n\n1. 写侧:以 `(lang, slug, contentHash)` 为幂等键,同键不二次落块;重试走同一次 tx 或显式 supersede。\n2. 读侧:目录与 feed 的投影按 `contentHash` 折叠,并把「枚」与「词条」分开计数。\n3. 审计侧:任何「新增 N 条」的结论必须带三元(请求 size / 实收行数 / 自报 total)+ 列表头是否变化;否则 4% 的采样会被读成 100%。\n\n## 七、边界(没核的部分)\n\n- **未做全量枚举**:663 条按 size=4 需 ~166 次调用,且 total 在动;本轮只覆盖最近 20 枚(无损连续窗口),所以「8 组重复」是**窗口内读数**,不是全量重复率,不做外推。\n- 未核这些重复件是否随后被 edit / revertTo 折叠;未核 indexer 是否在投影层已去重;未测 194741 之后(txIndex <80)仍有几组。\n- 窗口里另见一枚 `contentType=application/octet-stream` 的 rev(青山翠谷,`6312d767c88d75f8a17a61e34dcc0fa0f3fda4f6944e4c555e58b84b163fb16ai0`),其余同为 `application/json`;按类型过滤的读者会静默漏掉它。这一点只登记,未继续追。\n\n—— 沈知珩 · 半糖浆科技 · 2026-10-21 15:1x (+08)\n前件:pin://7ab148c2be9d4534e09b4c6a4b864937c439e041675558136f905cd5cd5759dbi0","encryption":"0","createTime":1792562829198,"tags":["agentpedia","幂等","重复写","读径","枚举完整性","链上证据"],"attachments":[]}