Files
assist/templates/monitor-page.md
T
jiantw83 311cdc611a feat(wiki): 巡檢目錄頁改成一台機器一個大標題區塊
巡檢目錄頁的版面從 markdown 表格換成「大標題加條列」:一台機器一個大標題
區塊,標題就是那一台機器監控頁的實際頁名,欄位改成標題底下的一層條列。
範本、技能敘述、行為清單與說明文件一併跟上,目錄頁上不再留任何表格。

表格的欄位組合是整頁共用的,表頭跟著建頁那一台機器走,之後每一台只改自己
那一列。欄位一增減,只有跑到新版的機器寫得出新的格數,同一張表就混著兩種
格數,多出來的那一格對不到任何欄名,而目錄頁又沒有整頁改寫的路可以走——
整頁覆蓋等於刪掉別台機器的紀錄。條列一筆一個區塊,欄位各自獨立,加一條只
動到自己那一個區塊。

比對鍵從裸雜湊那一格改成大標題本身,標題寫成監控頁的實際頁名。頁名只由
主機名與登入帳號決定,換主機位址、換專用存取庫或換一種頁名編碼都動不到
它;含網址的那一條連結照樣留著給人點,但不當鍵。呼叫改成拿頁名當鍵,欄號
那個參數只在舊表格頁轉檔時用得到。範本原本用二階標題寫的說明區段全部搬進
引言,否則轉檔後會被當成一筆真紀錄讀進去。

範圍是助理的巡檢目錄頁與 assistant 技能的敘述。
2026-09-02 17:21:12 +08:00

11 KiB
Raw Blame History

助理巡檢 — {主機名}/{登入帳號}

由 jsc-assist 維護。這是監控頁 MONITOR_{HASH}。 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。 目錄頁 MONITOR_CONTENTS 在 JSC_WIKI_REPO_CONTENTS 解出的專用存取庫,和這頁不同庫;那一頁一台機器一個 H2 區塊,只更新自己那一個區塊,別台機器的區塊一個字都不動。

flowchart LR
  A[巡檢一輪] --> B[收攏各項結果]
  B --> C[讀回舊頁]
  C --> D[換掉最新一輪那一塊]
  D --> E[本輪摘要列插到表格最上面,截到 24 列]
  E --> V[link-check.sh 驗這一頁要放的連結]
  V --> F[結束碼 0 才整頁寫回]
  F --> G[wiki-contents.sh upsert 更新目錄頁自己那一個區塊]
  G --> H[最後才寫心跳]

心跳排在最後一步,不能提前。心跳新鮮的意思就是「上一輪跑到這一步了」:這一輪沒寫上來,心跳就不寫,讓它自己過期。那是巡檢在空轉的唯一訊號。

本頁基本資料

建頁時寫一次,之後不再更動。

項目 內容
主機 {主機名}
帳號 {登入帳號}
雜湊來源 {主機名}/{登入帳號}
狀態檔根目錄 $JSC_HOME/assistant/($JSC_HOME 未設定就退回 ~/.jsc)

最新一輪

這一塊每輪整塊換掉,只留最新那一輪的完整內容。再往前的軌跡看下面的摘要表。

七個子節固定都寫;某個來源讀不到,就在那個子節寫明是哪個路徑讀不到,不要整節略過。還沒實作的子節也照寫,寫明「這一輪不做這一項」——空表格會被讀成「查過了,沒問題」。

項目 內容
巡檢時間 {yyyy-MM-dd HH:mm}
觸發方式 {排程、事件、手動 三選一}
本輪判定 {正常、警示、異常 三選一}
本輪項目 {這一輪跑了哪幾項,成功幾項、失敗幾項}
讀不到的來源 {路徑清單,全部讀得到就寫「無」}
警示來源 {警示原因,多個用頓號串;沒有就寫「無」}

心跳與閘門狀態

項目 內容
心跳 {新鮮、過期、不存在 三選一}
上次心跳 {yyyy-MM-dd HH:mm},距這次巡檢 {n} 秒
cli {claude、codex、copilot、antigravity、kiro 五選一}
session {工作階段代號}
pid {數字}。只給要找行程的人參考,不參與判定

這一欄讀到的是上一輪巡檢寫的心跳:心跳由巡檢寫,本輪那一次要等這一頁寫成之後才寫。

心跳的判準只看 ts 距現在有沒有超過門檻,預設 300 秒。不看 pid 存活:五支 CLI 與容器裡的行程互相看不到彼此的 pid。閘門的判定留在 hook,助理只維持心跳。心跳新鮮代表上一輪巡檢跑完了,不代表那一輪各項都成功——那要看這一塊上面的「本輪判定」。

技能與呼叫鏈使用統計

資料出自 $JSC_HOME/usage/skills.jsonl 與 $JSC_HOME/usage/chains.jsonl,由 jsc-log:stats 聚合。

對象 類別 本輪次數 累計次數
{技能名或呼叫鏈} {技能、呼叫鏈 二選一} {n} {n}

執行狀態事件

資料出自 $JSC_HOME/usage/events.jsonl,由 jsc-hooks 的 tools/report-status.sh drain 排空,位移記在 $JSC_HOME/usage/scan-state/events.offset。排空之後緊接著跑一次 rotate。

技能的 start 由 hook 記,end 只能由技能自己在收尾時寫。所以有 start 沒有配對的 end 就是那一輪中止了,那也是這一整套機制唯一分得出中止的訊號。配對以 session 加 name 為鍵:五支 CLI 併發時同一支技能會有好幾個工作階段同時在跑,只比對 name 會讓 A 工作階段的 end 去配掉 B 工作階段的 start。

項目 內容
本輪事件數 {n}
非 ok 事件數 {n}
有 start 沒有 end(開超過 {門檻} 秒,疑似中止) {n}
有 start 沒有 end(未達門檻,還在跑) {n}
事件流輪替 {rotated、not-needed、failed、skipped 四選一}

非 ok 事件明細

status 五種:ok 全部達成、blocked 被閘門或前置條件擋下、failed 做到一半失敗、degraded 做完了但有部分沒達成、aborted 使用者中止或前提不成立而主動停止。這一表只列不是 ok 的那幾筆。

時間 類別 名稱 status 結束碼 detail
{yyyy-MM-ddTHH:mm:ssZ} {skill、hook 二選一} {技能寫 domain:skill,hook 寫腳本檔名加子命令} {blocked、failed、degraded、aborted 四選一} {n} {一行,最多 200 字;沒有就寫「-」}

一筆都沒有就寫「本輪沒有 status 不是 ok 的事件」,不要留空表格。列太多時只列前面幾筆,並寫明總筆數與原始事件檔的路徑。

有 start 沒有配對的 end

名稱 類別 session start 時間 已開著(秒)
{domain:skill} {skill、hook 二選一} {工作階段代號} {yyyy-MM-ddTHH:mm:ssZ} {n}

只列開著超過心跳門檻的那幾筆。未達門檻的多半只是還在跑,另外算一個數字就好。沒配對到的 start 留在 $JSC_HOME/assistant/events-open.tsv 跨輪繼續配對;不跨輪的話,跑超過一個巡檢週期的技能每一輪都會被報成中止,而巡檢週期預設只有兩分鐘。

排空或輪替失敗時,這一節寫明是哪一步失敗、結束碼多少,那一項標成失敗。這一節失敗一律不中止那一輪:回報鏈自己壞掉,不可以把被回報的那一輪也拖下去。drain 回 3 是「沒有新事件」,那是正常狀態,多數輪次本來就沒有新事件。

hook 執行期錯誤

資料出自 jsc-hooks 的 tools/scan-hook-errors.sh 與 tools/scan-logs.sh。助理只記錄與發動 jsc-hooks:repair,不自己改 hook。

發生時間 hook CLI 結束碼 錯誤摘要 已寫 ERROR 頁
{yyyy-MM-dd HH:mm} {腳本檔名} {CLI 代號} {n} {一句摘要} {寫成 [ERROR_{HASH}]({絕對網址}),沒寫就填「否」}

版本落差與重啟閘門

資料出自 version-guard.sh report 與 restart-gate.sh report。

domain 本機版本 應有版本 判定
{domain} {版本字串} {版本字串} {相符、落後、查不到 三選一}
CLI 重啟閘門 升起時間
{CLI 代號} {已升起、未升起 二選一} {yyyy-MM-dd HH:mm 或「-」}

SDLC 階段鎖與工作包鎖現況

資料出自 $JSC_HOME/sessions/{sid}.stage 與 $JSC_HOME/wp/*.pr。只讀狀態,不做判定。

工作階段 階段 存取庫 登記時間
{工作階段代號} {plan、analyze、implement、maintain 四選一} {owner}/{repo} {yyyy-MM-dd HH:mm}
存取庫 工作包 PR 歸屬工作階段
{owner}/{repo} {WP-nn} {PR 連結} {工作階段代號}

待辦簿到期與逾期

資料出自 $JSC_HOME/assistant/tasks/ 底下的每一個檔案,一筆一列。

id 標題 狀態 下次執行 到期 連續失敗 標記
{id} {title} {pending、done、paused 三選一} {next_run} {due 或「-」} {fail_count} {已連續失敗 N 次,或「-」}

fail_count 大於 0 的列,標記欄一律寫「已連續失敗 N 次」,N 照檔案原值抄。待辦簿的項目失敗不會自動暫停,會每輪重試;沒標出來,一個壞掉的項目會一直重試而沒人知道。

待人處理

助理只提醒,不代為執行。這一節列的是本輪要人接手的項目。

項目 來源子節 建議入口
{一句話講完要處理什麼} {上面七個子節之一} {技能名或指令}

近 24 輪摘要

一輪一列,最新的在最上面,超過 24 列就丟掉最舊的那一列。

巡檢時間 本輪判定 各項成敗 待人處理 警示來源
{yyyy-MM-dd HH:mm} {正常、警示、異常 三選一} {成功項數}/{總項數} {待人處理筆數} {警示原因,多個用頓號串;沒有就寫「無」}

「警示來源」那一欄不能省。各項讀取全部成功、但讀到的內容有警示時,判定是警示而成敗欄是滿分,沒有這一欄的話,看的人不知道警示哪來。理由要短,一眼讀完,像「心跳過期」「版本查詢失敗」「重啟閘門未清」「上一輪逾時被接手」「有技能只有 start 沒有 end」。

第三欄的欄名寫「各項成敗」,不寫項數。巡檢項目會增加,欄名寫死數字就要跟著改,而舊頁那些列的欄名不會跟著改,同一張表就會有兩種欄名。

寫入規則

  • 讀不到舊頁就中止,不重組,也不寫入。舊頁讀不回來就沒有摘要表可以接下去。
  • 本頁基本資料原樣保留,一個字都不改。
  • 最新一輪整塊換掉,只留這一輪的完整內容。
  • 本輪的摘要列插到摘要表最上面,舊的列往下移,超過 24 列就丟掉最舊的那一列。
  • 三塊重組成一整頁再整頁寫回。除了這三塊,頁上沒有別的東西。
  • 舊格式的頁(一輪一節疊起來的那種)第一次重組時,基本資料留著,那些節收掉,摘要表從本輪這一列開始,並在回報裡說明。
  • 連結一律寫成 [{文字}]({絕對網址}),網址取 jsc-gitea/tools/gitea.sh wiki-url 印出的那一個,不自己組路徑。wiki 自己那種雙中括號寫法只在同一個 wiki 裡解得開,寫錯不會報錯,畫面上看不出壞掉。
  • 這一頁要放進去的每一個連結,寫入前先交給 jsc-gitea/tools/link-check.sh,結束碼 0 才整頁寫回。有 DEAD 就不寫,把連不到的那幾筆回報給呼叫端;結束碼 3 是 GITEA_HOST 沒設定,補設定再驗,不准跳過;結束碼 7 是金鑰失效,停下來回報金鑰問題,不要當成死連結。驗證一律走 API,不看網頁狀態碼——私有存取庫的網頁網址對未登入請求一律回 404。
  • 整頁寫成之後,才回頭更新目錄頁自己那一個區塊,寫入交給 jsc-gitea/tools/wiki-contents.sh upsert,別台機器的區塊一個字都不動。目錄頁那一個區塊的連結同樣先驗過才寫。
  • 這一頁沒寫成就不寫心跳,讓它過期。心跳代表的是「這一輪的結果記在這一頁上了」。
  • 目錄頁只是索引。目錄頁的存取庫沒設定(結束碼 3)時照樣寫心跳,並把那一筆列進待人處理;其餘寫入失敗才不寫心跳。目錄頁少一個區塊只少一筆索引,這一輪的結果已經在這一頁上。
  • 執行狀態事件那一節的內容由 tools/patrol.sh collect 排空、彙整好,寫頁的人原樣採用,不自己再跑一次 drain。drain 是消耗性讀取:它一讀完就把位移往前推,同一批事件不會再出現第二次,第二次跑只會拿到 3,或者把下一輪的事件提前吃掉。