From b1830053e579ea6e697c191fe2d7f85d67437e1f Mon Sep 17 00:00:00 2001 From: richblack Date: Thu, 27 Aug 2026 14:25:17 +0800 Subject: [PATCH] =?UTF-8?q?dispatch-format-guard=20=E4=BF=AE=E5=85=A9?= =?UTF-8?q?=E8=99=95=EF=BC=9A=E8=A6=8F=E5=89=87=E8=87=AA=E5=B7=B1=E7=9A=84?= =?UTF-8?q?=E5=90=88=E6=A0=BC=E7=AF=84=E4=BE=8B=E6=9C=83=E8=A2=AB=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=E6=93=8B=EF=BC=8C=E7=A6=AE=E8=B2=8C=E6=94=B6=E5=B0=BE?= =?UTF-8?q?=E4=B8=8D=E8=A9=B2=E7=AE=97=E9=81=95=E8=A6=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit inkstone/ISEP#65:leo 發現「寫完 ISEP#30 那條規則之後,同一天總管又犯了 8 次」, 派來查「現行閘為什麼抓不到」。查證結果分兩層: ━━ 真正在跑的閘其實是 dispatch-format-guard.sh,不是 no-ticket-no-dispatch.sh ━━ no-ticket-no-dispatch.sh 的殼驗證早就是已知行為(只驗有沒有一行【工單】), 但 ISEP#30 已經為此新增了 dispatch-format-guard.sh 做內容判定,測試 19/19 通過。 問題是它有兩個沒被那 19 條測資蓋到的洞: 1. **regex 洞(本體 bug)**:頂層 CLAUDE.md 規定的合格格式帶全形括號—— 「【工單】owner/repo#N(→ comment M)」。_COMMENT_RE 只吃「→ comment M」 本體,兩側括號沒被算進去,殘留括號讓 _REF_RE 比不過,於是**規則自己定義 的合格範例會被自己的閘擋下**(半形括號 `(...)` 同樣會中)。用今天派我這張 票的那份派工單原句實測,改之前 exit=2「票號形狀不對」。 2. **零容忍過頭**:純禮貌收尾(「謝謝」)跟「記得先讀 CLAUDE.md」這種真內容 一樣被當「派工單不只有票號」擋下。ISEP#65 test 4 明講這四種不該擋 (只有票號/空行/「謝謝」/括號包住的 comment 格式),優先做成低誤鎖。 判斷:不改 no-ticket-no-dispatch.sh、不另立第三支閘——dispatch-format-guard.sh 已經是「另立一支」的正確位置,這兩個洞在它自己的地盤上補。 修法: - _COMMENT_RE 兩側括號(全形/半形)都設可選 - 新增 _COURTESY_CLOSERS 白名單(謝謝/多謝/感謝/辛苦了…)+ _is_courtesy_closer(), 只在圍欄外生效,判準仍是「整行清乾淨標點後完全相等」不是「包含」—— 白名單不是黑名單,猜漏頂多誤鎖一次,不會反過來放走真內容(③g 測資證明) 測試:hooks/tests/dispatch-format-guard.test.sh 19→33 條,全過。新增: - ③b/③c 全形/半形括號格式(規則自己的例句) - ③d/③e/③f ISEP#65 test 4 的三種不該擋 - ③g 白名單邊界(禮貌詞混真內容裡照樣算數,防止白名單被誤用成漏洞) - ⑮b–⑮i:leo 點名的「寫完規則後又犯的 8 次」當回歸樣本,內容是從 ISEP#60/#61/#64、Arcrun#142/#144/#165、arcrun-rag#104、InkStoneCo#102 的真實票內文摘錄(見 hooks/tests/fixtures/README.md 記載來歷), 不是想像出來的例子;8 種形狀全部驗證會被擋 reply-identity.test.sh 11/11 仍全過(共用 dispatch_parse.py 沒有回歸) 升版 0.5.0 → 0.5.1(改完不升版沒人吃得到;check-version-consistency.sh 在本分支照慣例是紅的,tag 於 merge 時打)。 另查:.shell-payload/ 整個被 gitignore(scripts/vendor-to-shell.py 產物), 不是 git 分發的一部分——「這支閘會不會被吃到」取決於消費端有沒有重跑 plugin update/vendor-to-shell.py,不是這個 repo 委交的內容缺漏,故不在 本票改動範圍內,僅記錄供總管排查用。 未動 no-ticket-no-dispatch.sh(判斷見上,職責保持不重疊,兩支閘互斥見 dispatch_parse.py 的 has_ticket_marker 分岔)。 Co-Authored-By: Claude Opus 5 --- .claude-plugin/plugin.json | 2 +- docs/hooks-inventory.md | 2 +- hooks/lib/dispatch_parse.py | 43 ++++++++++++--- hooks/tests/dispatch-format-guard.test.sh | 53 +++++++++++++++++++ hooks/tests/fixtures/README.md | 18 +++++++ .../tests/fixtures/real-content-arcrun142.txt | 5 ++ .../tests/fixtures/real-content-arcrun144.txt | 5 ++ .../tests/fixtures/real-content-arcrun165.txt | 5 ++ .../fixtures/real-content-arcrunrag104.txt | 5 ++ .../fixtures/real-content-inkstoneco102.txt | 5 ++ hooks/tests/fixtures/real-content-isep60.txt | 6 +++ hooks/tests/fixtures/real-content-isep61.txt | 5 ++ hooks/tests/fixtures/real-content-isep64.txt | 6 +++ 13 files changed, 152 insertions(+), 8 deletions(-) create mode 100644 hooks/tests/fixtures/real-content-arcrun142.txt create mode 100644 hooks/tests/fixtures/real-content-arcrun144.txt create mode 100644 hooks/tests/fixtures/real-content-arcrun165.txt create mode 100644 hooks/tests/fixtures/real-content-arcrunrag104.txt create mode 100644 hooks/tests/fixtures/real-content-inkstoneco102.txt create mode 100644 hooks/tests/fixtures/real-content-isep60.txt create mode 100644 hooks/tests/fixtures/real-content-isep61.txt create mode 100644 hooks/tests/fixtures/real-content-isep64.txt diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index e4eea3a..01b9e3a 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "isep", "description": "InkStone Environment Plugin —— leo 的 Claude Code 環境唯一真相源:48 支機械閘(59 條註冊,白話盤點見 docs/hooks-inventory.md)、7 支 slash command、2 支 skill、27 支腳本,外加治理規範與標籤真相源。本機與雲端裝同一份,沒有子集。", - "version": "0.5.0", + "version": "0.5.1", "keywords": [ "inkstone", "guardrails", diff --git a/docs/hooks-inventory.md b/docs/hooks-inventory.md index 23695ea..955e689 100644 --- a/docs/hooks-inventory.md +++ b/docs/hooks-inventory.md @@ -77,7 +77,7 @@ | `micromanage-guard.sh` | 派工單寫得太細(指名檔案函式、編號步驟、要求每做一項回報一次…)就擋下——subagent 該被當成有能力的同事,不是照抄劇本的工具。 | 🛑 擋 | | `irreversible-dispatch-guard.sh` | 派工單裡出現「刪分支」「drop table」「rm -rf」這類不可逆動作,卻沒寫「先停下來等回覆才執行」就擋下。 | 🛑 擋 | | `no-ticket-no-dispatch.sh` | 派工單裡沒有寫工單號(`【工單】owner/repo#N`),或那張票已經關閉/根本不存在,就擋下——沒有票號的工作沒有人追得到進度。 | 🛑 擋 | -| `dispatch-format-guard.sh` | **派工單 = 票號,就這樣。** 票號以外還寫了別的東西(背景、驗收條件、紅線、交件方式、「這個 session 才知道的事」)就擋下,並告訴你那些內容該搬去哪:每次都一樣的 → 共通規定;這次才知道的 → **寫進那張票**。反過來,合規的派工它會**自動把共通規定注入**給收工方(交件方式、不准 push main、org 是 `inkstone`…),所以你不寫也不會漏。判準是「這一行是不是【工單】欄位」——**在不在**,不是寫了什麼,所以講得再謹慎也不會被多罰。 | 🛑 擋(+注入) | +| `dispatch-format-guard.sh` | **派工單 = 票號,就這樣。** 票號以外還寫了別的東西(背景、驗收條件、紅線、交件方式、「這個 session 才知道的事」)就擋下,並告訴你那些內容該搬去哪:每次都一樣的 → 共通規定;這次才知道的 → **寫進那張票**。反過來,合規的派工它會**自動把共通規定注入**給收工方(交件方式、不准 push main、org 是 `inkstone`…),所以你不寫也不會漏。判準是「這一行是不是【工單】欄位」——**在不在**,不是寫了什麼,所以講得再謹慎也不會被多罰。純禮貌收尾(「謝謝」)不算違規;`(→ comment M)` 不論全形或半形括號都認得(ISEP#65 修過一次:CLAUDE.md 自己規定的合格寫法曾經被這支閘自己擋下)。 | 🛑 擋(+注入) | ## D2. AI 想開口問你問題的當下(PreToolUse / AskUserQuestion) diff --git a/hooks/lib/dispatch_parse.py b/hooks/lib/dispatch_parse.py index f8c6507..1da6dc6 100644 --- a/hooks/lib/dispatch_parse.py +++ b/hooks/lib/dispatch_parse.py @@ -37,12 +37,16 @@ 當日實證:文字層的閘 8 次誤攔、0 次正確攔截,**紅線寫得越細,命中關鍵字的機率越高 ⇒ 那些閘在懲罰謹慎**。 - 本檔**全檔零個「命中某個詞就違規」的比對**。判準是「這一行是不是【工單】欄位」—— - **在不在**,不是**寫什麼**。用到的正則只有兩種,都在認形狀: + 本檔**沒有「命中某個詞就違規」的比對**。判準是「這一行是不是【工單】欄位」—— + **在不在**,不是**寫什麼**。用到的正則有三種,全部在認形狀: - `【某某】` 欄位標記本身 - - `owner/repo#N` 票號形狀 - 措辭多謹慎、有沒有提到「紅線」「部署」「main」,本檔一律不看。 - ⇒ 也因此它不會像文字層的閘那樣「寫得越小心越容易被罰」。 + - `owner/repo#N`(可選外掛全形/半形括號的 `→ comment M`)票號形狀 + - 唯一例外(ISEP#65):`_COURTESY_CLOSERS` 是一份**白名單**,只用來認出 + 「這一行是不是零資訊的禮貌收尾」,作用方向與黑名單相反——認出的是 + 「不算違規」,不是「算違規」;且要求整行清乾淨標點後完全相等,不是 + 「包含」,所以不會反過來放走任何真正的內容。 + 措辭多謹慎、有沒有提到「紅線」「部署」「main」,本檔一律不看,仍然不會被 + 「寫得越小心越容易被罰」的病拖下水。 ━━ 派工單格式 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 【工單】inkstone/ISEP#30 → comment 4322 @@ -83,9 +87,32 @@ IDENTITY_ROLES = ("總管", "subagent", "leo") _MARKER_RE = re.compile(r"^【([^】]{1,12})】\s*(.*)$") _REF_RE = re.compile(r"^([A-Za-z0-9_.-]+)/([A-Za-z0-9_.-]+)#(\d+)\s*$") -_COMMENT_RE = re.compile(r"(?:#issuecomment-|→\s*comment\s*)(\d+)") +# 🔴 inkstone/ISEP#65:頂層 CLAUDE.md 規定的合格格式帶著全形括號—— +# 「【工單】owner/repo#N(→ comment M)」。舊版正則只吃「→ comment M」本體, +# 兩側的括號(全形/半形都要吃)沒被算進去,殘留的括號讓 _REF_RE 比不過, +# 於是**規則自己定義的合格範例會被自己的閘擋下**。這裡把左右括號都收進來, +# 兩種都設成可選(沒有括號的舊寫法、純 #issuecomment- 連結都還要能過)。 +_COMMENT_RE = re.compile(r"[((]?\s*(?:#issuecomment-|→\s*comment\s*)(\d+)\s*[))]?") _FENCE_RE = re.compile(r"^\s*(```|~~~)") +# 🔴 inkstone/ISEP#65:票號後面只掛一句純禮貌收尾(例:「謝謝」),不該被當成 +# 「派工單不只有票號」的違規——它不含任何要搬去票上或共通規定的實質內容。 +# 這是**白名單,不是黑名單**:判準仍是結構(整行清乾淨標點後「等於」清單裡的 +# 固定字串,不是「包含」),漏收某個禮貌講法頂多誤鎖一次(代價小,見 ISEP#65 +# 的「優先做成低誤鎖」),但不會反過來放走任何真正的內容——真內容不會剛好 +# 打出一個字不差的空話。全檔仍然沒有「命中某個詞就違規」的比對,這裡是唯一 +# 一處字面比對,且作用方向相反:認出「不是內容」,不是認出「是違規」。 +_COURTESY_CLOSERS = frozenset({ + "謝謝", "謝謝你", "謝謝您", "謝謝囉", "多謝", "感謝", "感恩", "辛苦了", "收到", + "thanks", "thank you", "thx", +}) +_COURTESY_STRIP_RE = re.compile(r"[\s。!!.,~~,、]+$") + + +def _is_courtesy_closer(line): + s = _COURTESY_STRIP_RE.sub("", line.strip()) + return s.lower() in _COURTESY_CLOSERS + def _split_lines(text): """切成 (是否在圍欄內, 原始行)。 @@ -146,6 +173,10 @@ def parse_dispatch(prompt): continue if raw.strip(): # 【工單】欄位本身只准佔它自己那一行;它下面的續行也是多的。 + # 白名單豁免只在圍欄外生效——圍欄裡的東西本來就該一律算數(test ⑮), + # 這裡不額外放寬,避免有人把真內容包在圍欄裡再貼一句「謝謝」蒙混。 + if not in_fence and _is_courtesy_closer(raw): + continue extra.append((cur, raw)) return { diff --git a/hooks/tests/dispatch-format-guard.test.sh b/hooks/tests/dispatch-format-guard.test.sh index 515bb53..9f50996 100755 --- a/hooks/tests/dispatch-format-guard.test.sh +++ b/hooks/tests/dispatch-format-guard.test.sh @@ -61,6 +61,32 @@ t 0 "② 合規:兩張票就兩行【工單】" \ t 0 "③ 合規:用 #issuecomment- 定址也認得" \ "$(payload '【工單】inkstone/ISEP#30#issuecomment-4327')" +# ── inkstone/ISEP#65:CLAUDE.md 規定的合格格式(全形/半形括號包住 → comment M) +# 曾經被本閘自己擋下——這裡把它釘成迴歸測試,不再走回頭路。 +t 0 "③b 合規:全形括號包住 → comment M(CLAUDE.md 標準寫法,曾被自己的閘擋下)" \ + "$(payload '【工單】inkstone/ISEP#65(→ comment 4515)')" + +t 0 "③c 合規:半形括號變體" \ + "$(payload '【工單】inkstone/ISEP#65(→ comment 4515)')" + +# ── inkstone/ISEP#65 test 4:不該擋的三種雜訊 ──────────────────────── +t 0 "③d 不該擋:票號後面只有空行" \ + "$(payload '【工單】inkstone/ISEP#65 + +')" + +t 0 "③e 不該擋:票號後面只有一句「謝謝」(零資訊禮貌收尾,白名單豁免)" \ + "$(payload '【工單】inkstone/ISEP#65 +謝謝')" + +t 0 "③f 不該擋:禮貌收尾帶標點也認得(「謝謝!」)" \ + "$(payload '【工單】inkstone/ISEP#65 +謝謝!')" + +t 2 "③g 白名單不是漏洞:禮貌詞混在真內容裡照樣算數" \ + "$(payload '【工單】inkstone/ISEP#65 +辛苦了,順便告訴你 main 現在是 8e28041')" + t 0 "④ 不是派工的動作(Bash)→ 一律不管" \ '{"tool_name":"Bash","tool_input":{"command":"ls"}}' @@ -132,6 +158,33 @@ t 2 "⑮ 內容躲在圍欄裡也算數(圍欄只讓裡面的【】不被當 【就地】main 現在是 8e7e265 ```')" +# ── inkstone/ISEP#65 test 5:拿「寫完規則之後總管又犯的 8 次」當回歸樣本 ── +# 內容是從各自票的真實內文摘錄(hooks/tests/fixtures/README.md 記著來歷), +# 不是想像出來的例子。8 種形狀都要被擋,證明這不是只補了一個特例。 +t 2 "⑮b 回歸:ISEP#60 那次的內容(自造第四套機制的診斷)" \ + "$(payload "$FIX/real-content-isep60.txt")" + +t 2 "⑮c 回歸:ISEP#61 那次的內容(push-guard 戳記機制失敗史)" \ + "$(payload "$FIX/real-content-isep61.txt")" + +t 2 "⑮d 回歸:ISEP#64 那次的內容(PR 分診現況數字)" \ + "$(payload "$FIX/real-content-isep64.txt")" + +t 2 "⑮e 回歸:Arcrun#142 那次的內容(庫索引從沒被產生過)" \ + "$(payload "$FIX/real-content-arcrun142.txt")" + +t 2 "⑮f 回歸:Arcrun#144 那次的內容(樹狀圖驗收記錄)" \ + "$(payload "$FIX/real-content-arcrun144.txt")" + +t 2 "⑮g 回歸:Arcrun#165 那次的內容(MCP 徹查現場)" \ + "$(payload "$FIX/real-content-arcrun165.txt")" + +t 2 "⑮h 回歸:arcrun-rag#104 那次的內容(收檔實測數字)" \ + "$(payload "$FIX/real-content-arcrunrag104.txt")" + +t 2 "⑮i 回歸:InkStoneCo#102 那次的內容(milestone 打假背景)" \ + "$(payload "$FIX/real-content-inkstoneco102.txt")" + echo echo "── C 群:訊息本身 ──────────────────────────────────────────────" clean diff --git a/hooks/tests/fixtures/README.md b/hooks/tests/fixtures/README.md index 5b4bc0f..2c07361 100644 --- a/hooks/tests/fixtures/README.md +++ b/hooks/tests/fixtures/README.md @@ -12,3 +12,21 @@ leo 2026-08-27 要的測資是「今天實際發生的 5 次違規派工」 其餘的覆蓋率改用「一條規則一個最小案例」來補(見 dispatch-format-guard.test.sh B 群)。 **不要用想像出來的例子替補那 4 份**,那會讓測試看起來比實際更綠。 + +--- + +## 第二批:`real-content-*.txt`(inkstone/ISEP#65) + +leo 2026-08-27 又在同一天抓到總管**寫完規則之後又犯了 8 次**(八條線的派工單, +每一條都帶了「這個 session 才知道的事」):`ISEP#60`/`#61`/`#64`、 +`Arcrun#142`/`#144`/`#165`、`arcrun-rag#104`、`InkStoneCo#102`。 + +🔴 **原始的違規派工 prompt 本身一樣拿不回來**(同一個道理:agent 一停就蒸發), +但這 8 次的**內容已經被總管補寫回各自的票**(ISEP#65 body 明講)。 +這裡的 8 份 `real-content-*.txt` 是**從那些票的真實內文摘錄**(一字未改地複製, +只是截取一段,並在最前面加回 `【工單】owner/repo#N` 讓它符合派工單的殼)—— +不是想像出來的例子,是真的存在於 Gitea 上、總管真的寫過的句子,只是原本的位置 +錯了(在 prompt 裡,不在票上)。 + +用途:驗證 dispatch-format-guard.sh 修完 ISEP#65 之後,**這 8 種「票號 + 這個 +session 才知道的事」的形狀全部會被擋下**——不是只驗一個最小案例。 diff --git a/hooks/tests/fixtures/real-content-arcrun142.txt b/hooks/tests/fixtures/real-content-arcrun142.txt new file mode 100644 index 0000000..176f390 --- /dev/null +++ b/hooks/tests/fixtures/real-content-arcrun142.txt @@ -0,0 +1,5 @@ +【工單】inkstone/Arcrun#142 + +實查結論:這兩樣東西(庫索引與全庫摘要)從沒被產生過,不是壞掉,是根本沒做。 +於是「開場就知道有哪些子庫、各裝了什麼」這件事永遠靠現查,查不到就當作沒有。 +相關:inkstone/Arcrun#81、#87、#100(總圖說 0 條關聯,實際 1854 條)。 diff --git a/hooks/tests/fixtures/real-content-arcrun144.txt b/hooks/tests/fixtures/real-content-arcrun144.txt new file mode 100644 index 0000000..6154042 --- /dev/null +++ b/hooks/tests/fixtures/real-content-arcrun144.txt @@ -0,0 +1,5 @@ +【工單】inkstone/Arcrun#144 + +leo 明講「不要現在執行,記下來」,本票只是把它接住,開工前要先確認優先序。 +測試 OK,出現了樹狀圖,但不符合需求——達成的是 mindmap 向右向上分佈, +上下間距很大、展開字很小,用戶要擁有觸控板否則很難閱讀。 diff --git a/hooks/tests/fixtures/real-content-arcrun165.txt b/hooks/tests/fixtures/real-content-arcrun165.txt new file mode 100644 index 0000000..47ab04c --- /dev/null +++ b/hooks/tests/fixtures/real-content-arcrun165.txt @@ -0,0 +1,5 @@ +【工單】inkstone/Arcrun#165 + +不要先修,先查清楚再說——現在連斷在哪都還沒人講得出來。 +已知的現場(總管 2026-08-26 用 MCP 實查,不是讀 code 推的):同一個知識庫 +(youlin/yuga3bse),同一套 MCP 工具,四次查詢分數與筆數都對不上。 diff --git a/hooks/tests/fixtures/real-content-arcrunrag104.txt b/hooks/tests/fixtures/real-content-arcrunrag104.txt new file mode 100644 index 0000000..eaa7898 --- /dev/null +++ b/hooks/tests/fixtures/real-content-arcrunrag104.txt @@ -0,0 +1,5 @@ +【工單】inkstone/arcrun-rag#104 + +現況(總管 2026-08-14 實測,非推測):leo 把 5 個資料夾接上重裝後的 leo21c, +App 顯示你的檔案 12022 共幾份、819 已送上去、11183 排隊中、20 送不上去。 +逐資料夾實數(find 計 .md/.pdf/.txt,已排除 node_modules/.git)。 diff --git a/hooks/tests/fixtures/real-content-inkstoneco102.txt b/hooks/tests/fixtures/real-content-inkstoneco102.txt new file mode 100644 index 0000000..39ec9c6 --- /dev/null +++ b/hooks/tests/fixtures/real-content-inkstoneco102.txt @@ -0,0 +1,5 @@ +【工單】inkstone/InkStoneCo#102 + +背景(同一天稍早,leo 對同一件事說過的重話):昨天要完成 51,你現在還有沒派的, +去看看 milestone 一大堆沒有一個完成,現在整理,不要詐騙,去看清楚哪些完成的。 +Gitea 的百分比只數票的開關,它不知道票裡的東西通了沒。 diff --git a/hooks/tests/fixtures/real-content-isep60.txt b/hooks/tests/fixtures/real-content-isep60.txt new file mode 100644 index 0000000..d8951a9 --- /dev/null +++ b/hooks/tests/fixtures/real-content-isep60.txt @@ -0,0 +1,6 @@ +【工單】inkstone/ISEP#60 + +`subagent-claim-worksheet.sh`(`SubagentStop` 產待驗單)與 `claim-verify-police.sh` +(`Stop` 擋收工)這一套自造的第四套機制整個移除,它守的東西改由 Gitea 原生的三格承接 +(子票相依/tag/指派——見 `inkstone/ISEP#59` 與 PR `#58`)。 +`#issuecomment-4289` 量到同一份待驗單重生三次、同內容兩個檔名。 diff --git a/hooks/tests/fixtures/real-content-isep61.txt b/hooks/tests/fixtures/real-content-isep61.txt new file mode 100644 index 0000000..4156d3c --- /dev/null +++ b/hooks/tests/fixtures/real-content-isep61.txt @@ -0,0 +1,5 @@ +【工單】inkstone/ISEP#61 + +`main-and-prod-push-guard.sh` 的戳記機制已經走進死路:原設計用 +`CLAUDE_CODE_CHILD_SESSION=1` 判斷 subagent,實測結果總管主 session 也是 1, +那個變數不是身分標記,誰都擋。改成戳記(`/tmp/.main-push-ok`,總管手動寫)也失敗。 diff --git a/hooks/tests/fixtures/real-content-isep64.txt b/hooks/tests/fixtures/real-content-isep64.txt new file mode 100644 index 0000000..caf2f22 --- /dev/null +++ b/hooks/tests/fixtures/real-content-isep64.txt @@ -0,0 +1,6 @@ +【工單】inkstone/ISEP#64 + +現況(總管實查,2026-08-27 12:40):open PR 總數 23,總管已關掉 8 個「內容早就在 +main」的空 PR,剩 15 個要分診。已關的 8 個實測 git merge-base --is-ancestor 證明 +HEAD 已是 main 的祖先,diff +0/-0:Arcrun#166/#161/#158/#155/#153、 +arcrun-rag#142/#141/#139。