每一則回覆都自己說出拖了多久(inkstone/ISEP#63) #94

Merged
claude-code merged 1 commits from feat/countdown-stamp into main 2026-08-28 00:10:10 +00:00
Member

票:inkstone/ISEP#63 → comment 5106

要達成什麼

總管每一則回覆都帶著「這件事已經花了多久」,而且不需要總管記得。
判準:一個新開的 session,什麼都沒被交代,回覆裡照樣有時長。

兩半,缺一不可

票上點名了失效模式:「注入了但模型沒照做是這類機制的主要失效模式」。
所以 hooks/countdown-guard.sh 一支同時掛兩個事件:

事件 做什麼
UserPromptSubmit 把算好的那一行送到眼前(模型不必自己算、也算不準)
Stop 這一回合的回覆裡到底有沒有那一行,沒有就擋一次,並把算好的那一行印出來給它抄

只有前半 = 又一個會被忽略的提醒(本 repo 心法第 2 條明令不要做)。
只有後半 = 罰它做一件拿不到資料的事。

🔴 判準不是關鍵字黑名單

查的不是「有沒有講某些不該講的話」,而是「那個被要求的輸出元素在不在」
一個機器產生的標記(),值是機器算的。

  • 換講法照樣要帶標記 ⇒ 沒有「換個措辭就閃過」的空間
  • 多寫什麼都不會觸發 ⇒ 沒有「講到某個詞就被咬」的誤攔

這是 whitelist-of-one:要求一個東西在場,不是猜哪些東西不該在場

時長從哪一刻算起(票要我自己判斷並說明理由)

這段對話的第一則訊息。 理由:

  • leo 的「拖」指向任務,但「任務」在機器上沒有起點——沒有任何欄位記著「我什麼時候接手這條線」
  • 可機械取得、又真的對應一個交付的起點只有一個:對話的開頭
  • 而那正好就是交貨單位(CLAUDE.md 規則三點七「一段對話 = 一個 release」)
  • ⇒ 已過時間 = 這個 release 已經燒掉多久。這個數字不需要任何人維護,也不會說謊

resume 取「transcript 第一列的時間」而不是「hook 第一次被叫到的時間」:接關不是重新開始。

第二個數字=今天的收工線(台北 16:00)。過線後四小時內顯示「已超過」,
台北 20:00 之後才滾到隔天——超時消失的話,這個東西的意義就沒了

第三個數字=主線 milestone 期限(可選)。hooks/lib/countdown.py 從不打網路
UserPromptSubmit 走在每一則訊息的關鍵路徑上,在那裡打 HTTP = 每句話都先等一次網路。
快取由 SessionStart 的 scripts/countdown-milestone-refresh.sh 更新一次(不是輪詢)。
拿不到就整段不顯示,不編數字

連帶修好票上的兩個卡點(prod-write-guard.sh

卡點一:發一則 Telegram 給 leo 一定是帶 body 的寫入型請求打到那台實例
⇒ 在閘眼裡跟「部署一個工作流上線」長得一模一樣。
這不是誤攔,是解析度不夠——判準改成看打的是哪一個 named webhook

…/webhooks/named/<ns>/notify_leo/trigger        發一則訊息,什麼都沒改   → 放行
…/webhooks/named(無名字、無 /trigger)          部署工作流定義           → 照擋
…/webhooks/named/<ns>/ship_refresh_cdn/trigger  真的動線上狀態           → 照擋

放行範圍刻意只有一個名字,不放行整個家族(那會把部署一起放掉)。

卡點二:連「把這個卡點寫進票裡」都被同一支閘擋(同款第八次)——內文引用了判準關鍵字。
改成剝掉內文再判(沿用另外三支閘已經在用的同一支 lib),起始行保留
所以真的在部署的寫法仍然整條看得到、照樣擋。

📌 修這支的過程又撞了一次同款:連「把修法寫進檔案」的那條指令都被擋,
因為它的內文引用了主機名。那就是這一格最好的證據。

測試(照 docs/TESTING.md 的形狀,A14/A15)

bash hooks/tests/countdown-guard.test.sh                                  → 20/20
bash hooks/tests/prod-write-guard.test.sh hooks/prod-write-guard.sh        → 37/37(原 29)

全程離線:時鐘用 ISEP_COUNTDOWN_NOW 定住、狀態走 ISEP_COUNTDOWN_STATE_DIR
不打網路、不碰 $HOME、不留測試檔。

20 條裡 7 條是「不該擋」(誤攔比漏擋更該修):純工具回合/子 session/
已提醒過一次/transcript 讀不到或壞掉/這一回合戴了。

實測:期望值 vs 實際顯示

起點 06:28 台北、現在 08:03 台北
期望:已過 1 小時 35 分
實得:⏱ 已過 1 小時 35 分|距收工線(台北 16:00)剩 7 小時 56 分

🔴 跑測試前確認 CLAUDE_CODE_CHILD_SESSION 沒有殘留:本閘刻意放行子 session,
在一條 subagent 裡跑,B 群會全綠而且是假綠。第一次跑就撞到,測試檔已自己清掉。

沒驗的那一格(誠實標記)

A8「新 session 閘真的會觸發」沒有跑——這條 subagent 開不了新的 Claude Code session。
UserPromptSubmit 是這個 repo 第一次用的事件,harness 會不會真的去叫它,只有在新 session 裡才看得出來
合併後請在新 session 驗一眼:第一則回覆有沒有帶

版本

plugin.json 0.9.0 → 0.10.0(版本沒動=沒有人吃得到)。
盤點數字是併之前當場數出來的:52 支檔、67 條註冊(不是從上一版加減推的)。
合併時要跟著打 v0.10.0 的 tag,否則 check-version-consistency.sh 會紅。

票:`inkstone/ISEP#63` → comment 5106 ## 要達成什麼 **總管每一則回覆都帶著「這件事已經花了多久」,而且不需要總管記得。** 判準:一個新開的 session,什麼都沒被交代,回覆裡照樣有時長。 ## 兩半,缺一不可 票上點名了失效模式:「**注入了但模型沒照做**是這類機制的主要失效模式」。 所以 `hooks/countdown-guard.sh` 一支同時掛兩個事件: | 事件 | 做什麼 | |---|---| | `UserPromptSubmit` | 把算好的那一行送到眼前(模型不必自己算、也算不準) | | `Stop` | 這一回合的回覆裡到底有沒有那一行,沒有就擋一次,並把算好的那一行印出來給它抄 | 只有前半 = 又一個會被忽略的提醒(本 repo 心法第 2 條明令不要做)。 只有後半 = 罰它做一件拿不到資料的事。 ## 🔴 判準不是關鍵字黑名單 查的**不是「有沒有講某些不該講的話」,而是「那個被要求的輸出元素在不在」**: 一個機器產生的標記(`⏱`),值是機器算的。 - 換講法照樣要帶標記 ⇒ 沒有「換個措辭就閃過」的空間 - 多寫什麼都不會觸發 ⇒ 沒有「講到某個詞就被咬」的誤攔 這是 whitelist-of-one:**要求一個東西在場**,不是**猜哪些東西不該在場**。 ## 時長從哪一刻算起(票要我自己判斷並說明理由) **這段對話的第一則訊息。** 理由: - leo 的「拖」指向**任務**,但「任務」在機器上沒有起點——沒有任何欄位記著「我什麼時候接手這條線」 - 可機械取得、又真的對應一個交付的起點只有一個:對話的開頭 - 而那正好就是交貨單位(CLAUDE.md 規則三點七「**一段對話 = 一個 release**」) - ⇒ 已過時間 = 這個 release 已經燒掉多久。**這個數字不需要任何人維護,也不會說謊** resume 取「transcript 第一列的時間」而不是「hook 第一次被叫到的時間」:接關不是重新開始。 第二個數字=**今天的收工線(台北 16:00)**。過線後四小時內顯示「已超過」, 台北 20:00 之後才滾到隔天——**超時消失的話,這個東西的意義就沒了**。 第三個數字=**主線 milestone 期限**(可選)。`hooks/lib/countdown.py` **從不打網路**: `UserPromptSubmit` 走在每一則訊息的關鍵路徑上,在那裡打 HTTP = 每句話都先等一次網路。 快取由 SessionStart 的 `scripts/countdown-milestone-refresh.sh` 更新一次(不是輪詢)。 拿不到就整段不顯示,**不編數字**。 ## 連帶修好票上的兩個卡點(`prod-write-guard.sh`) **卡點一**:發一則 Telegram 給 leo 一定是帶 body 的寫入型請求打到那台實例 ⇒ 在閘眼裡跟「部署一個工作流上線」長得一模一樣。 這不是誤攔,是**解析度不夠**——判準改成看**打的是哪一個 named webhook**: ``` …/webhooks/named/<ns>/notify_leo/trigger 發一則訊息,什麼都沒改 → 放行 …/webhooks/named(無名字、無 /trigger) 部署工作流定義 → 照擋 …/webhooks/named/<ns>/ship_refresh_cdn/trigger 真的動線上狀態 → 照擋 ``` 放行範圍**刻意只有一個名字**,不放行整個家族(那會把部署一起放掉)。 **卡點二**:連「把這個卡點寫進票裡」都被同一支閘擋(同款第八次)——內文引用了判準關鍵字。 改成剝掉內文再判(沿用另外三支閘已經在用的同一支 lib),**起始行保留**, 所以真的在部署的寫法仍然整條看得到、照樣擋。 > 📌 修這支的過程**又撞了一次同款**:連「把修法寫進檔案」的那條指令都被擋, > 因為它的內文引用了主機名。那就是這一格最好的證據。 ## 測試(照 `docs/TESTING.md` 的形狀,A14/A15) ``` bash hooks/tests/countdown-guard.test.sh → 20/20 bash hooks/tests/prod-write-guard.test.sh hooks/prod-write-guard.sh → 37/37(原 29) ``` 全程離線:時鐘用 `ISEP_COUNTDOWN_NOW` 定住、狀態走 `ISEP_COUNTDOWN_STATE_DIR`, 不打網路、不碰 `$HOME`、不留測試檔。 20 條裡 **7 條是「不該擋」**(誤攔比漏擋更該修):純工具回合/子 session/ 已提醒過一次/transcript 讀不到或壞掉/這一回合戴了。 ### 實測:期望值 vs 實際顯示 ``` 起點 06:28 台北、現在 08:03 台北 期望:已過 1 小時 35 分 實得:⏱ 已過 1 小時 35 分|距收工線(台北 16:00)剩 7 小時 56 分 ``` 🔴 **跑測試前確認 `CLAUDE_CODE_CHILD_SESSION` 沒有殘留**:本閘刻意放行子 session, 在一條 subagent 裡跑,B 群會**全綠而且是假綠**。第一次跑就撞到,測試檔已自己清掉。 ## 沒驗的那一格(誠實標記) **A8「新 session 閘真的會觸發」沒有跑**——這條 subagent 開不了新的 Claude Code session。 `UserPromptSubmit` 是這個 repo 第一次用的事件,**harness 會不會真的去叫它,只有在新 session 裡才看得出來**。 合併後請在新 session 驗一眼:第一則回覆有沒有帶 `⏱`。 ## 版本 `plugin.json` 0.9.0 → **0.10.0**(版本沒動=沒有人吃得到)。 盤點數字是併之前當場數出來的:**52 支檔、67 條註冊**(不是從上一版加減推的)。 合併時要跟著打 `v0.10.0` 的 tag,否則 `check-version-consistency.sh` 會紅。
claude-code added 1 commit 2026-08-28 00:04:27 +00:00
leo 2026-08-27:「前面說過每個回覆要戴上已經花了總時長,這為什麼沒出現?」
              「這應該寫在 ISEP,隨時看自己拖了多久」
重點在後面那句:不是要總管記得戴,是要它長在機器上。
總管當時答「我沒做,現在開始戴」——而那正是這條規則第一次失效的方式。

一支閘掛兩個事件,是同一件事的兩半(票上點名的失效模式就在這裡):
  UserPromptSubmit → 注入算好的那一行(模型不必自己算,也算不準)
  Stop            → 查核這一回合的回覆裡到底有沒有那一行,沒有就擋一次
只做前半=又一個會被忽略的提醒;只做後半=罰它做一件拿不到資料的事。

判準不是關鍵字黑名單,是「那個被要求的輸出元素在不在」——
whitelist-of-one:要求一個機器產生的標記在場,不是猜哪些字不該在場。
換講法照樣要帶標記,多寫什麼都不會觸發。

時長從這段對話的第一則訊息算起,理由寫在 hooks/lib/countdown.py 檔頭:
「任務」在機器上沒有起點,而 CLAUDE.md 規則三點七「一段對話=一個 release」
剛好讓對話起點就是這個交付的起點——這個數字沒有人要維護,也不會說謊。
resume 取較早的那個:接關不是重新開始。

連帶修好票上的兩個卡點(prod-write-guard):
  卡點一 發一則 Telegram 跟部署工作流在閘眼裡一模一樣 ⇒ 判準改看打的是哪一個
        named webhook(路徑形狀+名字),放行範圍只有 notify_leo 一個名字
  卡點二 連「把卡點寫進票裡」都被同一支閘擋(同款第八次)⇒ 剝掉內文再判,
        起始行保留,所以真的在部署的寫法照樣擋
        (修這支的過程又撞了一次同款——那就是這一格最好的證據)

測試:countdown 20/20、prod-write-guard 29→37/37,全程離線
     (時鐘定住、狀態走環境變數、不打網路)。
實測:起點 06:28 台北、現在 08:03 ⇒ 期望「已過 1 小時 35 分」,實得同一字串。

plugin.json 0.9.0 → 0.10.0(版本沒動=沒有人吃得到)。

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
claude-code merged commit a9e30fc6e7 into main 2026-08-28 00:10:10 +00:00
Sign in to join this conversation.
No Reviewers
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: inkstone/ISEP#94