fix(assistant): 讓執行路徑走錯與警示原因都看得見

兩支腳本開跑就檢查自己的執行路徑,監控頁的摘要表與最新一輪那一塊補上
「警示來源」。

兩件都是實測之後補的。用全新的 CLI 行程跑,代理人會把工具路徑解回外掛
快取那一份,不是指定的工作樹那一份。原本的對策只在技能說明書裡寫明禁
止,那是文件約束,擋不住真的走錯的那一輪。權限閘門只放行 current 那一
組確切路徑,走錯就被靜靜擋掉,那一輪不寫心跳,外面只看得到心跳過期。
另一頭,沙箱跑出來的摘要列是「警示、四項全過、待人處理 0」,讀的人看
不出警示哪來。查過不是缺陷:四項來源都讀得到,是讀到的內容有警示。摘
要表的用途本來就是一眼看出從哪一輪開始壞,少了原因那一欄就做不到。

巡檢與排程兩支腳本開跑時先比對自己是不是從 current 底下被叫起來的,不
是就往 stderr 印一行警告,點名實際路徑、應該用的路徑與理由。刻意只警告
不中止:從工作樹直接跑腳本是開發時的正當用法,中止會把那條路擋掉,真
正的失敗本來就發生在權限閘門那裡。摘要表從四欄加到五欄,最新一輪那一
塊也補一列,值取每一處設警示時收下來的短理由,多個用頓號串,沒有就寫
「無」。腳本裡每一處把判定改成警示的地方,一律改走同一支函式順手收下
理由,collect 的輸出多印一行 warn_sources。技能主文、行為清單與監控頁
範本跟著寫明這兩件事。

功能範圍是助理的一輪巡檢與系統排程安裝。
This commit is contained in:
2026-09-01 18:54:05 +08:00
parent 2af9dda7a6
commit 02bfc70f4e
5 changed files with 74 additions and 21 deletions
+2 -2
View File
@@ -7,7 +7,7 @@
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 要啟動助理、要停止助理、要跑一輪巡檢,或要問助理現在還在不在跑、待辦簿剩下哪幾筆時用。四個操作 `start`、`status`、`patrol`、`stop` 都走這一支。排程每一輪叫起來的也是這一支的 `patrol`。執行環境健檢不走這支,走 `jsc-cli:doctor`。技能使用次數不走這支,走 `jsc-log:stats` |
| 關鍵步驟 | 先認出使用者要的是哪一個操作,`patrol` 那一路全程不問人。`start`:先照 `patrol` 的每一步跑完一輪巡檢,第一次心跳由那一輪寫、不另外寫、跑不完就不算啟動、跑 `heartbeat.sh report` 確認 `state=fresh`、跑 `tools/schedule.sh install patrol` 裝巡檢那一筆排程、把它印的 `allow_rule=` 每一行、環境快照提醒與 `current` 連結缺漏的警告原樣轉給人、依結束碼選一段收尾訊息印出——排程接上、排程寫進去了但 cron 沒在跑、排程沒接上三種各一段。心跳那一筆不裝了,`install heartbeat` 一律回 6。`patrol`:跑 `tools/patrol.sh collect` 取鎖並讀四項來源、結束碼 4 就讓開不寫任何東西、結束碼 1 與 3 照樣把這一輪寫上監控頁、`hash` 是空的就 `abort`、經 `jsc-gitea:wiki` 讀回 `MONITOR_{HASH}` 舊頁、基本資料原樣留著、最新一輪那一塊整塊換成 `latest_file`、`summary_file` 的本輪那一列擺最上面、舊的資料列接在下面並截到 24 列、三塊重組成整頁寫回、頁不存在(唯有結束碼 4)才用 `newpage_file` 建頁、讀不回舊頁就不寫、把 `contents_file` 的 `row` 更新到 `MONITOR_CONTENTS` 自己那一列、兩次寫入任一失敗就 `abort` 且不寫心跳、全部寫成才跑 `tools/patrol.sh finish` 寫心跳、最後印出四項結果與待人處理列。`status`:跑 `heartbeat.sh report` 取心跳現況、把 `state` 對映成新鮮、過期、心跳檔損壞、不存在、不自己解析心跳檔也不自己判定、從 `file=` 解出助理目錄後列出 `tasks/` 底下每一個檔案並解析 `state`、`title`、`next_run`、`fail_count`、跑 `tools/schedule.sh status` 取排程現況與週期、印成心跳、排程、待辦三塊、`fail_count` 大於 0 的列標上「已連續失敗 N 次」、心跳與排程兜起來會誤讀的四種組合各補一句話。`stop`:先跑 `heartbeat.sh report` 留下原本的狀態、再跑 `tools/schedule.sh remove all` 移除排程與舊版遺留的心跳條目、最後才跑 `heartbeat.sh clear` 清掉心跳、印出停止訊息並說明心跳清掉之後閘門會擋人、同時說明閘門還沒接線所以現在擋不到人 |
| 關鍵步驟 | 先認出使用者要的是哪一個操作,`patrol` 那一路全程不問人。`start`:先照 `patrol` 的每一步跑完一輪巡檢,第一次心跳由那一輪寫、不另外寫、跑不完就不算啟動、跑 `heartbeat.sh report` 確認 `state=fresh`、跑 `tools/schedule.sh install patrol` 裝巡檢那一筆排程、把它印的 `allow_rule=` 每一行、環境快照提醒與 `current` 連結缺漏的警告原樣轉給人、依結束碼選一段收尾訊息印出——排程接上、排程寫進去了但 cron 沒在跑、排程沒接上三種各一段。心跳那一筆不裝了,`install heartbeat` 一律回 6。`patrol`:跑 `tools/patrol.sh collect` 取鎖並讀四項來源、結束碼 4 就讓開不寫任何東西、結束碼 1 與 3 照樣把這一輪寫上監控頁、`hash` 是空的就 `abort`、經 `jsc-gitea:wiki` 讀回 `MONITOR_{HASH}` 舊頁、基本資料原樣留著、最新一輪那一塊整塊換成 `latest_file`、`summary_file` 的本輪那一列擺最上面(五欄:巡檢時間、本輪判定、四項成敗、待人處理、警示來源)、舊的資料列接在下面並截到 24 列、三塊重組成整頁寫回、頁不存在(唯有結束碼 4)才用 `newpage_file` 建頁、讀不回舊頁就不寫、把 `contents_file` 的 `row` 更新到 `MONITOR_CONTENTS` 自己那一列、兩次寫入任一失敗就 `abort` 且不寫心跳、全部寫成才跑 `tools/patrol.sh finish` 寫心跳、最後印出四項結果、判成警示時的警示來源與待人處理列。`status`:跑 `heartbeat.sh report` 取心跳現況、把 `state` 對映成新鮮、過期、心跳檔損壞、不存在、不自己解析心跳檔也不自己判定、從 `file=` 解出助理目錄後列出 `tasks/` 底下每一個檔案並解析 `state`、`title`、`next_run`、`fail_count`、跑 `tools/schedule.sh status` 取排程現況與週期、印成心跳、排程、待辦三塊、`fail_count` 大於 0 的列標上「已連續失敗 N 次」、心跳與排程兜起來會誤讀的四種組合各補一句話。`stop`:先跑 `heartbeat.sh report` 留下原本的狀態、再跑 `tools/schedule.sh remove all` 移除排程與舊版遺留的心跳條目、最後才跑 `heartbeat.sh clear` 清掉心跳、印出停止訊息並說明心跳清掉之後閘門會擋人、同時說明閘門還沒接線所以現在擋不到人 |
| 外部呼叫 | 工具一律走 `$JSC_HOME/current/{外掛名}` 那一組不帶版本的路徑:`current/jsc-assist/tools/patrol.sh`、`current/jsc-assist/tools/schedule.sh`、`current/jsc-hooks/hooks/heartbeat.sh`,wiki 那一支是 `current/jsc-gitea/tools/gitea.sh`,`$JSC_HOME` 沒設就退回 `~/.jsc`;不拿技能提示給的快取基底目錄組工具路徑——權限只放行 current 那一組,用錯路徑會被靜靜擋掉。`jsc-hooks/hooks/heartbeat.sh` 的 `write`、`report`、`clear` 三個子命令,六個結束碼各有處置:0 往下走、1 與 3 印「助理未運行」、2 回報判不出狀態並停下、4 當成不新鮮並回報心跳檔損壞、5 是嚴重狀況要吵出來且不得回報成功、6 是呼叫寫錯要更正後重跑。`write` 只由 `tools/patrol.sh finish` 呼叫,技能自己不呼叫。本 domain 的 `tools/schedule.sh` 的 `install`、`remove`、`status` 三個子命令:`install` 會查 `$JSC_HOME/current/jsc-assist` 與 `$JSC_HOME/current/jsc-gitea` 兩個連結在不在、不在就警告且不代建,會把巡檢的 CLI 用 `command -v` 解成絕對路徑、把 `GITEA_HOST`、`GITEA_TOKEN`、`JSC_HOME`、`JSC_ASSISTANT_HEARTBEAT_TTL` 與所有已設定的 `JSC_WIKI_REPO` 系列快照進條目、條目自帶 `JSC_GITEA_CONFIRM=yes`、並印出這一輪要開的 `allow_rule=` 規則(四支腳本各三種呼叫形式,含 `gitea.sh`——`Skill(jsc-gitea:wiki)` 只放行叫用技能,技能內部的 Bash 呼叫仍各自受檢;路徑是 `current` 那一組確切路徑,不用萬用字元);七個結束碼各有處置:0 往下走、1 是條目裝了但 cron 沒在跑要照實講不會執行、2 是缺 jsc-hooks 導致門檻讀不到、3 是這台機器沒有排程機制、4 是排程操作失敗要原樣引用 stderr、5 是回讀驗證失敗要叫人自己去看 `crontab -l`、6 是呼叫寫錯,含 `install heartbeat`、週期塞不進門檻、判不出 CLI,以及那一支 CLI 的執行檔不在 `PATH` 上。本 domain 的 `tools/patrol.sh` 的 `collect`、`finish`、`abort` 三個子命令,七個結束碼各有處置:0 往下走、1 部分失敗照樣寫頁、2 是 finish 找不到 heartbeat.sh 要回報「記下來了但沒有心跳」、3 是四項全失敗照樣寫頁且判定異常、4 是讓開或鎖被搶走一律不寫心跳、5 是檔案系統失敗要吵出來、6 是呼叫寫錯。巡檢那四項讀 `jsc-log/tools/usage-stats.sh`、`jsc-hooks/hooks/version-guard.sh report`、`jsc-hooks/hooks/restart-gate.sh report`、`$JSC_HOME/sessions/*.stage`、`$JSC_HOME/wp/*.pr`、`heartbeat.sh report`,全部只讀,任一項失敗不影響其餘三項。wiki 讀寫一律經 `jsc-gitea:wiki`,技能自己不拼 API 呼叫。crontab 與 schtasks 一律經 `tools/schedule.sh`。另外唯讀 `$JSC_HOME/assistant/tasks/` 底下的檔案。呼叫端沒講清楚要哪一個操作時走 `jsc-ask:ask` 的決策樹問,但 `patrol` 那一路一律不問。不參與閘門判定 |
| 完成條件 | `start` 要那一輪巡檢的 `finish` 回 0 且 `report` 回 `state=fresh`,才算啟動成功;巡檢沒寫成心跳一律回報失敗並停下,不得宣稱啟動;`schedule.sh install patrol` 回 1 要講明條目不會被執行與 `sudo service cron start`,不得宣稱排程會定時執行;回 0 或 1 都要把 `allow_rule=` 各行、「條目含金鑰快照、變數改了要重裝」這句提醒,以及 `current` 連結缺漏的警告轉出去。`patrol` 要四項各自有 `status`、監控頁三塊重組寫成、目錄頁那一列更新成功、`finish` 回 0,才算一輪跑完;`collect` 回 4 是讓開,不算失敗也不寫任何東西;舊頁讀不回來就不寫,回報「這一輪沒有結果」;監控頁或目錄頁任一沒寫成就 `abort`,心跳一定不寫。`status` 要印出現況表,或印出「助理未運行」並說明原因;心跳不存在、待辦簿目錄不存在、待辦簿零筆、排程沒裝,四種都算正常結束。`stop` 要 `schedule.sh remove all` 先回 0、`clear` 再回 0,並印出帶三段話的停止訊息;`remove` 非 0 就回報排程還在、助理停不掉,不清心跳也不印停止訊息;`clear` 回 5 就回報心跳檔還在、助理沒有確實停掉,不印停止訊息 |
| 可驗證跡象 | `start` 之後 `$JSC_HOME/assistant/heartbeat` 存在,`ts` 是剛才那一輪的時間,`crontab -l` 找得到一筆帶 `# jsc-assist:assistant patrol` 的條目,而且只有一筆,帶 `# jsc-assist:assistant heartbeat` 的舊條目一筆都不剩;那一筆條目裡的 CLI 是絕對路徑,前面帶著 `JSC_GITEA_CONFIRM=yes` 與環境變數快照;install 印出的 `allow_rule=` 都是 `$JSC_HOME/current` 那一組確切路徑,沒有萬用字元,也沒有 `Write(...)`。`patrol` 跑完之後 wiki 的 `MONITOR_{HASH}` 只有三塊:基本資料一字未改、最新一輪換成本輪、摘要表最上面一列是本輪且總列數不超過 24,`MONITOR_CONTENTS` 只有自己那一列變動,`$JSC_HOME/assistant/patrol/` 底下有本輪的 `latest.md`、`summary.md`、`summary-row.md`、`newpage.md`、`contents.tsv`,`$JSC_HOME/assistant/usage-prev.tsv` 換成本輪的累計數,`$JSC_HOME/assistant/patrol.lock` 已經放掉。讓開的那一輪沒有任何寫入跡象。`stop` 之後心跳路徑不存在,`crontab -l` 找不到任何 `# jsc-assist:assistant` 條目。以上都不動別人的排程條目,條目數量前後相同。`status` 無寫入跡象,只有回報內容。四個操作都不動 `tasks/` 底下的檔案,也不動 worktree 與程式碼存取庫。排程的 log 一律在 `$JSC_HOME/assistant/schedule.log`,不落在任何存取庫 |
| 可驗證跡象 | `start` 之後 `$JSC_HOME/assistant/heartbeat` 存在,`ts` 是剛才那一輪的時間,`crontab -l` 找得到一筆帶 `# jsc-assist:assistant patrol` 的條目,而且只有一筆,帶 `# jsc-assist:assistant heartbeat` 的舊條目一筆都不剩;那一筆條目裡的 CLI 是絕對路徑,前面帶著 `JSC_GITEA_CONFIRM=yes` 與環境變數快照;install 印出的 `allow_rule=` 都是 `$JSC_HOME/current` 那一組確切路徑,沒有萬用字元,也沒有 `Write(...)`。`patrol` 跑完之後 wiki 的 `MONITOR_{HASH}` 只有三塊:基本資料一字未改、最新一輪換成本輪、摘要表最上面一列是本輪且總列數不超過 24,`MONITOR_CONTENTS` 只有自己那一列變動,`$JSC_HOME/assistant/patrol/` 底下有本輪的 `latest.md`、`summary.md`、`summary-row.md`、`newpage.md`、`contents.tsv`,摘要列是五欄、警示來源那一欄有值或寫「無」;兩支腳本不是從 `$JSC_HOME/current` 跑起來時,stderr 會有一行 `[WARN]` 點出實際路徑與應該用的路徑,`$JSC_HOME/assistant/usage-prev.tsv` 換成本輪的累計數,`$JSC_HOME/assistant/patrol.lock` 已經放掉。讓開的那一輪沒有任何寫入跡象。`stop` 之後心跳路徑不存在,`crontab -l` 找不到任何 `# jsc-assist:assistant` 條目。以上都不動別人的排程條目,條目數量前後相同。`status` 無寫入跡象,只有回報內容。四個操作都不動 `tasks/` 底下的檔案,也不動 worktree 與程式碼存取庫。排程的 log 一律在 `$JSC_HOME/assistant/schedule.log`,不落在任何存取庫 |