體檢目錄頁改成大標題加條列,doctor 與 setup 的呼叫同步換鍵 #58

Merged
admin merged 2 commits from feat/contents-list/main into develop 2026-09-02 10:00:37 +00:00
8 changed files with 59 additions and 43 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-cli", "name": "jsc-cli",
"version": "0.3.0", "version": "0.3.1",
"description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署", "description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署",
"skills": "./skills", "skills": "./skills",
"author": { "author": {
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-cli", "name": "jsc-cli",
"version": "0.3.0", "version": "0.3.1",
"description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署", "description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署",
"skills": "./skills", "skills": "./skills",
"jsc": { "jsc": {
+2 -2
View File
@@ -54,11 +54,11 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
### `doctor` ### `doctor`
一次體檢執行環境,只讀不改。四項檢查**同時啟動,各一個 sub agent**:技能版本(`jsc-hooks/hooks/version-guard.sh report`)、Hook 接線(`jsc-hooks/tools/wire-cli.sh status`,唯讀子命令)、設定現況(`tools/scan-config.sh scan all` 比對 `tools/config-spec.tsv`,一次掃完全域與專案兩個範圍)、漏登錄變數(`scan-config.sh orphans`)。唯讀契約下放程式層:呼叫 `wire-cli.sh` 一律帶 `JSC_READONLY=1`,打錯子命令也不會改到機器。每項各出一張表,專案那張一定寫出掃的是哪個目錄;待修項目由 `tools/build-todo.sh` 合併三份輸出並排序。整份結果寫進 wiki `CHECK_{HASH}`,`HASH` 取 `{短主機名}/{登入帳號}`,兩個值都由程式取,主機名一律切掉網域,只保留最新一次。目錄頁 `CHECK_CONTENTS` 住另一個庫(`JSC_WIKI_REPO_CONTENTS`),改由 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 4` 只寫本機那一列。鍵是第 4 欄的裸 `HASH`,不是第 1 欄的連結:網址會隨站台、存取庫與頁名編碼改變,拿網址當鍵就比不中,同一台機器每體檢一次就多附一列。第 1 欄的連結給人點,填絕對網址。修復交給 `/jsc-cli:setup`,體檢本身不動任何設定。 一次體檢執行環境,只讀不改。四項檢查**同時啟動,各一個 sub agent**:技能版本(`jsc-hooks/hooks/version-guard.sh report`)、Hook 接線(`jsc-hooks/tools/wire-cli.sh status`,唯讀子命令)、設定現況(`tools/scan-config.sh scan all` 比對 `tools/config-spec.tsv`,一次掃完全域與專案兩個範圍)、漏登錄變數(`scan-config.sh orphans`)。唯讀契約下放程式層:呼叫 `wire-cli.sh` 一律帶 `JSC_READONLY=1`,打錯子命令也不會改到機器。每項各出一張表,專案那張一定寫出掃的是哪個目錄;待修項目由 `tools/build-todo.sh` 合併三份輸出並排序。整份結果寫進 wiki `CHECK_{HASH}`,`HASH` 取 `{短主機名}/{登入帳號}`,兩個值都由程式取,主機名一律切掉網域,只保留最新一次。目錄頁 `CHECK_CONTENTS` 住另一個庫(`JSC_WIKI_REPO_CONTENTS`),版面是 H1 加 `>` 引言,再一台機器一個 H2 區塊,頁上沒有 markdown 表格;改由 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 CHECK_{HASH}` 只寫本機那一個區塊,欄位是 `- {欄位名}:{值}` 的條列。鍵是 H2 標題,也就是體檢頁頁名 `CHECK_{HASH}`,不是任何含網址的值:網址會隨站台、存取庫與頁名編碼改變,頁名只由主機加帳號決定,拿網址當鍵就比不中,同一台機器每體檢一次就多附一個區塊。第二個參數 `1` 是 `<key-col>`,只在頁面還是舊表格、需要自動轉檔時用得到,指舊表格裡持有 `[CHECK_{HASH}](網址)` 的第 1 欄。「體檢頁」那一條的連結給人點,填絕對網址。修復交給 `/jsc-cli:setup`,體檢本身不動任何設定。
### `setup` ### `setup`
修復 `/jsc-cli:doctor` 找出的問題,一次一項,逐項確認才動手。待修清單優先讀 wiki `CHECK_{HASH}`,沒有頁面就當場重掃:**三支檢查腳本併行跑**,再用 `tools/build-todo.sh` 合併成同一張表。依修法分流:`auto` 用 `tools/apply-config.sh` 直接寫、`ask` 先用決策樹問到值再寫、`manual` 印出步驟交給操作者。複合修復交回原主:版本落後找 `/jsc-cli:deploy`(連同已確認的模式與版本報告一起傳過去,不讓它重問重查)、hook 未接線找 `/jsc-hooks:hooks-install`、缺 `model-tags.tsv` 找 `/jsc-cli:models`。逐項確認維持循序,**寫完的重驗併行**;環境變數類只驗「rc 段落裡確實有那一行」,環境層面交給下一次 `/jsc-cli:doctor`。最後覆寫體檢頁 `CHECK_{HASH}`,並用 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 4` 以裸 `HASH` 當鍵更新目錄頁 `CHECK_CONTENTS` 的本機那一列,兩頁分屬不同存取庫。 修復 `/jsc-cli:doctor` 找出的問題,一次一項,逐項確認才動手。待修清單優先讀 wiki `CHECK_{HASH}`,沒有頁面就當場重掃:**三支檢查腳本併行跑**,再用 `tools/build-todo.sh` 合併成同一張表。依修法分流:`auto` 用 `tools/apply-config.sh` 直接寫、`ask` 先用決策樹問到值再寫、`manual` 印出步驟交給操作者。複合修復交回原主:版本落後找 `/jsc-cli:deploy`(連同已確認的模式與版本報告一起傳過去,不讓它重問重查)、hook 未接線找 `/jsc-hooks:hooks-install`、缺 `model-tags.tsv` 找 `/jsc-cli:models`。逐項確認維持循序,**寫完的重驗併行**;環境變數類只驗「rc 段落裡確實有那一行」,環境層面交給下一次 `/jsc-cli:doctor`。最後覆寫體檢頁 `CHECK_{HASH}`,並用 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 CHECK_{HASH}` 以 H2 標題(也就是體檢頁頁名)當鍵,更新目錄頁 `CHECK_CONTENTS` 的本機那一個區塊,兩頁分屬不同存取庫。
<!-- JSC-SKILLS:END --> <!-- JSC-SKILLS:END -->
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "jsc-cli", "name": "jsc-cli",
"version": "0.3.0", "version": "0.3.1",
"description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署", "description": "CLI 偵測、模型能力標籤、子代理派工與技能庫批次部署",
"skills": "./skills/", "skills": "./skills/",
"jsc": { "jsc": {
+6 -6
View File
@@ -27,10 +27,10 @@
| 項目 | 內容 | | 項目 | 內容 |
| --- | --- | | --- | --- |
| 觸發時機 | 裝完或更新完技能組、技能因設定或接線問題失敗、機器要交接前用。要動手修不用這支,那是 jsc-cli:setup | | 觸發時機 | 裝完或更新完技能組、技能因設定或接線問題失敗、機器要交接前用。要動手修不用這支,那是 jsc-cli:setup |
| 關鍵步驟 | 同時開四個 sub agent 收版本、hook 接線、設定與未登錄變數,每個 sub agent 回傳原始輸出行、把四份輸出各存成檔、依 templates/check-page.md 印出五個區塊並寫明掃描的專案目錄、用 tools/build-todo.sh 把三份輸出合成待修項目表、用程式取短主機名與登入帳號(主機名切掉第一個點之後的網域)交給 hash-id 算出 HASH、用 wiki-repo CHECK 解出的存取庫透過 jsc-gitea:wiki 整頁覆寫 CHECK_{HASH}、寫完再用 wiki-url 取該頁絕對網址、把要寫進兩頁的每個連結交給 jsc-gitea/tools/link-check.sh 驗證且只有結束碼 0 才往下寫、第 1 欄的連結寫成 `[CHECK_{HASH}]({絕對網址})`、用 wiki-contents.sh upsert CHECK 4 以第 4 欄的裸 HASH 當鍵把本機那一列寫進 CONTENTS 存取庫的 CHECK_CONTENTS、報出四項計數並視情況建議 /jsc-cli:setup,最後呼叫 jsc-hooks/tools/report-status.sh skill-end jsc-cli:doctor 寫下這一輪的結果。這一筆是這支技能唯一的寫入動作,記的是查到什麼,不動設定、不動接線、不動版本,唯讀合約照樣成立;走每一條出口都要寫。status 五選一,四項檢查都有結論、五個區塊與待修項目表都在畫面上、兩頁都寫成是 ok,任何一項報成無法驗證、離線讓 Gitea 相關列變成 skipped、或 wiki-repo 回 3 而略過寫頁是 degraded——唯讀技能讀不到來源就是這一種,金鑰失效回 7 或其他 API 失敗回 8 讓紀錄寫不成是 failed,使用者在寫頁之前喊停是 aborted。blocked 這支用不到:沒 CLI、沒登錄檔、沒 wiki 存放庫的機器一樣查得出四項發現,報成 blocked 會把做完的一輪講成沒做事。detail 只放四項計數 | | 關鍵步驟 | 同時開四個 sub agent 收版本、hook 接線、設定與未登錄變數,每個 sub agent 回傳原始輸出行、把四份輸出各存成檔、依 templates/check-page.md 印出五個區塊並寫明掃描的專案目錄、用 tools/build-todo.sh 把三份輸出合成待修項目表、用程式取短主機名與登入帳號(主機名切掉第一個點之後的網域)交給 hash-id 算出 HASH、用 wiki-repo CHECK 解出的存取庫透過 jsc-gitea:wiki 整頁覆寫 CHECK_{HASH}、寫完再用 wiki-url 取該頁絕對網址、把要寫進兩頁的每個連結交給 jsc-gitea/tools/link-check.sh 驗證且只有結束碼 0 才往下寫、「體檢頁」那一條的連結寫成 `[CHECK_{HASH}]({絕對網址})`、用 wiki-contents.sh upsert CHECK 1 CHECK_{HASH} 以 H2 標題也就是體檢頁頁名當鍵,把本機那一個區塊寫進 CONTENTS 存取庫的 CHECK_CONTENTS,區塊檔是 `## CHECK_{HASH}` 那一行、一個空行,再照 templates/check-contents.md 的欄位順序每欄一條 `- {欄位名}:{值}`、報出四項計數並視情況建議 /jsc-cli:setup,最後呼叫 jsc-hooks/tools/report-status.sh skill-end jsc-cli:doctor 寫下這一輪的結果。這一筆是這支技能唯一的寫入動作,記的是查到什麼,不動設定、不動接線、不動版本,唯讀合約照樣成立;走每一條出口都要寫。status 五選一,四項檢查都有結論、五個區塊與待修項目表都在畫面上、兩頁都寫成是 ok,任何一項報成無法驗證、離線讓 Gitea 相關列變成 skipped、或 wiki-repo 回 3 而略過寫頁是 degraded——唯讀技能讀不到來源就是這一種,金鑰失效回 7 或其他 API 失敗回 8 讓紀錄寫不成是 failed,使用者在寫頁之前喊停是 aborted。blocked 這支用不到:沒 CLI、沒登錄檔、沒 wiki 存放庫的機器一樣查得出四項發現,報成 blocked 會把做完的一輪講成沒做事。detail 只放四項計數 |
| 外部呼叫 | jsc-hooks/hooks/version-guard.sh report、jsc-cli/tools/detect-clis.sh、jsc-hooks/tools/wire-cli.sh status(一律帶 JSC_READONLY=1)、jsc-cli/tools/scan-config.sh 的 scan all 與 orphans、jsc-cli/tools/build-todo.sh、jsc-gitea/tools/gitea.sh 的 wiki-repo、hash-id 與 wiki-url、jsc-gitea/tools/link-check.sh、jsc-gitea/tools/wiki-contents.sh upsert、jsc-hooks/tools/report-status.sh skill-end、jsc-gitea:wiki | | 外部呼叫 | jsc-hooks/hooks/version-guard.sh report、jsc-cli/tools/detect-clis.sh、jsc-hooks/tools/wire-cli.sh status(一律帶 JSC_READONLY=1)、jsc-cli/tools/scan-config.sh 的 scan all 與 orphans、jsc-cli/tools/build-todo.sh、jsc-gitea/tools/gitea.sh 的 wiki-repo、hash-id 與 wiki-url、jsc-gitea/tools/link-check.sh、jsc-gitea/tools/wiki-contents.sh upsert、jsc-hooks/tools/report-status.sh skill-end、jsc-gitea:wiki |
| 完成條件 | 四項檢查各有結論,或明寫無法驗證與原因;五個區塊與待修項目表都在畫面上;每個要寫進頁面的連結都經 link-check.sh 驗過,結束碼 0 才寫,1 就兩頁都不寫並列出 DEAD 那幾筆,3 把 GITEA_HOST 排進待修項目最前面,7 停下來回報金鑰問題而不判成死連結;連結一律寫成文字加絕對網址的形式;兩頁各自寫成功,或寫入略過連同結束碼一起回報,wiki-contents.sh 宣告的 0、1、2、3、4、7、8 每一碼都有分流,建不建新頁的判斷留在腳本裡,技能不自己建;必要項缺漏、設定錯誤、CLI 未接線、domain 落後四項計數都講出來。這一輪還要留下一筆 skill-end 事件,或是腳本不在而略過,兩者都算收好;略過不影響這支技能的結束碼 | | 完成條件 | 四項檢查各有結論,或明寫無法驗證與原因;五個區塊與待修項目表都在畫面上;每個要寫進頁面的連結都經 link-check.sh 驗過,結束碼 0 才寫,1 就兩頁都不寫並列出 DEAD 那幾筆,3 把 GITEA_HOST 排進待修項目最前面,7 停下來回報金鑰問題而不判成死連結;連結一律寫成文字加絕對網址的形式,H2 標題本身不放連結;兩頁各自寫成功,或寫入略過連同結束碼一起回報,wiki-contents.sh 宣告的 0、1、2、3、4、7、8 每一碼都有分流,結束碼 1 是組不出頁面內容或寫入失敗,頁上找不到本機那一個區塊不算錯、腳本改成附加,建不建新頁的判斷留在腳本裡,技能不自己建;必要項缺漏、設定錯誤、CLI 未接線、domain 落後四項計數都講出來。這一輪還要留下一筆 skill-end 事件,或是腳本不在而略過,兩者都算收好;略過不影響這支技能的結束碼 |
| 可驗證跡象 | wiki 的 CHECK_{HASH} 頁(雜湊來源是 {短主機名}/{登入帳號})被整頁覆寫成這次的結果;另一個存取庫的 CHECK_CONTENTS 多出本機那一列,或該列的缺漏數與最後體檢時間被更新;第 1 欄是 `[CHECK_{HASH}]({絕對網址})` 這種文字加連結的寫法,點下去連得到體檢頁,頁面上找不到同 wiki 的雙括號連結;第 4 欄是裸 HASH,也就是比對用的鍵,同一台機器重跑幾次都只有這一列,別台機器的列一個位元組都沒變;連結驗不過的那一輪,兩頁都維持上一輪的內容。$JSC_HOME/usage/events.jsonl 會多一筆 {kind:skill,phase:end} 事件,name 是 jsc-cli:doctor,status 與 exit 就是這一輪的結果,那也是這支技能唯一寫得出來的檔案痕跡。機器本身的設定、接線與版本都不動:這支技能不寫任何設定 | | 可驗證跡象 | wiki 的 CHECK_{HASH} 頁(雜湊來源是 {短主機名}/{登入帳號})被整頁覆寫成這次的結果;另一個存取庫的 CHECK_CONTENTS 多出本機那一個 H2 區塊,或該區塊的缺漏數與最後體檢時間被更新;區塊標題是 `## CHECK_{HASH}`,也就是比對用的鍵,標題上沒有連結也沒有網址,「體檢頁」那一條是 `[CHECK_{HASH}]({絕對網址})` 這種文字加連結的寫法,點下去連得到體檢頁,頁面上找不到同 wiki 的雙括號連結;欄位一律是 `- {欄位名}:{值}` 的條列,頁上沒有 markdown 表格,同一台機器重跑幾次都只有這一個區塊,別台機器的區塊一個位元組都沒變;連結驗不過的那一輪,兩頁都維持上一輪的內容。$JSC_HOME/usage/events.jsonl 會多一筆 {kind:skill,phase:end} 事件,name 是 jsc-cli:doctor,status 與 exit 就是這一輪的結果,那也是這支技能唯一寫得出來的檔案痕跡。機器本身的設定、接線與版本都不動:這支技能不寫任何設定 |
## models ## models
@@ -47,7 +47,7 @@
| 項目 | 內容 | | 項目 | 內容 |
| --- | --- | | --- | --- |
| 觸發時機 | doctor 報出待修項目、要實際動手修這台機器時用。只想做唯讀體檢不用這支,那是 jsc-cli:doctor | | 觸發時機 | doctor 報出待修項目、要實際動手修這台機器時用。只想做唯讀體檢不用這支,那是 jsc-cli:doctor |
| 關鍵步驟 | 用程式取短主機名與登入帳號算出 HASH,從 wiki-repo CHECK 解出的存取庫讀 CHECK_{HASH} 的待修項目表,讀不到就以 sub agent 同時重跑設定、接線、版本三個檢查器再用 build-todo.sh 合併、依 jsc-ask 決策樹逐項循序確認、依 fix 欄分流(auto 與 ask 走 apply-config.sh 的 set 或 mkdir、manual 印出步驟交給操作者、domain 落後轉呼叫 jsc-cli:deploy 並附上手上的版本報告、hook 未接線轉呼叫 jsc-hooks:hooks-install、缺 model-tags.tsv 轉呼叫 jsc-cli:models)、同時重驗每個已套用項目、重寫 CHECK_{HASH}、再用 wiki-url 取它的絕對網址、把要寫進兩頁的每個連結交給 jsc-gitea/tools/link-check.sh 驗證且只有結束碼 0 才往下寫、第 1 欄的連結寫成 `[CHECK_{HASH}]({絕對網址})`、以 wiki-contents.sh upsert CHECK 4 用第 4 欄的裸 HASH 當鍵更新 CONTENTS 存取庫的 CHECK_CONTENTS、報出已修、略過、轉呼叫、未修好四項計數,最後呼叫 jsc-hooks/tools/report-status.sh skill-end jsc-cli:setup 寫下這一輪的結果。收尾那一筆走每一條出口,連停在讀不到待修項目那一條也要寫;status 五選一,每一項都修好也重驗過、沒有略過、兩頁都寫成是 ok,環境不允許寫入、apply-config.sh 每一項都回 4 而一個 rc 檔都沒動到是 blocked,已套用的項目重驗仍失敗、apply-config.sh 回 2 是這支技能自己下錯命令、或金鑰失效回 7 與其他 API 失敗回 8 讓紀錄改寫不成是 failed,使用者否決某一項而那一項記成略過、或轉呼叫出去的修正沒收尾是 degraded,使用者在逐項確認到一半喊停、剩下的項目沒問到是 aborted。detail 只放四項計數,不放使用者輸入的值 | | 關鍵步驟 | 用程式取短主機名與登入帳號算出 HASH,從 wiki-repo CHECK 解出的存取庫讀 CHECK_{HASH} 的待修項目表,讀不到就以 sub agent 同時重跑設定、接線、版本三個檢查器再用 build-todo.sh 合併、依 jsc-ask 決策樹逐項循序確認、依 fix 欄分流(auto 與 ask 走 apply-config.sh 的 set 或 mkdir、manual 印出步驟交給操作者、domain 落後轉呼叫 jsc-cli:deploy 並附上手上的版本報告、hook 未接線轉呼叫 jsc-hooks:hooks-install、缺 model-tags.tsv 轉呼叫 jsc-cli:models)、同時重驗每個已套用項目、重寫 CHECK_{HASH}、再用 wiki-url 取它的絕對網址、把要寫進兩頁的每個連結交給 jsc-gitea/tools/link-check.sh 驗證且只有結束碼 0 才往下寫、「體檢頁」那一條的連結寫成 `[CHECK_{HASH}]({絕對網址})`、以 wiki-contents.sh upsert CHECK 1 CHECK_{HASH} 用 H2 標題也就是體檢頁頁名當鍵,更新 CONTENTS 存取庫的 CHECK_CONTENTS 上本機那一個區塊,區塊檔是 `## CHECK_{HASH}` 那一行、一個空行,再照 templates/check-contents.md 的欄位順序每欄一條 `- {欄位名}:{值}`、報出已修、略過、轉呼叫、未修好四項計數,最後呼叫 jsc-hooks/tools/report-status.sh skill-end jsc-cli:setup 寫下這一輪的結果。收尾那一筆走每一條出口,連停在讀不到待修項目那一條也要寫;status 五選一,每一項都修好也重驗過、沒有略過、兩頁都寫成是 ok,環境不允許寫入、apply-config.sh 每一項都回 4 而一個 rc 檔都沒動到是 blocked,已套用的項目重驗仍失敗、apply-config.sh 回 2 是這支技能自己下錯命令、或金鑰失效回 7 與其他 API 失敗回 8 讓紀錄改寫不成是 failed,使用者否決某一項而那一項記成略過、或轉呼叫出去的修正沒收尾是 degraded,使用者在逐項確認到一半喊停、剩下的項目沒問到是 aborted。detail 只放四項計數,不放使用者輸入的值 |
| 外部呼叫 | jsc-cli/tools/scan-config.sh、jsc-cli/tools/detect-clis.sh、jsc-hooks/tools/wire-cli.sh status(一律帶 JSC_READONLY=1)、jsc-hooks/hooks/version-guard.sh report、jsc-cli/tools/build-todo.sh、jsc-cli/tools/apply-config.sh 的 set、mkdir 與 show、jsc-gitea/tools/gitea.sh 的 wiki-repo、hash-id 與 wiki-url、jsc-gitea/tools/link-check.sh、jsc-gitea/tools/wiki-contents.sh upsert、jsc-hooks/tools/report-status.sh skill-end、jsc-ask:ask、jsc-gitea:wiki、jsc-cli:deploy、jsc-hooks:hooks-install、jsc-cli:models | | 外部呼叫 | jsc-cli/tools/scan-config.sh、jsc-cli/tools/detect-clis.sh、jsc-hooks/tools/wire-cli.sh status(一律帶 JSC_READONLY=1)、jsc-hooks/hooks/version-guard.sh report、jsc-cli/tools/build-todo.sh、jsc-cli/tools/apply-config.sh 的 set、mkdir 與 show、jsc-gitea/tools/gitea.sh 的 wiki-repo、hash-id 與 wiki-url、jsc-gitea/tools/link-check.sh、jsc-gitea/tools/wiki-contents.sh upsert、jsc-hooks/tools/report-status.sh skill-end、jsc-ask:ask、jsc-gitea:wiki、jsc-cli:deploy、jsc-hooks:hooks-install、jsc-cli:models |
| 完成條件 | 每一項都有已修、略過、轉呼叫或未修好的結果;每個已套用項目都由自己那一列指定的檢查器重驗過;每個寫進去的環境變數都附上 export 那一行;每個要寫進頁面的連結都經 link-check.sh 驗過,結束碼 0 才寫,1 就兩頁都不寫並列出 DEAD 那幾筆,3 回報 GITEA_HOST 仍未修好,7 停下來回報金鑰問題而不判成死連結;連結一律寫成文字加絕對網址的形式;兩頁各自寫好或略過都有回報,wiki-contents.sh 宣告的 0、1、2、3、4、7、8 每一碼都有分流,建不建新頁的判斷留在腳本裡,技能不自己建;四項計數都講出來。這一輪還要留下一筆 skill-end 事件,或是腳本不在而略過,兩者都算收好;略過不影響這支技能的結束碼 | | 完成條件 | 每一項都有已修、略過、轉呼叫或未修好的結果;每個已套用項目都由自己那一列指定的檢查器重驗過;每個寫進去的環境變數都附上 export 那一行;每個要寫進頁面的連結都經 link-check.sh 驗過,結束碼 0 才寫,1 就兩頁都不寫並列出 DEAD 那幾筆,3 回報 GITEA_HOST 仍未修好,7 停下來回報金鑰問題而不判成死連結;連結一律寫成文字加絕對網址的形式,H2 標題本身不放連結;兩頁各自寫好或略過都有回報,wiki-contents.sh 宣告的 0、1、2、3、4、7、8 每一碼都有分流,結束碼 1 是組不出頁面內容或寫入失敗,頁上找不到本機那一個區塊不算錯、腳本改成附加,建不建新頁的判斷留在腳本裡,技能不自己建;四項計數都講出來。這一輪還要留下一筆 skill-end 事件,或是腳本不在而略過,兩者都算收好;略過不影響這支技能的結束碼 |
| 可驗證跡象 | 各 shell rc 檔的 `# jsc-config` 區塊被改寫,改寫前的備份落在 $JSC_HOME/backup/config/{時間戳}/;auto 路線建立的目錄實際出現在磁碟上;wiki CHECK_{HASH} 被改寫成修完後的狀態,另一個存取庫的 CHECK_CONTENTS 只有本機那一列跟著更新,第 1 欄是 `[CHECK_{HASH}]({絕對網址})` 這種文字加連結的寫法、點下去連得到體檢頁,頁面上找不到同 wiki 的雙括號連結,第 4 欄是當鍵用的裸 HASH;連結驗不過的那一輪,兩頁都維持上一輪的內容;轉呼叫出去的項目留下各自技能的跡象,也就是 deploy 的重啟狀態檔、hooks-install 改寫的接線設定、models 產生的 model-tags.tsv;$JSC_HOME/usage/events.jsonl 會多一筆 {kind:skill,phase:end} 事件,name 是 jsc-cli:setup,status 與 exit 就是這一輪的結果 | | 可驗證跡象 | 各 shell rc 檔的 `# jsc-config` 區塊被改寫,改寫前的備份落在 $JSC_HOME/backup/config/{時間戳}/;auto 路線建立的目錄實際出現在磁碟上;wiki CHECK_{HASH} 被改寫成修完後的狀態,另一個存取庫的 CHECK_CONTENTS 只有本機那一個 H2 區塊跟著更新,區塊標題是當鍵用的 `## CHECK_{HASH}`,標題上沒有連結也沒有網址,「體檢頁」那一條是 `[CHECK_{HASH}]({絕對網址})` 這種文字加連結的寫法、點下去連得到體檢頁,頁面上找不到同 wiki 的雙括號連結,欄位一律是 `- {欄位名}:{值}` 的條列、頁上沒有 markdown 表格;連結驗不過的那一輪,兩頁都維持上一輪的內容;轉呼叫出去的項目留下各自技能的跡象,也就是 deploy 的重啟狀態檔、hooks-install 改寫的接線設定、models 產生的 model-tags.tsv;$JSC_HOME/usage/events.jsonl 會多一筆 {kind:skill,phase:end} 事件,name 是 jsc-cli:setup,status 與 exit 就是這一輪的結果 |
+16 -12
View File
@@ -1,6 +1,6 @@
--- ---
name: doctor name: doctor
description: Health-check the execution environment in one pass and record the result, changing nothing. Four checks - plugin versions from jsc-hooks/hooks/version-guard.sh report, hook wiring from jsc-hooks/tools/wire-cli.sh status, settings from tools/scan-config.sh against tools/config-spec.tsv, and orphan variables. Report one findings table per check, build the 待修項目 table with tools/build-todo.sh, then write the whole run to wiki CHECK_{HASH} where HASH comes from {short hostname}/{user}; the page keeps only the latest run, while its CHECK_CONTENTS row is upserted through wiki-contents.sh into the contents repo. Use after installing or updating the skill set, when a skill fails on a settings or wiring problem, or before handing a machine over; not for applying fixes, which is jsc-cli:setup. description: Health-check the execution environment in one pass and record the result, changing nothing. Four checks - plugin versions from jsc-hooks/hooks/version-guard.sh report, hook wiring from jsc-hooks/tools/wire-cli.sh status, settings from tools/scan-config.sh against tools/config-spec.tsv, and orphan variables. Report one findings table per check, build the 待修項目 table with tools/build-todo.sh, then write the whole run to wiki CHECK_{HASH} where HASH comes from {short hostname}/{user}; the page keeps only the latest run, while its CHECK_CONTENTS block is upserted through wiki-contents.sh into the contents repo. Use after installing or updating the skill set, when a skill fails on a settings or wiring problem, or before handing a machine over; not for applying fixes, which is jsc-cli:setup.
--- ---
# doctor — execution environment health check # doctor — execution environment health check
@@ -115,21 +115,25 @@ user=${USER:-$(id -un 2>/dev/null || printf 'unknown')}
**`CHECK_{HASH}` — content page.** Repo: `jsc-gitea/tools/gitea.sh wiki-repo CHECK`. Write it through `jsc-gitea:wiki` and overwrite the whole page, because this page type keeps only the latest run of this one machine. Whole-page overwrite is correct here and forbidden on the page below. **`CHECK_{HASH}` — content page.** Repo: `jsc-gitea/tools/gitea.sh wiki-repo CHECK`. Write it through `jsc-gitea:wiki` and overwrite the whole page, because this page type keeps only the latest run of this one machine. Whole-page overwrite is correct here and forbidden on the page below.
**`CHECK_CONTENTS` — contents page, in the contents repo.** Repo: `gitea.sh wiki-repo CONTENTS`. Every contents page lives there now; it never falls back to `JSC_WIKI_REPO_CHECK`. Do not hand-edit it — write the row with **`CHECK_CONTENTS` — contents page, in the contents repo.** Repo: `gitea.sh wiki-repo CONTENTS`. Every contents page lives there now; it never falls back to `JSC_WIKI_REPO_CHECK`. It is an H1, a `>` preamble and one H2 block per machine — no markdown table anywhere on it. Do not hand-edit it — write the block with
`jsc-gitea/tools/wiki-contents.sh upsert CHECK 4 "{HASH}" {row file} templates/check-contents.md` `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 "CHECK_{HASH}" {block file} templates/check-contents.md`
The script reads the page back, replaces this machine's row or appends it, then writes the whole page. That keeps the rule in one place: one row per run, never a whole-page overwrite, never another machine's row — those rows are other people's records, and this run read them from nowhere else. The block file holds the whole H2 block: the `## CHECK_{HASH}` line, a blank line, then one bullet per field in the order `templates/check-contents.md` lists them, written as `- {欄位名}:{值}` with a full-width colon. Every field of the template gets a bullet, `HASH` included — the heading is the key, and a field only in the heading is a field the next reader cannot read.
**The key is column 4, the bare `HASH`.** `4` is the 1-based index of the `HASH` column in `templates/check-contents.md`, and the key is the exact string `hash-id` printed — 40 uppercase hex characters, not shortened, not prefixed, not wrapped in a link. The script compares the whole cell, so the key and that cell must match character for character. The script reads the page back, replaces this machine's block or appends it, then writes the whole page. That keeps the rule in one place: one block per run, never a whole-page overwrite, never another machine's block — those blocks are other people's records, and this run read them from nowhere else.
The key is the bare hash and not the link cell for a reason: a cell holding a URL changes whenever `GITEA_HOST` changes, whenever `JSC_WIKI_REPO_CHECK` moves to another repo, or whenever Gitea encodes the page name differently. The comparison then never matches, and every run appends another row for the same machine — silently, because the page still looks right. **The key is the H2 heading, `CHECK_{HASH}`.** It is the name of the content page this block points at: the literal `CHECK_` plus exactly what `hash-id` printed — 40 uppercase hex characters, not shortened, not otherwise prefixed, not wrapped in a link, no date appended. The script compares the whole heading text after trimming, so the key and that heading must match character for character.
Column 1 stays the human-facing link and is never the key. Write it as `[CHECK_{HASH}]({absolute URL})` — text plus link, the one link form this skill set uses. The URL comes from `gitea.sh wiki-url {CHECK repo} CHECK_{HASH}` and is never composed by hand. Fetch it after `CHECK_{HASH}` is written: `wiki-url` exits 4 on a page that does not exist yet. The page name is the key because it is the one value that does not move. It is decided by `{host}/{user}` alone, so it survives a changed `GITEA_HOST`, a `JSC_WIKI_REPO_CHECK` moved to another repo, and a different Gitea encoding of the page name — all of which change the URL. Key on anything holding a URL and the comparison never matches, so every run appends a second block for the same machine — silently, because the page still looks right.
`1` is `<key-col>`, and it only matters while a page is still the old markdown table: it is the 1-based index of the column that held the identity, the `[CHECK_{HASH}]({URL})` cell in column 1, whose text becomes the H2 heading when the script converts that table to blocks. On a page already in block form the script ignores it.
The `體檢頁` bullet stays the human-facing link and is never the key; the heading itself carries no link and no URL. Write the bullet as `[CHECK_{HASH}]({absolute URL})` — text plus link, the one link form this skill set uses. The URL comes from `gitea.sh wiki-url {CHECK repo} CHECK_{HASH}` and is never composed by hand. Fetch it after `CHECK_{HASH}` is written: `wiki-url` exits 4 on a page that does not exist yet.
A same-wiki link form resolves only inside its own wiki, and the two pages are no longer in the same one. It fails without an error, reading on screen as plain text or a dead link, so nobody finds it and nobody fixes it. A same-wiki link form resolves only inside its own wiki, and the two pages are no longer in the same one. It fails without an error, reading on screen as plain text or a dead link, so nobody finds it and nobody fixes it.
**Verify every link before writing it.** Collect every URL heading into `CHECK_{HASH}` or into the `CHECK_CONTENTS` row, then hand the whole list to `jsc-gitea/tools/link-check.sh`. It prints `{OK|DEAD|SKIP}<TAB>{URL}<TAB>{reason}` per line. Only exit 0 may be written. **Verify every link before writing it.** Collect every URL heading into `CHECK_{HASH}` or into the `CHECK_CONTENTS` block, then hand the whole list to `jsc-gitea/tools/link-check.sh`. It prints `{OK|DEAD|SKIP}<TAB>{URL}<TAB>{reason}` per line. Only exit 0 may be written.
| Exit | Meaning | Action | | Exit | Meaning | Action |
| --- | --- | --- | | --- | --- | --- |
@@ -139,21 +143,21 @@ A same-wiki link form resolves only inside its own wiki, and the two pages are n
| 3 | The list holds a Gitea URL but `GITEA_HOST` is unset | Skip both writes and put `GITEA_HOST` at the top of 待修項目. Never write without verifying | | 3 | The list holds a Gitea URL but `GITEA_HOST` is unset | Skip both writes and put `GITEA_HOST` at the top of 待修項目. Never write without verifying |
| 7 | Gitea authentication failed | Stop and report the key problem. This is not a dead link | | 7 | Gitea authentication failed | Stop and report the key problem. This is not a dead link |
Exit 7 stays apart from exit 1 on purpose: with a dead key, Gitea's answer for a private repo looks the same as "page absent". Merge the two and one expired key marks every live page dead, and the rows pointing at them get rewritten or dropped. Exit 7 stays apart from exit 1 on purpose: with a dead key, Gitea's answer for a private repo looks the same as "page absent". Merge the two and one expired key marks every live page dead, and the blocks pointing at them get rewritten or dropped.
The script asks the API and never reads a web status code. A private repo's web URL answers 404 to a request with no credentials, so status codes turn good links into dead ones. The script asks the API and never reads a web status code. A private repo's web URL answers 404 to a request with no credentials, so status codes turn good links into dead ones.
| Exit | Meaning | Action | | Exit | Meaning | Action |
| --- | --- | --- | | --- | --- | --- |
| 0 | `updated` or `added` | Report which one it printed, with the repo and page it named | | 0 | `updated` or `added` | Report which one it printed, with the repo and page it named |
| 1 | Write failed | Nothing landed. Report it with the stderr, and keep 2.1's tables on screen | | 1 | The page content could not be built, or the write failed | Nothing landed. Report it with the stderr, and keep 2.1's tables on screen. A page with no block to replace is not this case: the script appends instead |
| 2 | Usage error | Report it as a defect in this skill. Do not retry with guessed arguments. A `templates/check-contents.md` that is not on disk also lands here — then name the path the script looked for, confirm the plugin install is complete, and rerun | | 2 | Usage error | Report it as a defect in this skill. Do not retry with guessed arguments. A `templates/check-contents.md` that is not on disk also lands here — then name the path the script looked for, confirm the plugin install is complete, and rerun |
| 3 | No contents repo configured | Skip this write and put `JSC_WIKI_REPO_CONTENTS` at the top of 待修項目 | | 3 | No contents repo configured | Skip this write and put `JSC_WIKI_REPO_CONTENTS` at the top of 待修項目 |
| 4 | Page absent and no template given | Unreachable the way this skill calls the script — the command above always passes `templates/check-contents.md`. A template that is not on disk comes back as exit 2, not 4. So treat a 4 as a malformed call: report it as a defect in this skill, name the command that produced it, and do not retry with guessed arguments | | 4 | Page absent and no template given | Unreachable the way this skill calls the script — the command above always passes `templates/check-contents.md`. A template that is not on disk comes back as exit 2, not 4. So treat a 4 as a malformed call: report it as a defect in this skill, name the command that produced it, and do not retry with guessed arguments |
| 7 | Key invalid or no permission | Nothing was read and nothing written. Name the exit code and create nothing | | 7 | Key invalid or no permission | Nothing was read and nothing written. Name the exit code and create nothing |
| 8 | Any other API failure | Same as 7: the old rows are unknown, so name the exit code and create nothing | | 8 | Any other API failure | Same as 7: the old blocks are unknown, so name the exit code and create nothing |
Exits 7 and 8 never mean the page is missing. Writing a fresh template over a directory whose rows were never read wipes every other machine's row, with no merge and no backup behind it — which is exactly why the script creates a page only when its own read reported that page absent, and why it owns that branch instead of this prose. Exits 7 and 8 never mean the page is missing. Writing a fresh template over a directory whose blocks were never read wipes every other machine's block, with no merge and no backup behind it — which is exactly why the script creates a page only when its own read reported that page absent, and why it owns that branch instead of this prose.
`wiki-repo` exiting 3 means that page type has no wiki repo configured: `JSC_WIKI_REPO_CHECK` for the content page, `JSC_WIKI_REPO_CONTENTS` for the contents page. Print the tables, skip that one write, and put the unset variable at the top of 待修項目 — it is itself a finding, so a failed write never fails the health check. Any other non-zero exit from `wiki-repo`, `wiki-url`, `hash-id` or the wiki write is reported the same way: tables on screen, write skipped, exit code named. `wiki-repo` exiting 3 means that page type has no wiki repo configured: `JSC_WIKI_REPO_CHECK` for the content page, `JSC_WIKI_REPO_CONTENTS` for the contents page. Print the tables, skip that one write, and put the unset variable at the top of 待修項目 — it is itself a finding, so a failed write never fails the health check. Any other non-zero exit from `wiki-repo`, `wiki-url`, `hash-id` or the wiki write is reported the same way: tables on screen, write skipped, exit code named.
+15 -11
View File
@@ -99,21 +99,25 @@ The two pages live in **two different wiki repos**. Resolve each one on its own.
Rewrite `CHECK_{HASH}` through `jsc-gitea:wiki` with the post-fix state, per `templates/check-page.md` — repo from `gitea.sh wiki-repo CHECK`. That page is a **content page** and keeps only the latest run, so this overwrites the pre-fix picture on purpose. Rewrite `CHECK_{HASH}` through `jsc-gitea:wiki` with the post-fix state, per `templates/check-page.md` — repo from `gitea.sh wiki-repo CHECK`. That page is a **content page** and keeps only the latest run, so this overwrites the pre-fix picture on purpose.
`CHECK_CONTENTS` is a **contents page**, it lives in the contents repo (`gitea.sh wiki-repo CONTENTS`, never a fallback to `JSC_WIKI_REPO_CHECK`), and it gets the opposite treatment. Write the row with `CHECK_CONTENTS` is a **contents page**, it lives in the contents repo (`gitea.sh wiki-repo CONTENTS`, never a fallback to `JSC_WIKI_REPO_CHECK`), and it gets the opposite treatment. It is an H1, a `>` preamble and one H2 block per machine — no markdown table anywhere on it. Write the block with
`jsc-gitea/tools/wiki-contents.sh upsert CHECK 4 "{HASH}" {row file} templates/check-contents.md` `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 "CHECK_{HASH}" {block file} templates/check-contents.md`
which reads the page back and refreshes this machine's row, or appends it when missing. Never overwrite the whole page, and never touch another machine's row. which reads the page back and refreshes this machine's block, or appends it when missing. Never overwrite the whole page, and never touch another machine's block.
**The key is column 4, the bare `HASH`.** `4` is the 1-based index of the `HASH` column in `templates/check-contents.md`, and the key is exactly what `hash-id` printed for `{host}/{user}` in step 1 — 40 uppercase hex characters, not shortened, not prefixed, not wrapped in a link. The script compares the whole cell, so the key and that cell must match character for character. The block file holds the whole H2 block: the `## CHECK_{HASH}` line, a blank line, then one bullet per field in the order `templates/check-contents.md` lists them, written as `- {欄位名}:{值}` with a full-width colon. Every field of the template gets a bullet, `HASH` included — the heading is the key, and a field only in the heading is a field the next reader cannot read.
A cell holding a URL would make a moving key: it changes with `GITEA_HOST`, with a move of `JSC_WIKI_REPO_CHECK` to another repo, and with Gitea's encoding of the page name. The comparison then never matches, and every run appends a second row for the same machine instead of updating it. **The key is the H2 heading, `CHECK_{HASH}`.** It is the name of the content page this block points at: the literal `CHECK_` plus exactly what `hash-id` printed for `{host}/{user}` in step 1 — 40 uppercase hex characters, not shortened, not otherwise prefixed, not wrapped in a link, no date appended. The script compares the whole heading text after trimming, so the key and that heading must match character for character.
Column 1 stays the human-facing link and is never the key. Write it as `[CHECK_{HASH}]({absolute URL})` — text plus link, the one link form this skill set uses. The URL comes from `gitea.sh wiki-url {CHECK repo} CHECK_{HASH}`, fetched after `CHECK_{HASH}` is rewritten, and is never composed by hand. A key holding a URL would be a moving key: the URL changes with `GITEA_HOST`, with a move of `JSC_WIKI_REPO_CHECK` to another repo, and with Gitea's encoding of the page name. The page name moves with none of them — `{host}/{user}` alone decides it. Key on the URL and the comparison never matches, so every run appends a second block for the same machine instead of updating it.
`1` is `<key-col>`, and it only matters while a page is still the old markdown table: it is the 1-based index of the column that held the identity, the `[CHECK_{HASH}]({URL})` cell in column 1, whose text becomes the H2 heading when the script converts that table to blocks. On a page already in block form the script ignores it.
The `體檢頁` bullet stays the human-facing link and is never the key; the heading itself carries no link and no URL. Write the bullet as `[CHECK_{HASH}]({absolute URL})` — text plus link, the one link form this skill set uses. The URL comes from `gitea.sh wiki-url {CHECK repo} CHECK_{HASH}`, fetched after `CHECK_{HASH}` is rewritten, and is never composed by hand.
A same-wiki link form resolves only inside its own wiki, and the two pages are no longer in the same one. It fails without an error, reading on screen as plain text or a dead link, so nobody finds it and nobody fixes it. A same-wiki link form resolves only inside its own wiki, and the two pages are no longer in the same one. It fails without an error, reading on screen as plain text or a dead link, so nobody finds it and nobody fixes it.
**Verify every link before writing it.** Collect every URL heading into `CHECK_{HASH}` or into the `CHECK_CONTENTS` row, then hand the whole list to `jsc-gitea/tools/link-check.sh`. It prints `{OK|DEAD|SKIP}<TAB>{URL}<TAB>{reason}` per line. Only exit 0 may be written. **Verify every link before writing it.** Collect every URL heading into `CHECK_{HASH}` or into the `CHECK_CONTENTS` block, then hand the whole list to `jsc-gitea/tools/link-check.sh`. It prints `{OK|DEAD|SKIP}<TAB>{URL}<TAB>{reason}` per line. Only exit 0 may be written.
| Exit | Meaning | Action | | Exit | Meaning | Action |
| --- | --- | --- | | --- | --- | --- |
@@ -123,23 +127,23 @@ A same-wiki link form resolves only inside its own wiki, and the two pages are n
| 3 | The list holds a Gitea URL but `GITEA_HOST` is unset | Skip both writes and report `GITEA_HOST` as still unfixed. Never write without verifying | | 3 | The list holds a Gitea URL but `GITEA_HOST` is unset | Skip both writes and report `GITEA_HOST` as still unfixed. Never write without verifying |
| 7 | Gitea authentication failed | Stop and report the key problem. This is not a dead link | | 7 | Gitea authentication failed | Stop and report the key problem. This is not a dead link |
Exit 7 stays apart from exit 1 on purpose: with a dead key, Gitea's answer for a private repo looks the same as "page absent". Merge the two and one expired key marks every live page dead, and the rows pointing at them get rewritten or dropped. Exit 7 stays apart from exit 1 on purpose: with a dead key, Gitea's answer for a private repo looks the same as "page absent". Merge the two and one expired key marks every live page dead, and the blocks pointing at them get rewritten or dropped.
The script asks the API and never reads a web status code. A private repo's web URL answers 404 to a request with no credentials, so status codes turn good links into dead ones. The script asks the API and never reads a web status code. A private repo's web URL answers 404 to a request with no credentials, so status codes turn good links into dead ones.
| Exit | Action | | Exit | Action |
| --- | --- | | --- | --- |
| 0 | Report the `updated` or `added` result with the repo and page it named | | 0 | Report the `updated` or `added` result with the repo and page it named |
| 1 | Write failed and nothing landed. Report it with the stderr | | 1 | The page content could not be built, or the write failed, and nothing landed. Report it with the stderr. A page with no block to replace is not this case: the script appends instead |
| 2 | Usage error. Report it as a defect in this skill; do not retry with guessed arguments. A `templates/check-contents.md` that is not on disk also lands here — then name the path the script looked for, confirm the plugin install is complete, and rerun | | 2 | Usage error. Report it as a defect in this skill; do not retry with guessed arguments. A `templates/check-contents.md` that is not on disk also lands here — then name the path the script looked for, confirm the plugin install is complete, and rerun |
| 3 | No contents repo configured. Skip this write and report `JSC_WIKI_REPO_CONTENTS` as still unfixed | | 3 | No contents repo configured. Skip this write and report `JSC_WIKI_REPO_CONTENTS` as still unfixed |
| 4 | Unreachable the way this skill calls the script — the command above always passes `templates/check-contents.md`, and a template that is not on disk comes back as exit 2. So treat a 4 as a malformed call: report it as a defect in this skill, name the command that produced it, and do not retry with guessed arguments | | 4 | Unreachable the way this skill calls the script — the command above always passes `templates/check-contents.md`, and a template that is not on disk comes back as exit 2. So treat a 4 as a malformed call: report it as a defect in this skill, name the command that produced it, and do not retry with guessed arguments |
| 7 | Key invalid or no permission. Nothing was read or written; name the exit code and create nothing | | 7 | Key invalid or no permission. Nothing was read or written; name the exit code and create nothing |
| 8 | Any other API failure. Same as 7 | | 8 | Any other API failure. Same as 7 |
Exits 7 and 8 never mean the page is missing: the whole-page overwrite that is correct for `CHECK_{HASH}` would here destroy every other machine's row, unread and unrecoverable. The script creates a page only when its own read reported that page absent, and it owns that branch. Exits 7 and 8 never mean the page is missing: the whole-page overwrite that is correct for `CHECK_{HASH}` would here destroy every other machine's block, unread and unrecoverable. The script creates a page only when its own read reported that page absent, and it owns that branch.
`gitea.sh wiki-url` has its own exits, and they are read before the upsert runs. Exit 4 means `CHECK_{HASH}` is not on the wiki yet, so rewrite that page first and fetch the URL again. Any other non-zero exit: name the exit code and stop — never hand-build the URL, because a guessed link goes into the row and points nowhere. `gitea.sh wiki-url` has its own exits, and they are read before the upsert runs. Exit 4 means `CHECK_{HASH}` is not on the wiki yet, so rewrite that page first and fetch the URL again. Any other non-zero exit: name the exit code and stop — never hand-build the URL, because a guessed link goes into the block and points nowhere.
No wiki repo configured, or any non-zero exit from `wiki-repo`, `hash-id`, `wiki-url` or the wiki write → report the tables on screen, say the record was skipped, and name the exit code. No wiki repo configured, or any non-zero exit from `wiki-repo`, `hash-id`, `wiki-url` or the wiki write → report the tables on screen, say the record was skipped, and name the exit code.
+17 -9
View File
@@ -1,19 +1,27 @@
# 體檢目錄 # 體檢目錄
> 由 `jsc-cli:doctor` 維護。每台執行環境一列;`HASH` 取 `{短主機名}/{登入帳號}`,算法與其他頁面共用。主機名取不含網域的短名,FQDN 要先切掉第一個點之後的部分,否則同一台機器會多出第二頁。 > 由 `jsc-cli:doctor` 維護。每台執行環境一個區塊;`HASH` 取 `{短主機名}/{登入帳號}`,算法與其他頁面共用。主機名取不含網域的短名,FQDN 要先切掉第一個點之後的部分,否則同一台機器會多出第二頁。
> >
> 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,與體檢頁 `CHECK_{HASH}` 不同庫。目錄頁全部住這裡,不退回 `JSC_WIKI_REPO_CHECK`。 > 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,與體檢頁 `CHECK_{HASH}` 不同庫。目錄頁全部住這裡,不退回 `JSC_WIKI_REPO_CHECK`。
> >
> 寫入語意:一列代表一台執行環境,也就是一組主機加帳號。一律用 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 4 {HASH} {列檔} {本範本}` 寫,它先讀回整頁,該執行環境已經有列就更新那一列,沒有才在表尾附加一列。禁止整頁覆蓋,也不得改動別人的列。體檢頁 `CHECK_{HASH}` 只留最新一次結果、可以整頁改寫,這份目錄頁不行。 > 寫入語意:一個區塊代表一台執行環境,也就是一組主機加帳號。一律用 `jsc-gitea/tools/wiki-contents.sh upsert CHECK 1 CHECK_{HASH} {區塊檔} {本範本}` 寫,它先讀回整頁,該執行環境已經有區塊就整塊換掉,沒有才在頁尾附加一個區塊。禁止整頁覆蓋,也不得改動別人的區塊。體檢頁 `CHECK_{HASH}` 只留最新一次結果、可以整頁改寫,這份目錄頁不行。
> >
> 鍵是第 4 欄的 `HASH`:`jsc-gitea/tools/hash-id` 印出什麼就填什麼,完整 40 碼大寫十六進位,不截短、不加前綴、不包成連結。腳本比對的是整格文字,鍵一定要跟這一格一字不差。 > 參數說明:第二個參數 `1` 是 `<key-col>`,只在這一頁還留著舊的 markdown 表格時用得到,指舊表格裡持有身分那一欄的序號,也就是持有 `[CHECK_{HASH}](網址)` 的第 1 欄,自動轉檔時取那一格的文字當 H2 標題;頁面已經是條列格式就完全忽略它。第三個參數 `<key>` 是 H2 標題文字,也就是體檢頁頁名 `CHECK_{HASH}`。第四個參數是區塊檔,不是列檔:內容為 `## CHECK_{HASH}` 那一行、一個空行,再接各條欄位。
> >
> 鍵用裸 `HASH` 才穩。`GITEA_HOST` 換掉、`JSC_WIKI_REPO_CHECK` 換過存取庫、Gitea 對頁名的網址編碼有差,網址就跟著變;拿含網址的儲存格當鍵,比對就永遠比不中,同一台機器每體檢一次就多附一列,畫面上還看不出來。 > 鍵是 H2 標題 `CHECK_{HASH}`:`jsc-gitea/tools/hash-id` 印出什麼就接在 `CHECK_` 後面,完整 40 碼大寫十六進位,不截短、不加別的前後綴、不包成連結、不加日期。腳本比對的是去掉頭尾空白後的整段標題文字,鍵一定要跟標題一字不差。
> >
> 連結寫法:第 1 欄的連結只給人點,不當鍵用。一律寫成 `[{文字}]({連結})`,也就是 `[CHECK_{HASH}]({wiki-url 印出的絕對網址})`,網址取 `jsc-gitea/tools/gitea.sh wiki-url` 印出的那一串,不自己組路徑。同 wiki 的雙括號寫法一概不用:兩頁分屬不同存取庫,連不過去,畫面上還看不出壞掉。 > 鍵用頁名才穩。頁名只由 `{短主機名}/{登入帳號}` 決定;`GITEA_HOST` 換掉、`JSC_WIKI_REPO_CHECK` 換過存取庫、Gitea 對頁名的網址編碼有差,網址就跟著變,頁名一個字都不動。拿含網址的值當鍵,比對就永遠比不中,同一台機器每體檢一次就多附一個區塊,畫面上還看不出來。
> >
> 連結驗證:這一列要寫進去的每一個連結,先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫。有任何一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。結束碼 3 代表 `GITEA_HOST` 沒設定,先設好再寫,不得跳過驗證;結束碼 7 代表金鑰失效,停下來回報金鑰問題,不要當成死連結。驗證一律走 API,不看網頁狀態碼:私有存取庫的網頁網址對未登入請求一律回 404,拿狀態碼判會把好連結判成壞的,整批砍掉還在的頁。 > 連結寫法:「體檢頁」那一條的連結只給人點,不當鍵用;H2 標題本身不放連結、不放網址。連結一律寫成 `[{文字}]({連結})`,也就是 `[CHECK_{HASH}]({wiki-url 印出的絕對網址})`,網址取 `jsc-gitea/tools/gitea.sh wiki-url` 印出的那一串,不自己組路徑。同 wiki 的雙括號寫法一概不用:兩頁分屬不同存取庫,連不過去,畫面上還看不出壞掉。
>
> 連結驗證:這個區塊要寫進去的每一個連結,先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫。有任何一筆 DEAD 就整個區塊都不寫,把連不到的清單回報給呼叫端。結束碼 3 代表 `GITEA_HOST` 沒設定,先設好再寫,不得跳過驗證;結束碼 7 代表金鑰失效,停下來回報金鑰問題,不要當成死連結。驗證一律走 API,不看網頁狀態碼:私有存取庫的網頁網址對未登入請求一律回 404,拿狀態碼判會把好連結判成壞的,整批砍掉還在的頁。
| 體檢頁 | 主機 | 帳號 | HASH | 必要項缺漏 | 設定錯誤 | 最後體檢 | ## CHECK_{HASH}
| --- | --- | --- | --- | --- | --- | --- |
| [CHECK_{HASH}]({wiki-url 印出的絕對網址}) | {短主機名} | {使用者帳號} | {HASH} | {n} | {n} | {yyyy-MM-dd HH:mm} | - 體檢頁:[CHECK_{HASH}]({wiki-url 印出的絕對網址})
- 主機:{短主機名}
- 帳號:{使用者帳號}
- HASH:{HASH}
- 必要項缺漏:{n}
- 設定錯誤:{n}
- 最後體檢:{yyyy-MM-dd HH:mm}