refactor(patrol): 監控頁改成固定三塊
巡檢產出與監控頁範本改成固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。 舊寫法是一輪附加一節。十五分鐘跑一輪,一天就疊出九十六節,那樣的頁沒 有人讀得完,也就沒有人會發現助理壞在哪一輪。軌跡留著卻沒人看,等於沒 有留。 基本資料建頁時寫一次就不動。最新一輪每輪整塊換掉,只留最新那一輪的完 整內容。摘要表一輪一列,最新的在最上面,超過 24 列就丟掉最舊的那一列, 一列只有四欄:巡檢時間、本輪判定、四項成敗、待人處理筆數,一眼看得出 是從哪一輪開始壞的。collect 的輸出鍵位跟著換,section_file 不再產出, 改印 latest_file、summary_file 與 summary_row_file,另外多印本輪待人處 理的筆數。範本連帶把流程圖與寫入規則改成先讀回舊頁、再三塊重組整頁寫 回,並寫明舊格式的頁第一次重組要怎麼收。 順手把腳本內部找別的 domain 工具的順序改成 current 連結優先,理由跟排 程那支一樣:技能與權限規則都以 current 為準,腳本自己去挑另一個版本就 會跑到混版的工具,而那種不一致查起來沒有線索。功能範圍是一輪巡檢的收 攏與監控頁產出。
This commit is contained in:
+33
-18
@@ -1,20 +1,23 @@
|
||||
# 助理巡檢 — {主機名}/{登入帳號}
|
||||
|
||||
> 由 `jsc-assist` 維護。這是監控頁 `MONITOR_{HASH}`。
|
||||
> 這頁是這台機器的巡檢軌跡:一次巡檢附加一節,節標題帶時間戳,舊的節一個字都不動。
|
||||
> 附加是刻意的。助理的寫入是背景行為,覆寫錯了沒人在現場,軌跡被抹掉也看不出斷在哪一輪。
|
||||
> 目錄頁 `MONITOR_CONTENTS` 只更新自己那一列,寫入語意與這頁不同,不要混用。
|
||||
> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。
|
||||
> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。
|
||||
> 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。
|
||||
> 目錄頁 `MONITOR_CONTENTS` 只更新自己那一列,別台機器的列一個字都不動。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A[巡檢一輪] --> B[收攏各類結果]
|
||||
B --> C[附加一節,節標題帶時間戳]
|
||||
C --> D[既有的節原樣保留]
|
||||
D --> E[回頭更新 MONITOR_CONTENTS 自己那一列]
|
||||
E --> F[最後才寫心跳]
|
||||
A[巡檢一輪] --> B[收攏四項結果]
|
||||
B --> C[讀回舊頁]
|
||||
C --> D[換掉最新一輪那一塊]
|
||||
D --> E[本輪摘要列插到表格最上面,截到 24 列]
|
||||
E --> F[整頁寫回]
|
||||
F --> G[更新 MONITOR_CONTENTS 自己那一列]
|
||||
G --> H[最後才寫心跳]
|
||||
```
|
||||
|
||||
心跳排在最後一步,不能提前。心跳新鮮的意思就是「上一輪跑到這一步了」:這一節沒寫上來,心跳就不寫,讓它自己過期。那是巡檢在空轉的唯一訊號。
|
||||
心跳排在最後一步,不能提前。心跳新鮮的意思就是「上一輪跑到這一步了」:這一輪沒寫上來,心跳就不寫,讓它自己過期。那是巡檢在空轉的唯一訊號。
|
||||
|
||||
## 本頁基本資料
|
||||
|
||||
@@ -27,9 +30,11 @@ flowchart LR
|
||||
| 雜湊來源 | `{主機名}/{登入帳號}` |
|
||||
| 狀態檔根目錄 | `$JSC_HOME/assistant/`(`$JSC_HOME` 未設定就退回 `~/.jsc`) |
|
||||
|
||||
## 巡檢 {yyyy-MM-dd HH:mm}
|
||||
## 最新一輪
|
||||
|
||||
一輪巡檢就是這樣一節,最新的一節放在最下面。六個子節固定都寫;某個來源讀不到,就在那個子節寫明是哪個路徑讀不到,不要整節略過。還沒實作的子節也照寫,寫明「這一輪不做這一項」——空表格會被讀成「查過了,沒問題」。
|
||||
這一塊每輪整塊換掉,只留最新那一輪的完整內容。再往前的軌跡看下面的摘要表。
|
||||
|
||||
六個子節固定都寫;某個來源讀不到,就在那個子節寫明是哪個路徑讀不到,不要整節略過。還沒實作的子節也照寫,寫明「這一輪不做這一項」——空表格會被讀成「查過了,沒問題」。
|
||||
|
||||
| 項目 | 內容 |
|
||||
| --- | --- |
|
||||
@@ -49,9 +54,9 @@ flowchart LR
|
||||
| session | {工作階段代號} |
|
||||
| pid | {數字}。只給要找行程的人參考,不參與判定 |
|
||||
|
||||
這一欄讀到的是**上一輪**巡檢寫的心跳:心跳由巡檢寫,本輪那一次要等這一節寫上來之後才寫。
|
||||
這一欄讀到的是**上一輪**巡檢寫的心跳:心跳由巡檢寫,本輪那一次要等這一頁寫成之後才寫。
|
||||
|
||||
心跳的判準只看 `ts` 距現在有沒有超過門檻,預設 300 秒。不看 pid 存活:五支 CLI 與容器裡的行程互相看不到彼此的 pid。閘門的判定留在 hook,助理只維持心跳。心跳新鮮代表上一輪巡檢跑完了,不代表那一輪四項都成功——那要看本節上面的「本輪判定」。
|
||||
心跳的判準只看 `ts` 距現在有沒有超過門檻,預設 300 秒。不看 pid 存活:五支 CLI 與容器裡的行程互相看不到彼此的 pid。閘門的判定留在 hook,助理只維持心跳。心跳新鮮代表上一輪巡檢跑完了,不代表那一輪四項都成功——那要看這一塊上面的「本輪判定」。
|
||||
|
||||
### 技能與呼叫鏈使用統計
|
||||
|
||||
@@ -111,11 +116,21 @@ flowchart LR
|
||||
| --- | --- | --- |
|
||||
| {一句話講完要處理什麼} | {上面六個子節之一} | {技能名或指令} |
|
||||
|
||||
## 近 24 輪摘要
|
||||
|
||||
一輪一列,最新的在最上面,超過 24 列就丟掉最舊的那一列。
|
||||
|
||||
| 巡檢時間 | 本輪判定 | 四項成敗 | 待人處理 |
|
||||
| --- | --- | --- | ---: |
|
||||
| {yyyy-MM-dd HH:mm} | {正常、警示、異常 三選一} | {成功項數}/{總項數} | {待人處理筆數} |
|
||||
|
||||
## 寫入規則
|
||||
|
||||
- 一次巡檢附加一節,節標題帶時間戳,節名不重複。
|
||||
- 既有的節原樣保留,一個字都不改。
|
||||
- 禁止整頁覆寫。覆寫等於把這台機器的巡檢軌跡刪掉。
|
||||
- 讀不到舊內容就中止,不附加,也不寫入。
|
||||
- 附加成功之後,才回頭更新 `MONITOR_CONTENTS` 自己那一列。
|
||||
- 讀不到舊頁就中止,不重組,也不寫入。舊頁讀不回來就沒有摘要表可以接下去。
|
||||
- 本頁基本資料原樣保留,一個字都不改。
|
||||
- 最新一輪整塊換掉,只留這一輪的完整內容。
|
||||
- 本輪的摘要列插到摘要表最上面,舊的列往下移,超過 24 列就丟掉最舊的那一列。
|
||||
- 三塊重組成一整頁再整頁寫回。除了這三塊,頁上沒有別的東西。
|
||||
- 舊格式的頁(一輪一節疊起來的那種)第一次重組時,基本資料留著,那些節收掉,摘要表從本輪這一列開始,並在回報裡說明。
|
||||
- 整頁寫成之後,才回頭更新 `MONITOR_CONTENTS` 自己那一列,別台機器的列一個字都不動。
|
||||
- 兩頁都寫成之後,才寫這一輪的心跳。任一頁沒寫成就不寫心跳,讓它過期。
|
||||
|
||||
+64
-21
@@ -8,7 +8,7 @@
|
||||
#
|
||||
# collect 帶了 --out,finish 與 abort 就要帶同一個目錄,不然換不到本輪的用量快照。
|
||||
#
|
||||
# collect 讀四項來源、組出監控頁要附加的那一節、把鎖拿在手上。
|
||||
# collect 讀四項來源、組出監控頁那三塊、把鎖拿在手上。
|
||||
# finish 在監控頁寫成功之後才呼叫:寫心跳、換上用量快照、放掉鎖。
|
||||
# abort 在監控頁沒寫成時呼叫:只放掉鎖,不寫心跳。
|
||||
#
|
||||
@@ -16,10 +16,10 @@
|
||||
# 0 collect:四項全部讀到底(含「來源在、沒有資料」);finish:心跳寫好、快照換上、
|
||||
# 鎖放掉;abort:鎖放掉,本來就沒鎖也算
|
||||
# 1 collect:部分成功——至少一項失敗,也至少一項有結果。**結果照樣印得出來,呼叫端
|
||||
# 照樣要把這一節寫上監控頁**,只是本輪判定要標成警示
|
||||
# 照樣要把這一輪寫上監控頁**,只是本輪判定要標成警示
|
||||
# 2 finish:找不到 jsc-hooks 的 hooks/heartbeat.sh,心跳沒有東西可寫。collect 不會回這
|
||||
# 一碼——心跳讀不到只是 D-09 這一項失敗,另外三項照跑
|
||||
# 3 collect:四項全部失敗,一項資料都沒有。這一節還是要寫上監控頁,本輪判定標成異常
|
||||
# 3 collect:四項全部失敗,一項資料都沒有。這一輪還是要寫上監控頁,本輪判定標成異常
|
||||
# 4 上一輪還在跑,本輪讓開(collect),或鎖已經不在自己手上(finish、abort)。這不是
|
||||
# 失敗,是刻意讓開:不寫心跳、不寫監控頁,下一輪再來
|
||||
# 5 檔案系統失敗:鎖建不起來或放不掉、暫存檔寫不進去、快照換不上,或 heartbeat.sh write
|
||||
@@ -43,8 +43,7 @@
|
||||
#
|
||||
# 一輪巡檢包含一次 CLI 呼叫與兩次 wiki 寫入,跑過一個排程週期是有可能的。所以整輪拿一把
|
||||
# 鎖:$JSC_HOME/assistant/patrol.lock 是目錄,mkdir 是原子操作,搶不到就是別人在跑。
|
||||
# 搶不到的那一輪回 4 直接讓開,不排隊、不並行——並行的兩輪會在同一頁附加兩節,還會互相
|
||||
# 蓋掉用量快照。
|
||||
# 搶不到的那一輪回 4 直接讓開,不排隊、不並行——並行的兩輪會互相蓋掉監控頁與用量快照。
|
||||
# 鎖會逾時自動搶回來,門檻取心跳門檻(heartbeat.sh report 的 ttl 欄):上一輪跑得比門檻
|
||||
# 還久,它本來就已經維持不住心跳新鮮了,讓新的一輪接手才對。被搶回來的那一輪,finish 會
|
||||
# 拿 --round 比對出鎖不是自己的,回 4 且不寫心跳。
|
||||
@@ -65,6 +64,16 @@
|
||||
# 巡檢照抄第四欄原字,不自己補查遠端版本、不把「查詢失敗」寫成「相符」或「最新」。
|
||||
# 查不到就是沒有證據,寫成別的字等於幫一個既有缺陷蓋章。
|
||||
#
|
||||
# --- 監控頁固定三塊 ---
|
||||
#
|
||||
# 監控頁不再一輪附加一節。15 分鐘一輪,一天就是 96 節,那樣的頁沒有人讀得完,也就沒有人
|
||||
# 會發現壞掉。改成固定三塊:
|
||||
# 本頁基本資料 建頁時寫一次,之後一個字都不動
|
||||
# 最新一輪 每輪整塊換掉,只留最新那一輪的完整內容
|
||||
# 近 24 輪摘要 一輪一列,最新的在最上面,超過 24 列就丟掉最舊的
|
||||
# 軌跡留在摘要表:一輪一列,看得出是從哪一輪開始壞的。完整內容只留最新一輪,因為頁面要能
|
||||
# 讀完才有人讀。
|
||||
#
|
||||
# --- collect 的輸出 ---
|
||||
#
|
||||
# stdout 是 key=value,一行一個鍵,供呼叫端逐行取值。監控頁要用的 markdown 不印在
|
||||
@@ -79,8 +88,11 @@
|
||||
# verdict= 正常、警示、異常
|
||||
# failed_sources= 讀不到的來源路徑,以「、」分隔;全部讀得到就是「無」
|
||||
# tasks_total= tasks_failing= 待辦簿筆數與連續失敗筆數,只供目錄頁那一列用
|
||||
# section_file= 要附加到 MONITOR_{HASH} 的那一節
|
||||
# newpage_file= MONITOR_{HASH} 不存在時要建的整頁內容
|
||||
# pending= 本輪待人處理的筆數
|
||||
# latest_file= 「最新一輪」那一塊,整塊換掉舊頁同名那一塊
|
||||
# summary_file= 「近 24 輪摘要」那一塊,表格裡先放本輪這一列,舊頁的資料列接在下面
|
||||
# summary_row_file= 只有本輪那一列,方便直接插到既有表格最上面
|
||||
# newpage_file= MONITOR_{HASH} 不存在時要建的整頁內容,三塊都已經排好
|
||||
# contents_file= MONITOR_CONTENTS 那一列的欄位值
|
||||
#
|
||||
# 環境變數:
|
||||
@@ -95,6 +107,7 @@ set -u
|
||||
|
||||
JSC_HOME="${JSC_HOME:-$HOME/.jsc}"
|
||||
STATE_DIR="$JSC_HOME/assistant"
|
||||
CURRENT="$JSC_HOME/current"
|
||||
LOCK="$STATE_DIR/patrol.lock"
|
||||
PREV_SNAP="$STATE_DIR/usage-prev.tsv"
|
||||
RD="$STATE_DIR/patrol"
|
||||
@@ -126,12 +139,17 @@ die() { # $1=結束碼 $2=訊息
|
||||
}
|
||||
|
||||
# 找一支別的 domain 的腳本。搜尋順序比照 schedule.sh 的 heartbeat_sh():先環境變數覆寫,
|
||||
# 再開發用的並排存取庫版面,最後已安裝的 plugin 快取版面。
|
||||
# 再 $JSC_HOME/current 那一組連結,然後開發用的並排存取庫版面,最後已安裝的 plugin 快取
|
||||
# 版面。current 排在快取前面是刻意的:技能與權限規則都以 current 為準,腳本內部再自己去挑
|
||||
# 另一個版本,同一輪就會跑到混版的工具,而那種不一致查起來沒有任何線索。
|
||||
find_tool() { # $1=domain 短名 $2=domain 內相對路徑 $3=環境變數覆寫值(可為空)
|
||||
if [ -n "$3" ]; then
|
||||
[ -f "$3" ] && { printf '%s\n' "$3"; return 0; }
|
||||
return 1
|
||||
fi
|
||||
for _c in "$CURRENT/jsc-$1/$2" "$CURRENT/$1/$2"; do
|
||||
[ -f "$_c" ] && { printf '%s\n' "$_c"; return 0; }
|
||||
done
|
||||
_root="${CLAUDE_PLUGIN_ROOT:-$SCRIPT_DIR/..}"
|
||||
for _c in "$_root/../$1/$2" "$_root/../jsc-$1/$2"; do
|
||||
[ -f "$_c" ] && { printf '%s\n' "$_c"; return 0; }
|
||||
@@ -519,7 +537,7 @@ d09() {
|
||||
printf '| pid | %s。只給要找行程的人參考,不參與判定 |\n' "$(cell "${_pid:--}")"
|
||||
printf '| 心跳檔 | `%s` |\n' "$(cell "${_file:--}")"
|
||||
printf '\n心跳的判準只看 `ts` 距現在有沒有超過門檻,不看 pid 存活:五支 CLI 與容器裡的行程互相看不到彼此的 pid。閘門的判定留在 hook,助理只維持心跳,不參與判定。\n'
|
||||
printf '\n心跳新鮮代表上一輪巡檢跑完了,而且結果記上監控頁了。它不代表那一輪四項都成功——四項的成敗看本節上面的「本輪判定」。\n'
|
||||
printf '\n心跳新鮮代表上一輪巡檢跑完了,而且結果記上監控頁了。它不代表那一輪四項都成功——四項的成敗看這一塊上面的「本輪判定」。\n'
|
||||
} >>"$RD/d09.md"
|
||||
|
||||
case "$_st" in
|
||||
@@ -546,7 +564,7 @@ count_tasks() {
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- 組出監控頁那一節 ---
|
||||
# --- 組出監控頁那三塊 ---
|
||||
|
||||
tally() { # $1=項目狀態
|
||||
case "$1" in
|
||||
@@ -562,8 +580,12 @@ compose() {
|
||||
[ "$OK_COUNT" -eq 0 ] && _v='異常'
|
||||
VERDICT="$_v"
|
||||
|
||||
ITEM_TOTAL=$(( OK_COUNT + FAIL_COUNT ))
|
||||
PEND_COUNT=$(grep -c '^| ' "$RD/pend.md" 2>/dev/null); [ -n "$PEND_COUNT" ] || PEND_COUNT=0
|
||||
|
||||
{
|
||||
printf '## 巡檢 %s\n\n' "$AT"
|
||||
printf '## 最新一輪\n\n'
|
||||
printf '這一塊每輪整塊換掉,只留最新那一輪的完整內容。再往前的軌跡看下面的摘要表。\n\n'
|
||||
printf '| 項目 | 內容 |\n'
|
||||
printf '| --- | --- |\n'
|
||||
printf '| 巡檢時間 | %s |\n' "$AT"
|
||||
@@ -589,21 +611,38 @@ compose() {
|
||||
printf '| 項目 | 來源子節 | 建議入口 |\n'
|
||||
printf '| --- | --- | --- |\n'
|
||||
if [ -s "$RD/pend.md" ]; then cat "$RD/pend.md"; else printf '| (無) | - | - |\n'; fi
|
||||
} >"$RD/section.md"
|
||||
} >"$RD/latest.md"
|
||||
|
||||
# 摘要表的那一列。欄位刻意只有四個:時間、判定、四項成敗、待人處理筆數——一列要能一眼
|
||||
# 看完,才看得出是從哪一輪開始壞的。
|
||||
printf '| %s | %s | %s/%s | %s |\n' \
|
||||
"$AT" "$VERDICT" "$OK_COUNT" "$ITEM_TOTAL" "$PEND_COUNT" >"$RD/summary-row.md"
|
||||
|
||||
# 摘要那一塊:標題、表頭,加上本輪這一列。呼叫端把舊頁的資料列接在這一列下面,截到 24 列。
|
||||
{
|
||||
printf '## 近 24 輪摘要\n\n'
|
||||
printf '一輪一列,最新的在最上面,超過 24 列就丟掉最舊的那一列。\n\n'
|
||||
printf '| 巡檢時間 | 本輪判定 | 四項成敗 | 待人處理 |\n'
|
||||
printf '| --- | --- | --- | ---: |\n'
|
||||
cat "$RD/summary-row.md"
|
||||
} >"$RD/summary.md"
|
||||
|
||||
# 監控頁不存在時要建的整頁內容。基本資料建頁時寫一次,之後不再更動。
|
||||
{
|
||||
printf '# 助理巡檢 — %s/%s\n\n' "$HOST" "$USER_NAME"
|
||||
printf '> 由 `jsc-assist` 維護。這是監控頁 `%s`。\n' "$PAGE"
|
||||
printf '> 這頁是這台機器的巡檢軌跡:一次巡檢附加一節,節標題帶時間戳,舊的節一個字都不動。\n'
|
||||
printf '> 附加是刻意的。助理的寫入是背景行為,覆寫錯了沒人在現場,軌跡被抹掉也看不出斷在哪一輪。\n'
|
||||
printf '> 目錄頁 `MONITOR_CONTENTS` 只更新自己那一列,寫入語意與這頁不同,不要混用。\n\n'
|
||||
printf '> 這頁固定三塊:本頁基本資料、最新一輪、近 24 輪摘要。\n'
|
||||
printf '> 最新一輪每輪整塊換掉;摘要表一輪一列往上疊,只留 24 列;基本資料建頁時寫一次就不動。\n'
|
||||
printf '> 完整內容只留最新一輪,頁面才讀得完;軌跡留在摘要表,看得出是從哪一輪開始壞的。\n'
|
||||
printf '> 目錄頁 `MONITOR_CONTENTS` 只更新自己那一列,別台機器的列一個字都不動。\n\n'
|
||||
printf '```mermaid\nflowchart LR\n'
|
||||
printf ' A[巡檢一輪] --> B[收攏四項結果]\n'
|
||||
printf ' B --> C[附加一節,節標題帶時間戳]\n'
|
||||
printf ' C --> D[既有的節原樣保留]\n'
|
||||
printf ' D --> E[回頭更新 MONITOR_CONTENTS 自己那一列]\n'
|
||||
printf ' E --> F[最後才寫心跳]\n'
|
||||
printf ' B --> C[讀回舊頁]\n'
|
||||
printf ' C --> D[換掉最新一輪那一塊]\n'
|
||||
printf ' D --> E[本輪摘要列插到表格最上面,截到 24 列]\n'
|
||||
printf ' E --> F[整頁寫回]\n'
|
||||
printf ' F --> G[更新 MONITOR_CONTENTS 自己那一列]\n'
|
||||
printf ' G --> H[最後才寫心跳]\n'
|
||||
printf '```\n\n'
|
||||
printf '## 本頁基本資料\n\n'
|
||||
printf '建頁時寫一次,之後不再更動。\n\n'
|
||||
@@ -613,7 +652,8 @@ compose() {
|
||||
printf '| 帳號 | %s |\n' "$(cell "$USER_NAME")"
|
||||
printf '| 雜湊來源 | `%s/%s` |\n' "$(cell "$HOST")" "$(cell "$USER_NAME")"
|
||||
printf '| 狀態檔根目錄 | `$JSC_HOME/assistant/`(`$JSC_HOME` 未設定就退回 `~/.jsc`) |\n\n'
|
||||
cat "$RD/section.md"
|
||||
cat "$RD/latest.md"; printf '\n'
|
||||
cat "$RD/summary.md"
|
||||
} >"$RD/newpage.md"
|
||||
|
||||
{
|
||||
@@ -695,7 +735,10 @@ case "$CMD" in
|
||||
printf 'failed_sources=%s\n' "${FAILED_SOURCES:-無}"
|
||||
printf 'tasks_total=%s\n' "$TASKS_TOTAL"
|
||||
printf 'tasks_failing=%s\n' "$TASKS_FAILING"
|
||||
printf 'section_file=%s\n' "$RD/section.md"
|
||||
printf 'pending=%s\n' "$PEND_COUNT"
|
||||
printf 'latest_file=%s\n' "$RD/latest.md"
|
||||
printf 'summary_file=%s\n' "$RD/summary.md"
|
||||
printf 'summary_row_file=%s\n' "$RD/summary-row.md"
|
||||
printf 'newpage_file=%s\n' "$RD/newpage.md"
|
||||
printf 'contents_file=%s\n' "$RD/contents.tsv"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user