釋出 jsc-assist 0.1.0:助理主體、排程與一輪巡檢 #6

Merged
admin merged 5 commits from develop into master 2026-09-01 07:51:27 +00:00
Member

摘要

  • 需求描述:助理主體落地。三個操作(啟動、查現況、停止)、系統排程接線、一輪巡檢。巡檢這一輪只做四項純讀取的項目。排程不會自己裝上,要人明著下指令,所以這一支合併之後這台機器的行為不變。
  • 計畫名稱:無
  • 計畫頁:無
  • 分析頁:無

變更內容

檔案 為什麼改
skills/assistant/SKILL.md 新增。助理主體,四個操作
skills/status/SKILL.md 刪除。併進助理主體
tools/schedule.sh 新增。排程的安裝、移除、查現況
tools/patrol.sh 新增。一輪巡檢的收集、收尾、中止
templates/monitor-*.md 巡檢實際會用到,補齊欠缺的節與欄位
references/behaviors.md、README.md 跟著更新
三份 manifest 版本一起提升,對 hooks 的下限提到含心跳腳本的那一版

設計重點

為什麼把 status 併進主體

助理的生命週期是一件事。拆在兩支技能裡,靠描述自動觸發的 CLI 等於擲骰子挑一支,準則也明寫技能目標不得重複。刪除的代價此時最低:這個 domain 剛落地,那是它唯一一支技能,除了自己的文件沒有別的東西指向它。

心跳改由巡檢跑完才寫

原本排程直接寫心跳,那樣心跳新鮮只證明排程活著。巡檢整個壞掉、每輪都失敗,心跳照樣新鮮,閘門照樣放行,而且沒有任何錯誤訊息——這是無聲失效,最難發現的一種。

改成巡檢收尾才寫,心跳新鮮才等於上一輪真的跑完了。閘門判的才是工作訊號。連帶把 install heartbeat 這個工作代號改成拒絕:多一個寫心跳的人就退回舊語意。

週期由門檻推導,不寫死

門檻是讀取端的設定,心跳檔裡不存它。所以週期與門檻各寫死一個數字一定會撞:門檻五分鐘、巡檢十五分鐘,心跳永遠是過期的。

改成安裝時讀出實際生效的門檻,取「漏掉一輪還算新鮮、漏掉兩輪才過期」的最大週期,並且要能整除一小時。要拉長巡檢週期就調大門檻,週期自動跟著長,兩個數字不會各走各的。

心跳只看有沒有把結果記下來

四項有失敗但監控頁寫成了就寫心跳,頁上判定標警示;頁寫不成就中止,一定不寫,讓心跳自己過期。

理由:頁每輪都寫失敗卻照樣寫心跳,等於把上面那個要修掉的缺陷原封不動搬過去。新鮮的意思是「上一輪跑完了」,不是「一切正常」,健康度看頁上的判定欄。

併發與逾時

整輪拿一把目錄鎖。搶不到就讓開,不排隊也不並行——並行兩輪會在同一頁附兩節,還會互相蓋掉用量快照。鎖逾時可被下一輪搶走,被搶走的那一輪收尾時對不上就不寫心跳:它沒跑到底,不該蓋章。

排程的安全要求

固定標記比對,安裝先濾掉自己的舊條目再追加。絕不覆寫整份排程、別人的條目一行都不碰,寫完回讀驗證,出現兩筆或別人的行數對不上就報錯。

裝完檢查排程服務在不在跑。這台機器是 WSL,預設不啟動 cron,裝了條目卻一次都不會執行——不檢查就會宣稱一件沒發生的事。

stop 先移除排程再清心跳,順序不能反。反過來的話清完下一分鐘排程就補寫一次,stop 等於騙人。

巡檢這一輪只做四項

使用統計、版本落差與重啟閘門盤點、階段鎖與工作包鎖現況、心跳自述。四項都是純讀取,不呼叫別的技能、不做決策。

任一項失敗不影響其餘項目。 失敗要在監控頁上看得出來是失敗,不是沒資料;來源是空的則明寫「那是零次,不是故障」。

版本盤點照抄腳本原字。抄到查詢失敗就寫查詢失敗,不改寫成相符、也不自己補查遠端版本——查不到是沒有證據,不是版本沒問題。

測試結果

四支檢核全部 exit 0:lint-scripts.sh、check-behaviors.sh、lint-frontmatter.sh、ste100-lint.sh。兩支腳本 sh -n 都通過,檔案無 BOM。

排程(假的 crontab 內容加預跑,真實 crontab 全程沒動,已回查確認):條目標記正確;已有別人的四行條目時安裝不動到它們;移除只拿掉自己那一筆;重複安裝三次不疊加;查現況在裝了與沒裝兩種情況都正確;cron 服務沒跑時正確吵出提醒並回非 0。門檻 300 推出每 2 分鐘、門檻 1800 推出每 12 分鐘、--period 15 配門檻 300 被拒、install heartbeat 被拒。

巡檢(暫存 JSC_HOME):四項都有資料、四項全空(回「空」不是「失敗」)、其中一項來源不存在時其餘三項照樣完成、頁面結構對得上範本、部分失敗且頁寫成時心跳寫入、頁寫不成時中止且心跳不寫、鎖競爭時第二輪讓開、鎖逾時被搶後舊輪收尾不寫心跳、增量統計正確。

真實環境未受影響:crontab -l 仍是「no crontab for root」,/root/.jsc/assistant 不存在。

前置 Push Request

  • 無
## 摘要 - 需求描述:助理主體落地。三個操作(啟動、查現況、停止)、系統排程接線、一輪巡檢。巡檢這一輪只做四項純讀取的項目。**排程不會自己裝上,要人明著下指令**,所以這一支合併之後這台機器的行為不變。 - 計畫名稱:無 - 計畫頁:無 - 分析頁:無 ## 變更內容 | 檔案 | 為什麼改 | | --- | --- | | `skills/assistant/SKILL.md` | 新增。助理主體,四個操作 | | `skills/status/SKILL.md` | 刪除。併進助理主體 | | `tools/schedule.sh` | 新增。排程的安裝、移除、查現況 | | `tools/patrol.sh` | 新增。一輪巡檢的收集、收尾、中止 | | `templates/monitor-*.md` | 巡檢實際會用到,補齊欠缺的節與欄位 | | `references/behaviors.md`、`README.md` | 跟著更新 | | 三份 manifest | 版本一起提升,對 hooks 的下限提到含心跳腳本的那一版 | ## 設計重點 ### 為什麼把 status 併進主體 助理的生命週期是一件事。拆在兩支技能裡,靠描述自動觸發的 CLI 等於擲骰子挑一支,準則也明寫技能目標不得重複。刪除的代價此時最低:這個 domain 剛落地,那是它唯一一支技能,除了自己的文件沒有別的東西指向它。 ### 心跳改由巡檢跑完才寫 原本排程直接寫心跳,那樣**心跳新鮮只證明排程活著**。巡檢整個壞掉、每輪都失敗,心跳照樣新鮮,閘門照樣放行,而且沒有任何錯誤訊息——這是無聲失效,最難發現的一種。 改成巡檢收尾才寫,心跳新鮮才等於上一輪真的跑完了。閘門判的才是工作訊號。連帶把 `install heartbeat` 這個工作代號改成拒絕:多一個寫心跳的人就退回舊語意。 ### 週期由門檻推導,不寫死 門檻是**讀取端**的設定,心跳檔裡不存它。所以週期與門檻各寫死一個數字一定會撞:門檻五分鐘、巡檢十五分鐘,心跳永遠是過期的。 改成安裝時讀出實際生效的門檻,取「漏掉一輪還算新鮮、漏掉兩輪才過期」的最大週期,並且要能整除一小時。要拉長巡檢週期就調大門檻,週期自動跟著長,兩個數字不會各走各的。 ### 心跳只看有沒有把結果記下來 四項有失敗但監控頁寫成了就寫心跳,頁上判定標警示;**頁寫不成就中止,一定不寫**,讓心跳自己過期。 理由:頁每輪都寫失敗卻照樣寫心跳,等於把上面那個要修掉的缺陷原封不動搬過去。新鮮的意思是「上一輪跑完了」,不是「一切正常」,健康度看頁上的判定欄。 ### 併發與逾時 整輪拿一把目錄鎖。搶不到就讓開,不排隊也不並行——並行兩輪會在同一頁附兩節,還會互相蓋掉用量快照。鎖逾時可被下一輪搶走,被搶走的那一輪收尾時對不上就不寫心跳:它沒跑到底,不該蓋章。 ### 排程的安全要求 固定標記比對,安裝先濾掉自己的舊條目再追加。**絕不覆寫整份排程、別人的條目一行都不碰**,寫完回讀驗證,出現兩筆或別人的行數對不上就報錯。 裝完檢查排程服務在不在跑。這台機器是 WSL,預設不啟動 cron,裝了條目卻一次都不會執行——不檢查就會宣稱一件沒發生的事。 `stop` 先移除排程再清心跳,**順序不能反**。反過來的話清完下一分鐘排程就補寫一次,`stop` 等於騙人。 ### 巡檢這一輪只做四項 使用統計、版本落差與重啟閘門盤點、階段鎖與工作包鎖現況、心跳自述。四項都是純讀取,不呼叫別的技能、不做決策。 **任一項失敗不影響其餘項目。** 失敗要在監控頁上看得出來是失敗,不是沒資料;來源是空的則明寫「那是零次,不是故障」。 版本盤點**照抄腳本原字**。抄到查詢失敗就寫查詢失敗,不改寫成相符、也不自己補查遠端版本——查不到是沒有證據,不是版本沒問題。 ## 測試結果 四支檢核全部 exit 0:`lint-scripts.sh`、`check-behaviors.sh`、`lint-frontmatter.sh`、`ste100-lint.sh`。兩支腳本 `sh -n` 都通過,檔案無 BOM。 **排程**(假的 crontab 內容加預跑,真實 crontab 全程沒動,已回查確認):條目標記正確;已有別人的四行條目時安裝不動到它們;移除只拿掉自己那一筆;重複安裝三次不疊加;查現況在裝了與沒裝兩種情況都正確;cron 服務沒跑時正確吵出提醒並回非 0。門檻 300 推出每 2 分鐘、門檻 1800 推出每 12 分鐘、`--period 15` 配門檻 300 被拒、`install heartbeat` 被拒。 **巡檢**(暫存 `JSC_HOME`):四項都有資料、四項全空(回「空」不是「失敗」)、其中一項來源不存在時其餘三項照樣完成、頁面結構對得上範本、部分失敗且頁寫成時心跳寫入、頁寫不成時中止且心跳不寫、鎖競爭時第二輪讓開、鎖逾時被搶後舊輪收尾不寫心跳、增量統計正確。 **真實環境未受影響**:`crontab -l` 仍是「no crontab for root」,`/root/.jsc/assistant` 不存在。 ## 前置 Push Request - 無
jiantw83 added 5 commits 2026-09-01 07:42:18 +00:00
What:
- 新增 assistant 技能,提供啟動、查現況、停止三個操作。
- 刪掉原本獨立的 status 技能,行為清單與 README 的技能目錄一起改。
- 三份 manifest 的版本一起提升,對 jsc-hooks 的下限提到含心跳腳本的那一版。

Why:
- 助理的生命週期是一件事,拆在兩支技能裡,靠描述自動觸發的 CLI 等於擲骰子挑一支。準則也明寫技能目標不得重複。
- 刪除的代價這時候最低:這個 domain 才剛落地,status 是它唯一一支技能,除了自己的文件沒有別的東西指向它。晚一步等各處都引用了再收攏,成本差很多。

How:
- 心跳的判定一律交給 jsc-hooks 的心跳腳本,三個操作都讀它的回報,不自己解析心跳檔。判定有兩份就會漂移,狀態與訊息就會對不上。
- 那支腳本的每一個結束碼都在技能裡有明確處置,包含「腳本自己沒跑起來」那一種——那時候既不能說助理在跑,也不能說助理停了。
- 時間戳壞掉一律當成不新鮮,絕不退回判成新鮮。
- 異常結束不需要額外的清理機制:心跳是時間戳,過了門檻自動失效。反過來說,stop 以外的任何路徑都不該寫心跳,否則就是留一個假心跳。
- stop 清心跳是它的職責,不算助理界線裡「不刪狀態檔」那一條。技能內文把這個例外寫明白,免得日後照界線把 stop 砍掉。
- stop 的收尾同時講兩件事:心跳清掉之後閘門會擋下技能呼叫,以及閘門目前還沒接線所以這一刻擋不到誰。前者是設計後果,停助理的人一定要知道;後者不講就是說一件還沒成真的事。
- 排程這一輪不做,start 只寫第一次心跳,並講明心跳不會自動更新。

Who:
助理落地的第二塊:心跳有了,接著要有人寫它、讀它、清它。
What:
- 新增 tools/schedule.sh,三個子命令:install 安裝、remove 移除、status 查現況。兩筆工作分別是每分鐘的心跳與每十五分鐘的巡檢。
- 助理主體的 start 接上安裝、stop 接上移除,行為清單與 README 跟著更新。

Why:
- 心跳要有人定時寫,不然啟動之後過幾分鐘就自己過期,助理看起來像沒在跑。
- 排程這件事有標準的輸入與輸出,準則要求下放腳本,技能只描述何時呼叫。

How:
- 條目行尾帶固定標記,安裝先濾掉自己的舊條目再追加。絕不覆寫整份排程,別人的條目一行都不碰;寫完回讀驗證,出現兩筆或別人的行數對不上就報錯。
- 裝完檢查排程服務在不在跑。這台機器是 WSL,預設不啟動 cron,裝了條目卻一次都不會執行——不檢查就會宣稱一件沒發生的事。
- 條目一律帶 </dev/null。排程是非互動環境,任何等輸入的東西都會把整筆卡死。
- 輸出寫到 JSC_HOME 底下,不寫進任何專案目錄,免得多出未追蹤檔污染別人的變更盤點。
- 巡檢那一筆預設不裝,要明著指定才會裝。巡檢本體還沒實作,裝了只會每十五分鐘失敗一次;巡檢指令也不猜 CLI,判不出來就停下,猜錯的代價是每十五分鐘跑一支不存在的執行檔。
- stop 先移除排程再清心跳,順序不能反。反過來的話清完下一分鐘排程就補寫一次,stop 等於騙人。

Who:
助理落地的第三塊。這裡留下一個要在巡檢那一輪解掉的問題:心跳目前由排程直接寫,所以心跳新鮮只證明排程活著,不證明助理做了事。往後應該改由巡檢跑完那一輪去寫,心跳才等於工作訊號。限制已寫進技能內文與說明文件。
What:
- 新增 tools/patrol.sh,三個子命令:collect 收集、finish 收尾、abort 中止。這一輪的巡檢項目是四項純讀取:使用統計、版本落差與重啟閘門、階段鎖與工作包鎖、心跳自述。
- 心跳的寫入從排程移到巡檢收尾。排程只呼叫巡檢,不再直接寫心跳。
- 排程週期由心跳門檻推導,不再寫死。install heartbeat 這個工作代號改為拒絕。

Why:
- 排程直接寫心跳的話,心跳新鮮只證明排程活著。巡檢整個壞掉、每輪都失敗,心跳照樣新鮮,閘門照樣放行,而且沒有任何錯誤訊息——這是無聲失效,是最難發現的一種。
- 改成巡檢寫,心跳新鮮才等於上一輪真的跑完了。閘門判的才是工作訊號,不是行程存活訊號。
- 門檻是讀取端的設定,心跳檔裡不存它。所以週期與門檻各寫死一個數字一定會撞:門檻五分鐘、巡檢十五分鐘,心跳永遠是過期的。

How:
- 週期取「滿足漏掉一輪還算新鮮、漏掉兩輪才過期」的最大值,並且要能整除一小時。要拉長巡檢週期就調大門檻,週期自動跟著長,兩個數字不會各走各的。
- 心跳只看「這一輪有沒有把結果記下來」,不看四項的成敗。四項有失敗但監控頁寫成了就寫心跳,頁上判定標警示;頁寫不成就中止,一定不寫,讓心跳自己過期。頁每輪都寫失敗卻照樣寫心跳,等於把這次要修掉的缺陷原封不動搬過去。
- 整輪拿一把目錄鎖,搶不到就讓開,不排隊也不並行。並行兩輪會在同一頁附兩節,還會互相蓋掉用量快照。鎖逾時可被下一輪搶走,被搶走的那一輪收尾時對不上就不寫心跳——它沒跑到底,不該蓋章。
- 任一項失敗不影響其餘項目。失敗要在監控頁上看得出來是失敗,不是沒資料;來源是空的則明寫「那是零次,不是故障」。
- 版本盤點照抄腳本原字。抄到查詢失敗就寫查詢失敗,不改寫成相符、也不自己補查遠端版本——查不到是沒有證據,不是版本沒問題。

Who:
助理落地的第四塊。排程那一輪留下的問題在這裡解掉了。
Reviewed-on: #4
Reviewed-on: #5
admin approved these changes 2026-09-01 07:51:24 +00:00
admin merged commit 9590adcfd4 into master 2026-09-01 07:51:27 +00:00
Sign in to join this conversation.
No Reviewers
No labels
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: plugins/assist#6