feat(wiki): 體檢目錄頁改走專用存取庫,並補齊設定規格表

What:CHECK_CONTENTS 改由 wiki-repo CONTENTS 解析並透過 wiki-contents.sh upsert
寫入,CHECK_{HASH} 仍走 wiki-repo CHECK。目錄頁新增一欄裸 HASH 當比對鍵。主機名
改由程式取短名,不再交給模型自由填。

Why:比對鍵原本是含網址的儲存格,換主機或換存取庫就比對不到,每跑一次體檢就替同一台
機器多附一列,畫面上還看不出來。主機名短名與 FQDN 不一致時,同一台機器會分裂成兩張頁,
而助理巡檢那邊是用程式取值的,兩邊對不起來。

How:設定規格表同一輪補齊三處既有缺漏——補上漏掉的 JSC_WIKI_REPO_MONITOR,體檢本來
看不到它而孤兒掃描還會誤報;刪掉指向不存在頁面的 MAINTAIN 內容頁字樣;MAINTAIN 那一列
改成不需要使用者處理,免得體檢叫人去設一支管不到任何頁的變數。整列保留,刪掉會讓孤兒
掃描開始誤報那個變數。

Who:jsc-cli
This commit is contained in:
2026-09-02 11:03:07 +08:00
parent 225e33d2c8
commit fb80559159
10 changed files with 123 additions and 47 deletions
+38 -9
View File
@@ -1,6 +1,6 @@
---
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 {hostname}/{user}; the page keeps only the latest run. 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 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.
---
# doctor — execution environment health check
@@ -102,18 +102,47 @@ Done when all five blocks of 2.1 are on screen with the scanned directory stated
### 3.1 Record
Write the page through `jsc-gitea:wiki`:
This run writes two pages, and they live in **two different wiki repos**. Resolve each repo on its own and never reuse one for the other.
- Wiki repo: `jsc-gitea/tools/gitea.sh wiki-repo CHECK`.
- Page name: `CHECK_` plus `gitea.sh hash-id "{hostname}/{user}"` — the host and the login account, not `{owner}/{repo}`. Doctor checks a machine, and it has to work in directories that are not repositories at all.
- `CHECK_{HASH}` is a **content page**: overwrite the whole page, because this page type keeps only the latest run of this one machine.
- `CHECK_CONTENTS` is a **contents page** and follows the opposite rule: read it back first, then upsert this machine's row from `templates/check-contents.md` in the same pass — add the row if missing, otherwise refresh its 必要項缺漏、設定錯誤、最後體檢 columns. Never overwrite the whole page, and never touch a row belonging to another machine: those rows are other people's records, and this run never read them from anywhere else.
- The `CHECK_CONTENTS` read branches by exit code, and only exit 4 opens the create path. Exit 0 means the page is there, so upsert into what came back. Exit 4 means the page really does not exist yet, so build it from the template. Exit 7 (key invalid or no permission) and exit 8 (any other API failure) both mean the old rows are unknown, never that the page is missing: skip the `CHECK_CONTENTS` write, name the exit code in the report, and create nothing. Writing a fresh template over a directory whose rows were never read wipes every other machine's row, and the write carries no merge and no backup.
**The HASH — take it from the machine, do not compose it by hand.** `jsc-assist`'s `MONITOR_{HASH}` claims the same hash source and takes it in code, so the two pages only ever line up when this skill takes it the same way:
`wiki-repo` exiting 3 means no wiki repo is configured for CHECK. Print the tables, skip the wiki write, and put `JSC_WIKI_REPO_CHECK` at the top of 待修項目 — that unset variable is itself a finding, so a failed write never fails the health check. Any other non-zero exit from `wiki-repo`, `hash-id` or the wiki write is reported the same way: tables on screen, write skipped, exit code named.
```sh
host=$(hostname 2>/dev/null || uname -n 2>/dev/null || printf 'unknown'); host=${host%%.*}
user=${USER:-$(id -un 2>/dev/null || printf 'unknown')}
```
`${host%%.*}` is the point of the snippet: `hostname` prints the FQDN on some machines and the short name on others, so a hand-written value gives one machine two pages that never merge again. Pass `"{host}/{user}"` to `gitea.sh hash-id` and use exactly what it prints. It is the host and the login account, not `{owner}/{repo}` — doctor checks a machine, and it has to work in directories that are not repositories at all.
**`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
`jsc-gitea/tools/wiki-contents.sh upsert CHECK 4 "{HASH}" {row 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 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 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.
Column 1 stays the human-facing link and is never the key. Build it as an **absolute URL** from `gitea.sh wiki-url {CHECK repo} CHECK_{HASH}`. `[[CHECK_{HASH}]]` resolves only inside its own wiki, and the two pages are no longer in the same one. Fetch the URL after `CHECK_{HASH}` is written: `wiki-url` exits 4 on a page that does not exist yet.
| Exit | Meaning | Action |
| --- | --- | --- |
| 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 |
| 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 待修項目 |
| 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 |
| 8 | Any other API failure | Same as 7: the old rows 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.
`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.
### 3.2 Hand off
State the four counts from 2.2's `summary` line: required items missing, settings invalid, CLIs unwired, domains behind. Recommend `/jsc-cli:setup` when any of those is above zero. Never fix anything here.
Done when either the wiki page URL is reported or the skipped write is reported together with its reason, **and** the four counts are stated with the recommendation given or explicitly withheld.
Done when each of the two pages is reported with its URL, or its skipped write is reported together with its reason, **and** the four counts are stated with the recommendation given or explicitly withheld.
+37 -6
View File
@@ -9,7 +9,14 @@ This skill writes. Every write is confirmed first, backed up, and verified after
## 1. Get the work list
Read the 待修項目 table from wiki `CHECK_{HASH}` — repo from `jsc-gitea/tools/gitea.sh wiki-repo CHECK`, page name from `gitea.sh hash-id "{hostname}/{user}"`.
Read the 待修項目 table from wiki `CHECK_{HASH}` — repo from `jsc-gitea/tools/gitea.sh wiki-repo CHECK`, page name from `gitea.sh hash-id "{host}/{user}"`, where `host` is the **short hostname** and `user` the login account, both taken from the machine:
```sh
host=$(hostname 2>/dev/null || uname -n 2>/dev/null || printf 'unknown'); host=${host%%.*}
user=${USER:-$(id -un 2>/dev/null || printf 'unknown')}
```
`${host%%.*}` matters: `hostname` prints the FQDN on some machines, and a hash built on the long name reads a page doctor never wrote. This is the same value doctor hashes, so it must be taken the same way.
No page, or `wiki-repo` exits 3, or any other non-zero exit from `wiki-repo`, `hash-id` or the wiki read → rebuild the list here. Rebuilding **MUST run as a sub agent**, and its three checkers **start together**: they read different files and share no state, so serialising them only triples the wait.
@@ -88,14 +95,38 @@ Done when every applied item has a fresh verdict from the checker its own row na
## 5. Record
Rewrite `CHECK_{HASH}` through `jsc-gitea:wiki` with the post-fix state, per `templates/check-page.md`. That page is a **content page** and keeps only the latest run, so this overwrites the pre-fix picture on purpose.
The two pages live in **two different wiki repos**. Resolve each one on its own.
`CHECK_CONTENTS` is a **contents page** and gets the opposite treatment: read it back first, then upsert this machine's row per `templates/check-contents.md` — add the row if missing, otherwise refresh its counts and 最後體檢. Never overwrite the whole page, and never touch another machine's row.
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.
The `CHECK_CONTENTS` read branches by exit code, and only exit 4 opens the create path. Exit 0 means upsert into the content that came back. Exit 4 means the page really is not there yet, so build it from the template. Exit 7 (key invalid or no permission) and exit 8 (any other API failure) mean the old rows are unknown, not that the page is missing: skip the `CHECK_CONTENTS` write, name the exit code, and create nothing — the overwrite that is correct for `CHECK_{HASH}` would here destroy every other machine's row, unread and unrecoverable.
`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
No wiki repo configured, or any non-zero exit from the wiki write → report the tables on screen, say the record was skipped, and name the exit code.
`jsc-gitea/tools/wiki-contents.sh upsert CHECK 4 "{HASH}" {row 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.
**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.
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.
Column 1 stays the human-facing link and is never the key. Build it as an **absolute URL** from `gitea.sh wiki-url {CHECK repo} CHECK_{HASH}`, fetched after `CHECK_{HASH}` is rewritten. `[[CHECK_{HASH}]]` resolves only inside its own wiki, and the two pages are no longer in the same one.
| Exit | Action |
| --- | --- |
| 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 |
| 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 |
| 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 |
| 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.
`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.
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.
Then state the counts: fixed, skipped, delegated, and 未修好. Recommend `/jsc-cli:doctor` for a clean re-check when anything was delegated.
Done when the page is written or the skip is reported, and the four counts are stated.
Done when each of the two pages is written or its skip is reported, and the four counts are stated.