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