Problem
PackageInspector.stripComments 用 <!--.*?-->(.dotMatchesLineSeparators)對每個 word/**/_rels/*.rels 與其 part 做註解剝除。part 內若有大量未閉合 的 <!--,每個開頭都讓 lazy .*? 掃到檔尾:實測 40 / 80 / 160 KB 的 rels part → 4.4 / 16.7 / 62.3 s(二次);一個 5.3 KB 的 .docx(chart rels part 內塞未閉合 <!--,DocxReader 永遠不會 parse 它、但 inspector 每次都掃)展開後 62 s 牆鐘。inspector 明文「不是 parser、不驗 well-formedness」,所以這條輸入不會被擋在前面。
che-word-mcp 的 save gate 早就經 writeData(scratch 序列化)餵它,raw-channel parts 也會原樣搬運註解;che-word-mcp#199 的 list_images Direct Mode 是第一條把原始磁碟位元組 直接餵進來的唯讀路徑,DA 實測卡住整個 actor server。che-word-mcp 端會先做線性預掃(<!-- 多於 --> 就拒絕檢查)當緩解,但根因在這裡。
Type
bug
Expected
註解剝除改成線性掃描(找 <!-- 後找下一個 -->,找不到即視為到檔尾一次結束),或對 part 大小/註解數設上限並在報告裡具名(與 #130 的 IO 上限同族)。
Actual
二次退化,可被小檔觸發。
Source : surfaced during /idd-verify che-word-mcp#199 (Step 5b follow-up triage). Verify report: PsychQuant/che-word-mcp#218 (comment)
Refs PsychQuant/che-word-mcp#199 , #130
Current Status
Phase : needs-fix
Last updated : 2026-09-08 by idd-verify (R10 complete → fix round 11 landed 65f2ecc; R11 next) — unattended /idd-all, cluster PR #141
Key Decisions
Verify R10 FAIL(requirements/logic/regression PASS、security/DA FAIL;Codex usage limit 未跑) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — R9 的 blocking 全部確認 FIXED;阻擋項是本 PR 自己引入的放大(成因訊息裡的姊妹屬性名逐字進訊息,三 lens 匯聚、一詞可修);DA mutation probe 四個全綠(私有副本 0600、extraction walk NOFOLLOW、writer 標籤名界線、decodeBudget)+ 可選的 TargetMode 躲過 id gate;既有問題(base 3.6.4 相同)開 rels merge double-escapes character/entity references in Type/Target/TargetMode (silent, cumulative) #148 (rels 二次轉義)/writer fixed-slot collision with an unmanaged original relationship silently drops the fontTable relationship and leaves an orphan part #149 (固定槽碰撞丟 fontTable)/XMLParser's own error text reaches localizedDescription verbatim: multi-line and unbounded #150 (parser 錯誤文字無上限)/DocxWriter output bytes are not reproducible: docProps timestamps default to Date() #151 (位元組不可重現)/test suite leaves ~292 reader namespace directories per run (tests never call close()) #152 (測試套件殘留)並寫進 CHANGELOG。fix round 11 65f2ecc:全部折入,cluster 81 tests、全套 1553/0/31。
Verify R9 FAIL(requirements/logic FAIL——同一條 fix round 9 引入的迴歸;security/regression 撞 session limit 未完成;Codex usage limit 未跑;DA 未跑) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — 迴歸:Relationship 標籤 regex 惰性到 />,屬性值裡的 /> 把 attrs 切在值中間,fix9 的 tokenizer 回傳截斷值而不是 nil,merge 靜默寫出截斷的 Target、丟掉 TargetMode(三個 R8 拒存形狀變 exit 0);語料命中 0、無測試覆蓋。其餘:R8 各項全 FIXED、tokenizer 對 1025 份語料零影響、Id 定義三方一致 12 形狀;security partial 確認 N-S8-3 FIXED、發現 id 逐字進訊息可注入換行與 U+2028 未跳脫。fix round 10(63a0781,全套 1546/0)全部折入 :被切斷的標籤整個不信任(回到 R8 拒存)、relativeName 兩邊都 resolve、sibling 成因走 tokenizer、每個 id 與 entry 名稱經 displayName(含 U+2028/零寬/雙向/BOM,480 字封頂)、CHANGELOG 引證與四類差分。
Verify R8 FAIL(requirements/logic/security FAIL——全是宣稱與訊息層、無行為缺陷;regression 撞 session limit 未完成;Codex 撞 usage limit 未跑;DA 未跑) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — attempt 1(c8d13d5)四 lens 被網路中斷砍掉、partial 折成 fix round 8(symlink-safe walk、路徑不進訊息、同 uid 邊界);attempt 2(2ab2b2c):security 確認 TOCTOU/權限窗口/殘留/fallback 全收斂、不同 uid 確擋得住;requirements 確認 overlay 定義改變對 1025 份語料零影響;regression 確認 740 份 reader/inspector/存檔逐 byte 相同。擋下的是:entry 名稱未跳脫(可偽造第二行輸出)、relative 少算 8 字元、「所有訊息不含路徑」與「兩條 text scan 同一定義」兩句被實測推翻。fix round 9(9dc4d44,全套 1541/0)全部折入 :entry 名稱跳脫截斷、overlay 改逐屬性 tokenizer(永不看進別的屬性值)+標籤名邊界與診斷一致、relative 以 resolved root 計算、成因新增「同列其他屬性單引號」。
Verify R7 FAIL(Codex/requirements/logic/security FAIL;regression 被 session resume 中止、DA 未跑,由 R8 覆蓋) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — R6 的兩個 Codex HIGH 只到 NARROWED;Codex 再抓兩個 HIGH:命名空間「檢查後建立」的 race(另一 uid 可搶建;security 同 uid 競態 20 秒導走 13256 個解壓目錄)、spelling map 同 bucket 陣列查重又是 O(n²) 且 regex 誤認 data-Id;logic:失敗路徑清理不完整(目錄 entry 0300/0400/0500 讓 removeItem 刪不掉,整棵樹含私有副本留在共用命名空間)、Target 值裡的 Id='…' 仍劫持成因;requirements:「九種是封閉列舉」不封閉(第十種:前綴屬性排在真 Id 前),overlay 與診斷對 Id 定義不一致。fix round 7(c8d13d5,全套 1536/0)全部折入 :命名空間 mkdir(0700)+O_NOFOLLOW fd+fstat 驗證、mkdirat/openat(O_CREAT|O_EXCL, 0600)、失敗路徑寬鬆 chmod walk 後刪、unzipItem 錯誤具名且無路徑、成因改由該 <Relationship> 標籤的 occurrence 推導(解碼預算 200、前 20 個 deficit 即停、fallback 20 對、顯示 120 字)、overlay attribute() 同一定義(第十種現在照常存檔)、空/NUL 測試計數、CHANGELOG 逐句改正。Follow-ups:ooxml-swift#147(目錄 entry 權限位讓整份被拒)、PackageInspector: raw Cocoa read errors escape the invalidDocx envelope; word/.xml is skipped; pass-1 orphan invariant only implicit (R5 logic L6/L8/L9) #146 補述。
Verify R6 FAIL(Codex/requirements/logic FAIL、security/regression PASS、DA 撞 session limit 未完成) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — 功能面零回歸(reader/inspector/存檔 740 份逐檔相同、head 零暫存殘留);R5 的 TOCTOU 與 structure O(n²) 各 lens 確認 FIXED;Codex 再抓兩個 HIGH:成因診斷逐 id 重掃 rels(O(n²),DA partial 實測 400/800 個 id = 1.0/3.9 s)、解壓目錄/私有副本在 chmod 前非 owner-only;requirements 的 blocking 全是可實測為假的文件句(ofPackageAt: 不省寫出、五種拒絕 entry、內容 part 的宣告不丟);logic:四條快照式 leak 測試對並行 reader 必然失敗。fix round 6(7417f5d,四次 amend、全套 1527/0)全部折入 :owner-only 自第一個位元組、單次掃描+上限 20 且超過不再計算(3200 個 id = 80 ms)、四條測試改 per-test marker、命名空間目錄拒 symlink/他人 uid、文件逐句改正。
Verify R5 FAIL(六 lens 全完成) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — 功能面零回歸(740 份 reader/inspector/存檔逐檔相同、9 份 SIGTRAP 變具名錯誤);blocking:預掃與解壓不是同一份不可變位元組(TOCTOU,security 實測 15% 逃逸)、fix4 引入的 structure 去重 O(n²)(292 KB → 27 s)、inspector 與 reader 共用暫存命名空間讓兩個既有測試不 hermetic、列舉失敗規則零測試、封閉列舉措辭。fix round 5(ba4ccbe,已 ff-merge) :ZipHelper.unzip(data:namespace:) 位元組讀一次、in-memory 預掃、私有副本解壓、inspector 專屬命名空間、權限位重設、空/NUL 路徑拒;discovery 零字串摺疊(.xml.rels identity)、stat 錯誤拋錯;rels 至少宣告一條才讀 part;structure 四類各記一次;字元參照成因解碼具名;列舉可注入並有失敗測試;CHANGELOG 同輪交錯效能表(3.6.4 2156 / rc2 1795 / 3.7.0 7247 ms)。1521 tests / 0 failures。Follow-ups:ZipHelper.unzip entry-path policy should reuse PathValidator.isSafeRelativeOOXMLPath (repo has two definitions of a safe relative path; the newer one is looser) #145 (PathValidator 重用)、PackageInspector: raw Cocoa read errors escape the invalidDocx envelope; word/.xml is skipped; pass-1 orphan invariant only implicit (R5 logic L6/L8/L9) #146 (hardening 殘留)、che-word-mcp#222(consumer 釘 3.4.0 編不過 main)。
Verify R4 FAIL :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — Codex/requirements/security FAIL、logic PASS;regression 與 DA agent 停滯未完成(R5 覆蓋)。security 抓到 CRITICAL :ZIPFoundation 0.9.20 對「指向 . 的 symlink + ..」合取擋不住,fix round 3 的解壓把 reader 既有的逃逸洞接到了 inspector。fix round 4(55ee4e6,已 ff-merge 進 cluster branch) :discovery 全面改用檔案 identity(device+inode)、沒有 rels 的 part 不讀、列舉失敗拋錯、ZipHelper.unzip 對 reader 與 inspector 一體拒絕 symlink/../絕對路徑 entry(合取鏈測試斷言零 canary)、前綴規則改問 mapping、writer 結構閘門改 parser 事件並逐 id 具名成因、DocxWriter 把 rId1–rId4 寫死給 styles/settings/fontTable/numbering——真實封裝若把 rId1 用在圖片就無法存檔(sister concern from #139) #140 兩集合各報;CHANGELOG 九種拼法、磁碟/對抗軸成本、副作用聲明。1516 tests / 0 failures;740 份語料零差異。Follow-up:PackageInspector only scans .xml source parts; a .vml (or other XML) part with an image rels is never reconciled, contrary to the documented "any rels under word/" contract #144 (非 .xml source part)。
Verify R3 FAIL(六 lens) :cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141 (comment) — 兩個根因:R2 的 B7「開 namespace 處理即拒未宣告前綴」是假修(libxml2 SAX 只記錄 nsWellFormed;守門測試把 <zz:x/> 放在根元素之後、revert 整個修法照綠);R2 用 lowercased() 索引模擬檔案系統本身是新的靜音開關(碰撞時依封裝順序選、APFS 摺 U+017F、./ 收合)。
fix round 3(3807b6a,在隔離 worktree 完成、ff-merge 進 cluster branch) :inspector 改走 reader 自己的 ZipHelper.unzip、part 以 OPC 位址組字串交檔案系統查(不模擬);解壓失敗/無 word/document.xml → invalidDocx;delegate 自判未宣告前綴(真實 writer 唯一會產生的那一類);撤回「reader 拒的 inspector 也拒」全稱宣稱 (DA D1 裁決:走封閉列舉——拒絕理由四類、不模擬的 namespace 類別點名、isConsistent 是關係陳述),以 leniency 測試釘住;預檢驗完整 UTF-8、自閉合算深度;writer 以 rels 檔存在判斷、拒非 UTF-8/零長度/含註解/CDATA/前綴 <Relationship> 的 rels、訊息依成因分句(DocxWriter 把 rId1–rId4 寫死給 styles/settings/fontTable/numbering——真實封裝若把 rId1 用在圖片就無法存檔(sister concern from #139) #140 亦然)。1512 tests / 0 failures。
效能(release、740 份、三代) :合計 2043 → 1663 → 6204 ms、中位 0.21 → 0.14 → 5.3 ms——每份約 5 ms 固定解壓 IO,是不模擬檔案系統的價錢,CHANGELOG 照實寫。
Follow-ups R3:ooxml-swift#143(strict namespace image type 不對稱);xmlns 數軸與屬性密集常數併 IO 層對不受信任 .docx 無大小上限:0.56 MB 檔 → 1.7 GB RSS / 39 s;PackageInspector 新增一次不受信任讀取 #130 ;合法 rels 拼法 round-trip → writer 端仍以原始 rels 文字比對已解碼的 model id:hasNewTypedRelationships 與 RelationshipsOverlay.parseRels 是 #137 的同一反模式(sister concern from #137) #142 ;固定槽 → DocxWriter 把 rId1–rId4 寫死給 styles/settings/fontTable/numbering——真實封裝若把 rId1 用在圖片就無法存檔(sister concern from #139) #140 。
R2:https://github.com/PsychQuant/ooxml-swift/pull/141(Verify (R2) comment);R1 同 PR。MAX_ROUND=2 已超(standing instruction:全 IDD 走完)。
Blocking
Problem
PackageInspector.stripComments用<!--.*?-->(.dotMatchesLineSeparators)對每個word/**/_rels/*.rels與其 part 做註解剝除。part 內若有大量未閉合的<!--,每個開頭都讓 lazy.*?掃到檔尾:實測 40 / 80 / 160 KB 的 rels part → 4.4 / 16.7 / 62.3 s(二次);一個 5.3 KB 的 .docx(chart rels part 內塞未閉合<!--,DocxReader永遠不會 parse 它、但 inspector 每次都掃)展開後 62 s 牆鐘。inspector 明文「不是 parser、不驗 well-formedness」,所以這條輸入不會被擋在前面。che-word-mcp 的 save gate 早就經
writeData(scratch 序列化)餵它,raw-channel parts 也會原樣搬運註解;che-word-mcp#199 的list_imagesDirect Mode 是第一條把原始磁碟位元組直接餵進來的唯讀路徑,DA 實測卡住整個 actor server。che-word-mcp 端會先做線性預掃(<!--多於-->就拒絕檢查)當緩解,但根因在這裡。Type
bug
Expected
註解剝除改成線性掃描(找
<!--後找下一個-->,找不到即視為到檔尾一次結束),或對 part 大小/註解數設上限並在報告裡具名(與 #130 的 IO 上限同族)。Actual
二次退化,可被小檔觸發。
Source: surfaced during /idd-verify che-word-mcp#199 (Step 5b follow-up triage). Verify report: PsychQuant/che-word-mcp#218 (comment)
Refs PsychQuant/che-word-mcp#199, #130
Current Status
Phase: needs-fix
Last updated: 2026-09-08 by idd-verify (R10 complete → fix round 11 landed
65f2ecc; R11 next) — unattended /idd-all, cluster PR #141Key Decisions
TargetMode躲過 id gate;既有問題(base 3.6.4 相同)開 rels merge double-escapes character/entity references in Type/Target/TargetMode (silent, cumulative) #148(rels 二次轉義)/writer fixed-slot collision with an unmanaged original relationship silently drops the fontTable relationship and leaves an orphan part #149(固定槽碰撞丟 fontTable)/XMLParser's own error text reaches localizedDescription verbatim: multi-line and unbounded #150(parser 錯誤文字無上限)/DocxWriter output bytes are not reproducible: docProps timestamps default to Date() #151(位元組不可重現)/test suite leaves ~292 reader namespace directories per run (tests never call close()) #152(測試套件殘留)並寫進 CHANGELOG。fix round 1165f2ecc:全部折入,cluster 81 tests、全套 1553/0/31。Relationship標籤 regex 惰性到/>,屬性值裡的/>把 attrs 切在值中間,fix9 的 tokenizer 回傳截斷值而不是 nil,merge 靜默寫出截斷的Target、丟掉TargetMode(三個 R8 拒存形狀變 exit 0);語料命中 0、無測試覆蓋。其餘:R8 各項全 FIXED、tokenizer 對 1025 份語料零影響、Id 定義三方一致 12 形狀;security partial 確認 N-S8-3 FIXED、發現 id 逐字進訊息可注入換行與 U+2028 未跳脫。fix round 10(63a0781,全套 1546/0)全部折入:被切斷的標籤整個不信任(回到 R8 拒存)、relativeName兩邊都 resolve、sibling 成因走 tokenizer、每個 id 與 entry 名稱經displayName(含 U+2028/零寬/雙向/BOM,480 字封頂)、CHANGELOG 引證與四類差分。c8d13d5)四 lens 被網路中斷砍掉、partial 折成 fix round 8(symlink-safe walk、路徑不進訊息、同 uid 邊界);attempt 2(2ab2b2c):security 確認 TOCTOU/權限窗口/殘留/fallback 全收斂、不同 uid 確擋得住;requirements 確認 overlay 定義改變對 1025 份語料零影響;regression 確認 740 份 reader/inspector/存檔逐 byte 相同。擋下的是:entry 名稱未跳脫(可偽造第二行輸出)、relative少算 8 字元、「所有訊息不含路徑」與「兩條 text scan 同一定義」兩句被實測推翻。fix round 9(9dc4d44,全套 1541/0)全部折入:entry 名稱跳脫截斷、overlay 改逐屬性 tokenizer(永不看進別的屬性值)+標籤名邊界與診斷一致、relative以 resolved root 計算、成因新增「同列其他屬性單引號」。data-Id;logic:失敗路徑清理不完整(目錄 entry 0300/0400/0500 讓removeItem刪不掉,整棵樹含私有副本留在共用命名空間)、Target值裡的Id='…'仍劫持成因;requirements:「九種是封閉列舉」不封閉(第十種:前綴屬性排在真Id前),overlay 與診斷對Id定義不一致。fix round 7(c8d13d5,全套 1536/0)全部折入:命名空間mkdir(0700)+O_NOFOLLOWfd+fstat驗證、mkdirat/openat(O_CREAT|O_EXCL, 0600)、失敗路徑寬鬆 chmod walk 後刪、unzipItem錯誤具名且無路徑、成因改由該<Relationship>標籤的 occurrence 推導(解碼預算 200、前 20 個 deficit 即停、fallback 20 對、顯示 120 字)、overlayattribute()同一定義(第十種現在照常存檔)、空/NUL 測試計數、CHANGELOG 逐句改正。Follow-ups:ooxml-swift#147(目錄 entry 權限位讓整份被拒)、PackageInspector: raw Cocoa read errors escape the invalidDocx envelope;word/.xmlis skipped; pass-1 orphan invariant only implicit (R5 logic L6/L8/L9) #146 補述。ofPackageAt:不省寫出、五種拒絕 entry、內容 part 的宣告不丟);logic:四條快照式 leak 測試對並行 reader 必然失敗。fix round 6(7417f5d,四次 amend、全套 1527/0)全部折入:owner-only 自第一個位元組、單次掃描+上限 20 且超過不再計算(3200 個 id = 80 ms)、四條測試改 per-test marker、命名空間目錄拒 symlink/他人 uid、文件逐句改正。ba4ccbe,已 ff-merge):ZipHelper.unzip(data:namespace:)位元組讀一次、in-memory 預掃、私有副本解壓、inspector 專屬命名空間、權限位重設、空/NUL 路徑拒;discovery 零字串摺疊(.xml.relsidentity)、stat 錯誤拋錯;rels 至少宣告一條才讀 part;structure 四類各記一次;字元參照成因解碼具名;列舉可注入並有失敗測試;CHANGELOG 同輪交錯效能表(3.6.4 2156 / rc2 1795 / 3.7.0 7247 ms)。1521 tests / 0 failures。Follow-ups:ZipHelper.unzip entry-path policy should reuse PathValidator.isSafeRelativeOOXMLPath (repo has two definitions of a safe relative path; the newer one is looser) #145(PathValidator 重用)、PackageInspector: raw Cocoa read errors escape the invalidDocx envelope;word/.xmlis skipped; pass-1 orphan invariant only implicit (R5 logic L6/L8/L9) #146(hardening 殘留)、che-word-mcp#222(consumer 釘 3.4.0 編不過 main)。.的 symlink +..」合取擋不住,fix round 3 的解壓把 reader 既有的逃逸洞接到了 inspector。fix round 4(55ee4e6,已 ff-merge 進 cluster branch):discovery 全面改用檔案 identity(device+inode)、沒有 rels 的 part 不讀、列舉失敗拋錯、ZipHelper.unzip對 reader 與 inspector 一體拒絕 symlink/../絕對路徑 entry(合取鏈測試斷言零 canary)、前綴規則改問 mapping、writer 結構閘門改 parser 事件並逐 id 具名成因、DocxWriter把rId1–rId4寫死給 styles/settings/fontTable/numbering——真實封裝若把rId1用在圖片就無法存檔(sister concern from #139) #140 兩集合各報;CHANGELOG 九種拼法、磁碟/對抗軸成本、副作用聲明。1516 tests / 0 failures;740 份語料零差異。Follow-up:PackageInspector only scans.xmlsource parts; a.vml(or other XML) part with an image rels is never reconciled, contrary to the documented "any rels under word/" contract #144(非.xmlsource part)。nsWellFormed;守門測試把<zz:x/>放在根元素之後、revert 整個修法照綠);R2 用lowercased()索引模擬檔案系統本身是新的靜音開關(碰撞時依封裝順序選、APFS 摺 U+017F、./收合)。3807b6a,在隔離 worktree 完成、ff-merge 進 cluster branch):inspector 改走 reader 自己的ZipHelper.unzip、part 以 OPC 位址組字串交檔案系統查(不模擬);解壓失敗/無word/document.xml→invalidDocx;delegate 自判未宣告前綴(真實 writer 唯一會產生的那一類);撤回「reader 拒的 inspector 也拒」全稱宣稱(DA D1 裁決:走封閉列舉——拒絕理由四類、不模擬的 namespace 類別點名、isConsistent是關係陳述),以 leniency 測試釘住;預檢驗完整 UTF-8、自閉合算深度;writer 以 rels 檔存在判斷、拒非 UTF-8/零長度/含註解/CDATA/前綴<Relationship>的 rels、訊息依成因分句(DocxWriter把rId1–rId4寫死給 styles/settings/fontTable/numbering——真實封裝若把rId1用在圖片就無法存檔(sister concern from #139) #140 亦然)。1512 tests / 0 failures。hasNewTypedRelationships與RelationshipsOverlay.parseRels是 #137 的同一反模式(sister concern from #137) #142;固定槽 →DocxWriter把rId1–rId4寫死給 styles/settings/fontTable/numbering——真實封裝若把rId1用在圖片就無法存檔(sister concern from #139) #140。Blocking
65f2ecc;PASS → merge PR cluster: PackageInspector parses XML with the reader's parser, and no serialization path traps (#137, #138, #139) #141、tag v3.7.0、idd-close ×3;FAIL → fix round 12。