feat(assistant): 一輪算完就寫提醒佇列
助理算得出哪幾筆到期、哪幾筆逾期,但那些結果只留在監控頁上。這一批讓 每一輪把「要送到人面前」的那幾筆寫成一份佇列檔,位置固定在助理狀態目錄 底下,讀的那一端只印、不判。 判定不放在讀的那一邊,理由是漂移:讓讀的人自己拿 due 欄與 next_run 去跟 現在比,就是第二套到期判定,跟這裡那一套遲早對不上,而對不上的那一天 兩邊都說自己是對的。 這樣換來一個新的失效模式,所以檔頭寫足四個值:輪次代號、UTC 時間戳、 連續失敗筆數、待辦總筆數,再加同一刻的 epoch 秒。助理沒在跑的時候佇列 不會更新,而一份舊佇列讀起來跟新的一模一樣——讀的那一端要算得出它多舊, 「沒有提醒」與「沒有人算提醒」不可以長得一樣。epoch 是為了讓讀的那一端 不必自己解 ISO 字串:那等於在每一個讀取端各放一份日期解析。 同一筆待辦既到期又逾期時只列逾期那一行。同一批提醒裡出現兩行會被讀成 兩件事,而「逾期五天」比「排定點過了」講得更清楚。 逾期那幾行的「逾期多久」直接取判定那一支算好的寫法,不在這裡再寫一個 時間長度格式化——同一個秒數在兩個地方就會印出兩種說法。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,7 +44,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
|
||||
| 檔案 | 用途 |
|
||||
| --- | --- |
|
||||
| `tools/schedule.sh` | 助理系統排程的安裝、移除與查現況。三個子命令 `install`、`remove`、`status`,只裝 `patrol` 這一筆——心跳由巡檢自己寫,`install heartbeat` 一律回 6,舊版遺留的心跳條目由 `install patrol` 順手清掉。巡檢週期由心跳的過期門檻算出來(`2 × 週期 × 60 < 門檻`,再取能整除一小時的分鐘數):門檻 300 秒是每 2 分鐘一輪,門檻 1800 秒是每 12 分鐘一輪。Linux、WSL 與 macOS 走 crontab,Windows 走 schtasks。條目行尾帶固定標記 `# jsc-assist:assistant {工作}`,只動自己那一筆,別人的排程一行都不碰。條目自己把環境帶齊:CLI 用 `command -v` 解成絕對路徑、安裝當下把 `GITEA_HOST`、`GITEA_TOKEN`、`JSC_HOME`、`JSC_ASSISTANT_HEARTBEAT_TTL`、存取庫掃描的 `JSC_ASSIST_SCAN_ROOT` 與 `JSC_ASSIST_SCAN_EXCLUDE`,以及已設定的 `JSC_WIKI_REPO` 系列快照進條目、自帶 `JSC_GITEA_CONFIRM=yes`。`JSC_WIKI_REPO` 系列含內容頁的 `JSC_WIKI_REPO_MONITOR` 與目錄頁的 `JSC_WIKI_REPO_CONTENTS`:監控頁 `MONITOR_{HASH}` 與目錄頁 `MONITOR_CONTENTS` 分屬不同存取庫,兩支變數都要帶。名單是安裝當下從環境撈出所有已設定的,不寫死,所以新增的頁型變數自動涵蓋,這支不必跟著改——cron 的 PATH 很短、不讀設定檔、也沒有 tty。印出條目時金鑰一律遮掉,條目本身含金鑰快照,crontab 檔案要保持只有本人讀得到,變數改過要重跑一次 install。安裝當下把解好的字面根目錄寫進條目的提示文字(`工具根目錄={絕對路徑}`)並印成 `patrol_root=`:那一輪自己解不出根目錄,只能從提示文字拿,拿不到就停下回報;自訂巡檢指令沒帶這一段只警告、不中止。裝完會檢查排程服務在不在跑,沒跑就回 1——WSL 預設不啟動 cron;也會檢查 `$JSC_HOME/current` 那組連結在不在、印出這一輪要開的 allow 規則,連結不在只警告、不代建。`--dry-run` 只印組出來的條目與寫回後的內容,什麼都不動。條目長度兩個模式都量,印成 `entry_len=`:cron 一行有長度上限,超過就整批寫不進去,而那個限制是 `crontab` 自己在寫入那一刻才擋,`--dry-run` 那一路根本不碰它——實測踩過一次,預演全綠、安裝回「command too long」 |
|
||||
| `tools/patrol.sh` | 一輪巡檢的收攏與收口。三個子命令:`collect` 取鎖、讀五項來源、組出監控頁的「最新一輪」與「近 24 輪摘要」兩塊、本輪的摘要列與目錄頁那一個區塊(區塊的 H2 標題是內容頁頁名 `MONITOR_{HASH}`,upsert 拿標題當鍵;「監控頁」那一條是連結,網址留佔位,等監控頁寫成之後由呼叫端用 `gitea.sh wiki-url` 的絕對網址換掉);`finish` 在監控頁寫成之後才寫心跳、換上用量快照、放掉鎖;`abort` 只放掉鎖,不寫心跳。各項來源各自獨立,一項失敗其餘各項照跑,失敗那一項在頁上寫明是「這一項失敗」而不是沒資料。執行狀態事件那一項由 `collect` 自己叫 `jsc-hooks/tools/report-status.sh` 排空再輪替,把非 ok 的事件與「有 start 沒有配對 end」的技能彙整成頁上那一節;`drain` 是消耗性讀取,所以只由這支跑,且它失敗一律不中止那一輪。整輪拿一把目錄鎖,上一輪還在跑就回 4 讓開;鎖逾時(門檻取心跳門檻)會被下一輪搶回來,並在頁上記一筆。`version-guard.sh report` 回「查詢失敗」時照原字抄,不補查、不美化 |
|
||||
| `tools/patrol.sh` | 一輪巡檢的收攏與收口。三個子命令:`collect` 取鎖、讀五項來源、組出監控頁的「最新一輪」與「近 24 輪摘要」兩塊、本輪的摘要列與目錄頁那一個區塊(區塊的 H2 標題是內容頁頁名 `MONITOR_{HASH}`,upsert 拿標題當鍵;「監控頁」那一條是連結,網址留佔位,等監控頁寫成之後由呼叫端用 `gitea.sh wiki-url` 的絕對網址換掉);`finish` 在監控頁寫成之後才寫心跳、換上用量快照、放掉鎖;`abort` 只放掉鎖,不寫心跳。各項來源各自獨立,一項失敗其餘各項照跑,失敗那一項在頁上寫明是「這一項失敗」而不是沒資料。執行狀態事件那一項由 `collect` 自己叫 `jsc-hooks/tools/report-status.sh` 排空再輪替,把非 ok 的事件與「有 start 沒有配對 end」的技能彙整成頁上那一節;`drain` 是消耗性讀取,所以只由這支跑,且它失敗一律不中止那一輪。整輪拿一把目錄鎖,上一輪還在跑就回 4 讓開;鎖逾時(門檻取心跳門檻)會被下一輪搶回來,並在頁上記一筆。`version-guard.sh report` 回「查詢失敗」時照原字抄,不補查、不美化。`collect` 另外把要送到人面前的那幾筆寫成提醒佇列 `$JSC_HOME/assistant/reminders.tsv`:第一行帶輪次與 UTC 時間戳,之後一行一筆,到期的只提醒項與逾期項各一種。判定留在到期判定那一支,佇列只是輸出——讀的那一端只印、不判。時間戳是關鍵:沒有人更新的佇列讀起來跟新的一模一樣 |
|
||||
| `references/behaviors.md` | 本 domain 的技能行為清單:一支技能一節,五列記下觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象,供稽核與驗證比對。格式合約見 `plugins/meta` 的 `references/guidelines.md`「技能行為清單」 |
|
||||
| `templates/monitor-contents.md` | 目錄頁 `MONITOR_CONTENTS` 的範本,這一頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,和監控頁不同庫。版面是 H1、`>` 引言,然後一台機器一個 H2 區塊,欄位在標題底下一行一條 `- {欄位名}:{值}`,頁上不放 markdown 表格。H2 標題就是內容頁頁名 `MONITOR_{HASH}`,雜湊來源是 `{主機名}/{登入帳號}`,主機名取短的那一段。寫入一律走 `jsc-gitea/tools/wiki-contents.sh upsert`,比對鍵是 H2 標題:**只更新自己那一個區塊**,別台機器的區塊原樣保留,禁止整頁覆蓋。「監控頁」那一條的連結一律寫成 `[{頁名}]({絕對網址})`,網址取 `gitea.sh wiki-url` 印的那一個,寫入前先過 `jsc-gitea/tools/link-check.sh`、結束碼 0 才寫;但那一條含主機位址與網址編碼,會變,所以不當鍵 |
|
||||
| `templates/monitor-page.md` | 內容頁 `MONITOR_{HASH}` 的範本。記的是這台機器的巡檢軌跡。頁面固定三塊:本頁基本資料建頁時寫一次就不動、最新一輪每輪整塊換掉、近 24 輪摘要一輪一列且最新的在最上面。軌跡留在摘要表,完整內容只留最新一輪,頁面才讀得完 |
|
||||
|
||||
Reference in New Issue
Block a user