Commit Graph
1 Commits
Author SHA1 Message Date
jiantw83 2ec4379924 feat(tasks): 助理待辦簿的存放格式與讀寫工具
助理要記住兩種項目——內建的定期檢查項與使用者當面交辦的事——但目前沒有地方放。待辦簿是助理待辦與建議下一個指令兩批功能的共同地基,先把存放格式與讀寫工具做出來,判定邏輯之後才接。

存成一筆一檔,純文字 key=value。一筆一檔的理由同重啟閘門那組檔案:並行寫入不互相覆寫。五支 CLI 加上排程那一輪有可能同時動待辦簿,整本存成一個檔案的話兩邊各讀一次整檔、各改自己那一筆、各寫回整檔,後寫的那一次就把前一次整本蓋掉,而且沒有任何訊號。同一個 id 被同時寫也不會寫出半份:先寫暫存檔再搬過去,搬在同一個檔案系統上是原子操作。暫存檔名以點號開頭,列表跳過點號開頭的檔案,寫到一半的那一份不會被列出來。

規格的欄位表少一個欄位。id 是建立時間加標題的雜湊,但表上沒有存建立時間的欄位,不存它就再也算不回同一個 id、驗不出檔名對不對、前綴碰撞時也接不下去。補上建立時間,所以一筆是十四個鍵。

id 取完整雜湊的前八碼,不跟 wiki 頁名的四十碼一致。兩者用途不同:四十碼那條規則管的是 wiki 頁名,頁名一撞就是兩台機器的紀錄互相覆寫且看不出來,所以不准截短;這裡的 id 是本機檔名,人要念它、打進指令、看它印在狀態表與提醒文字裡,四十碼沒有人讀得完也打不對,人就會改用「第三筆」這種說法指定要關哪一筆,那才是真正會關錯的地方。前八碼是同一個雜湊的前綴不是另一套算法,拿建立時間與標題重算就驗得回來。前綴撞上而內容不同就每次多取兩碼再試,不在後面補序號——補了就算不回來。建立時間與標題都相同的不是碰撞而是同一筆被登錄兩次,一律不寫並回專屬結束碼,因為定期檢查項會因為清單重建而重跑登錄,靜靜多寫一筆會讓同一個檢查每輪做兩次。

操作補到六個,兩個都是補規格的洞。失敗那一個是因為規格說失敗要累加連續失敗次數,但原本四個操作沒有一個寫得到它,少了它那個計數永遠是零,監控頁與提醒上那句「已連續失敗幾次」永遠是零次,於是一個壞掉的項目每輪重試而沒人知道,那正是這個欄位要防的事。恢復那一個是因為停用只由人設,也就只有人解得開,沒有別的元件寫得出這個轉移;只給停用不給恢復就是一道單向門,人只能去手改檔案,而手改繞過了轉移表。

上次執行與下次執行不另開操作:完成與失敗都吃選項把值餵進來,值由算到期那一邊算好。這一支不算下一次是什麼時候,兩邊各算一次就會漂移。也刻意沒有編輯操作:id 由建立時間與標題算出來,改掉標題之後 id 就對不回去了。

狀態轉移寫成明確的表並在腳本裡擋住不合法的轉移。停用之後不收完成,因為停掉的那一筆助理本來就沒在跑,標成做完等於偷偷解開又收掉;停用之後不收失敗,因為助理沒在跑它就不可能是它失敗;收掉的那一筆不再有下一次,四種操作全擋。恢復刻意不把連續失敗次數歸零,那幾次失敗真的發生過,歸零會把提醒抹掉而那筆一恢復就會照樣再失敗,要歸零就等它跑成功一次。

值裡有等號或換行用兩條約定處理,不發明跳脫規則。讀的時候只在第一個等號斷開,十四個鍵都是固定的詞、一個都不含等號。寫的時候把值折成一行並在標準錯誤記一行說折過了。選折行不選跳脫,是因為讀的人不只這一支腳本,技能本文與巡檢那一輪都直接把檔案當 key=value 讀,跳脫規則要每個讀的人各自實作一次,漏掉一個就會把跳脫序列原樣印進報告或監控頁,而那看起來很像正常內容。

根目錄解析退回使用者家目錄底下那一層,跟排程、巡檢與心跳三支退回同一個值——兩邊退回的位置不同,待辦簿就會躲在一個沒有人去讀的目錄裡,而每一支都自認為讀對了。連家目錄也沒有才拒絕,不猜路徑:猜錯就是把待辦寫到一個沒有人會去讀的地方,而且看起來像成功。

實測抓到一個真的臭蟲並修掉。查找記錄原本寫成印出路徑、由呼叫端用命令替換接,於是裡面的錯誤退出只結束子行程、外層照樣往下走,訊息說欄位不合法卻回了找不到那一筆的碼,呼叫端拿到的碼與真正的原因不一樣。改寫成直接設變數再回碼。

這一輪只做存放與讀寫。登錄時的補問流程、事件詞彙表接上產生者、到期判定、逾期與失敗處理、提醒送到前景、wiki 雙向同步,六項都不在這一輪,但欄位都已經留在檔案裡,接的時候不必改存放格式。目前還沒有任何操作呼叫得到這六個子命令,行為契約只記下這一支工具存在,不讓清單先跑到技能前面去。
2026-09-03 15:46:09 +08:00