feat(meta): 委派判定接進五支技能異動流程

上一輪建好了判準文件、清單與檢核腳本,但沒有任何一支技能會去用它。清單不接進流程,過幾天就跟實際技能脫節,回到手工盤點的老問題。

新增技能要走完決策樹五題加接續技能那一題,產出判定結果寫進清單,沒有那一列不算建立完成。修改技能動到流程或描述就重判,只改文案可沿用舊結論但要更新版本號。刪除技能要刪掉那一列。一次改多支要逐支重判,一支都不能跳。例行稽核把清單一致性排進第一組檢查。

檢核腳本的四個結束碼在五支裡逐一路由。特別寫清楚「回 0 但帶提示」那一種:版本落後與待複核的種入列都回 0,那是提示不是缺失,不能因為看到輸出就判成失敗。版本號是 domain 層級,改一支會標到整個 domain,當成缺失看每次發版整片紅,提示很快就沒人看。

接線時撞到四個原本沒看到的問題,一併處理:

清單放在技能組的中樞存放庫,但改的技能常在別的存放庫,所以四支異動技能各加一條「不在中樞時另開一條清單 PR」,並把「技能 PR 開了、清單 PR 沒開」列進部分完成。不加的話清單改動沒有落地路徑。

一次改多支那一支是平行處理,每個 sub agent 改自己的存放庫。那個設計在各改各的檔案時正確,一加入全技能組共用的單一清單就變成資料競爭。改成 sub agent 只回傳判定列,主 agent 收齊後一次併檔。

技能改名或刪除時,別列指過來的接續欄會變成指向不存在的技能,那正是檢核腳本會抓出來的一種缺失。修改與刪除兩支都加了連動處理。

刪除那一支的參照盤點會撈到清單那一列,盤點步驟與刪除步驟都可能去改它。明寫留給刪除步驟,盤點步驟的完成條件多一種合法結論。

清單十一欄沒有備註欄,多寫一欄會被檢核擋下,所以「沿用前一輪判定」寫進 PR 描述與異動報告,列上只動版本號。

刪除技能還要「移除待辦簿裡引用它的內建項」,但待辦簿本身還不存在,那一半據實寫成尚未接線,並要求帶進異動報告,免得日後被讀成已經清乾淨。

委派清單沒有加進審核檢查清單。那份清單每一項都是逐 domain 判定,委派清單是整輪一份、只存在於中樞存放庫;加進去會讓每個 domain 的 sub agent 各判一次同一個檔。改成在例行稽核裡明寫它不是那幾項之一。
This commit is contained in:
2026-09-03 14:43:15 +08:00
parent 737e3560f3
commit 1a34529231
6 changed files with 107 additions and 55 deletions
+16 -6
View File
@@ -13,13 +13,23 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
2. Run `tools/list-skills.sh` and present its `domain / name / description` rows to the user. The tool prints skills, not domains, so read the domain column to prove coverage. Exit 1 means the root could not be derived, the domain list was unreadable, or no skill was found — read stderr, fix the named cause (`JSC_PLUGINS_ROOT` for the root case, as in step 1) and rerun; never read it as an empty skill set. Completion condition: the script exits 0 and every domain printed by step 1 appears in at least one row; a domain with no row means its repo is missing or holds no skill — return to step 1 for that domain.
3. Let the user pick the skill to update. Completion condition: one `{domain}/{name}` pair is confirmed.
4. Ask for update details via the `jsc-ask:ask` decision tree (change the goal? the trigger? the flow? move rules down to a hook or a tool?). Every option states its impact scope (example: renaming breaks the existing invocation command). Completion condition: every question has a recorded answer.
5. Update the skill — the modification part MUST run as a sub agent: modify SKILL.md and related files. In the same pass, update this skill's `## {name}` section in `references/behaviors.md` so its five rows — 觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象 — describe the new behavior. A renamed skill gets its section renamed and moved back into dictionary order. The behavior list ships in this same PR: a behavior change that lands without its section makes the domain's list wrong from the merge onward, and the next audit reports drift this step created. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the skill files carry the change, the skill's `references/behaviors.md` section states the new behavior with all five rows filled, and all three manifests show the same new version.
6. Check every item of the guidelines.md audit checklist. Run `tools/check-behaviors.sh {domain-path}` for the behavior-list item instead of comparing by eye, and route each exit code: 0 — the list matches `skills/` and all five rows are filled; 1 — every mismatch is printed on stderr as `{檔案}:{技能名}:{說明}`, so fix each one and rerun; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found, so create the missing file and rerun. **Exit 3 is never a pass.** On any failure, **return to step 4**: ask again and fix, until all items pass. Completion condition: every checklist item passes and `tools/check-behaviors.sh {domain-path}` exits 0.
7. Call `jsc-git:pr` to open a Push Request. Completion condition: a PR URL comes back and is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md).
Settle the delegation verdict in the same tree, before any file is touched. A change that touches the **flow** or the **`description`** re-runs the whole decision tree of [`../../references/delegate-criteria.md`](../../references/delegate-criteria.md) — all five questions plus the `next` question — and produces a fresh verdict. Skipping that leaves a skill that just turned from read-only into file-writing sitting on its old verdict, and the background assistant keeps triggering it on a description of behavior it no longer has. A change that only rewrites wording and touches no behavior may keep the recorded verdict; then step 5 moves the row's `version` alone and the reuse is stated in the report, never left silent. Every option states its impact scope, this one included: reusing a verdict wrongly is the one failure this flow cannot detect later, because the row still looks complete. Completion condition: the run holds either a fresh verdict with a value in every column `delegate-criteria.md` marks mandatory for it, or a recorded decision to reuse the existing verdict together with the reason it changed no behavior.
5. Update the skill — the modification part MUST run as a sub agent: modify SKILL.md and related files. In the same pass, update this skill's `## {name}` section in `references/behaviors.md` so its five rows — 觸發時機、關鍵步驟、外部呼叫、完成條件、可驗證跡象 — describe the new behavior. A renamed skill gets its section renamed and moved back into dictionary order. In the same pass, update this skill's row in `jsc-meta/tools/delegate-spec.tsv` from the step 4 answer: a re-judged skill has every column rewritten from the fresh verdict with `origin` set to `judged`; a reused verdict keeps its columns and its `origin` untouched. Either way the `version` column moves to the version the manifests carry after the `sync-skill-manifest.sh` run below — a row left on the old version reads as never revisited, and the next audit reports it as pending re-judgement. The reuse itself is **not** recorded in the row: the eleven columns hold no note column and a twelfth column fails the checker, so state it in the PR description and in the step 8.2 wiki section as 「沿用前一輪判定」 with the date that judgement was made. A renamed skill also has its row's `name` column renamed, and every other row whose `next` named the old name is repointed in the same edit — those rows now name a skill that cannot be called, and the assistant retries such a name instead of pausing on it. The file lives in `plugins/meta` whichever domain owns the skill, so updating a skill outside `meta` changes two repos. The behavior list ships in this same PR: a behavior change that lands without its section makes the domain's list wrong from the merge onward, and the next audit reports drift this step created. Then run `tools/sync-skill-manifest.sh {domain-path}` directly (no sub agent needed) to sync the domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path, `skills/`, `README.md`, the `JSC-SKILLS` markers, a `SKILL.md`, a manifest, or a manifest `version` field is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs under `set -e`, so treat it as an environment fault and stop, never as a successful sync. Completion condition: the skill files carry the change, the skill's `references/behaviors.md` section states the new behavior with all five rows filled, its `tools/delegate-spec.tsv` row carries the fresh verdict or the reused one with a moved `version`, and all three manifests show the same new version.
6. Check every item of the guidelines.md audit checklist. Run `tools/check-behaviors.sh {domain-path}` for the behavior-list item instead of comparing by eye, and route each exit code: 0 — the list matches `skills/` and all five rows are filled; 1 — every mismatch is printed on stderr as `{檔案}:{技能名}:{說明}`, so fix each one and rerun; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, because `references/behaviors.md` is missing, `skills/` is missing, or no `SKILL.md` was found, so create the missing file and rerun. **Exit 3 is never a pass.**
Then run `tools/check-delegate.sh` for the delegation-list item. It takes the plugins root, not a domain path, and the list is one file covering every domain, so it runs **once for the whole flow**. Route each exit code:
- 0 — the list matches the skills on this machine and every mandatory column is filled. **A run that printed lines on stdout and exited 0 still passed.** Those lines are hints, not defects: `origin=seed` marks a row seeded from the earlier inventory and awaiting review, and a version-behind line marks a row whose `version` trails its domain's current one. The version number is per domain, so bumping one skill's domain marks every other skill in it — reading those lines as failures paints the whole domain red on every release until nobody reads them at all. Report the hints and treat this item as passed. The one hint worth acting on here is a version-behind line naming **the skill this run just changed**: that row's `version` was supposed to move in step 5, so go back and move it.
- 1 — a missing row, a duplicate row, a row for a skill this machine does not have, an empty column, a column value outside its vocabulary, or a `next` pointing at a skill that does not exist. Every one is printed on stderr as `{清單路徑}:{domain}/{技能名}:{說明}`; fix each and rerun. A rename that left the old name behind lands here twice — once as a stale row, once as another row's dead `next`.
- 2 — usage error: the script takes at most one argument. Fix the call and rerun.
- 3 — nothing was checked, because `tools/delegate-spec.tsv` is missing, the root could not be derived, or `list-skills.sh` listed no skill. Read stderr and fix the named cause; set `JSC_PLUGINS_ROOT` to the directory holding the domain repos for the root case, as in step 1. **Exit 3 is never a pass.**
On any failure, **return to step 4**: ask again and fix, until all items pass. Completion condition: every checklist item passes, `tools/check-behaviors.sh {domain-path}` exits 0, and `tools/check-delegate.sh` exits 0 with its hint lines reported as hints.
7. Call `jsc-git:pr` to open a Push Request. When the updated skill lives in a domain other than `meta`, the `tools/delegate-spec.tsv` row is a change to `plugins/meta` and needs its own Push Request against that repo — two repos changed, two PRs, neither waiting on the other. Completion condition: a PR URL comes back for every repo this run changed, `plugins/meta` included when the row landed there, and each is reported with the table format in [`../../references/pr-report.md`](../../references/pr-report.md).
8. Deploy the update, verify it runs, then report:
1. Follow [`../../references/deploy-verify.md`](../../references/deploy-verify.md) from section 1 to section 5: `tools/deploy-route.sh {domain-path}` picks the route, the deploy route or the worktree route runs, and the verification then runs in a **fresh CLI process**, never in the session that ran the deploy. That session raised the restart gate itself and still holds the old skill body, so verifying inside it either gets blocked or passes on stale behavior. Verify the updated `description` in the skill's `tools/list-skills.sh` row, every tool this change touched, and one minimal prompt per checkable CLI — the per-CLI prompts run in parallel. Completion condition: every completion condition in `deploy-verify.md` sections 1 to 5 holds for this domain repo.
2. Write the change report to the wiki — this part MUST run as a sub agent. It is two pages in two repos, and they must not be mixed up.
- **Content page `SKILLSET_{HASH}`.** Resolve its repo with `jsc-gitea/tools/gitea.sh wiki-repo SKILLSET`, which reads `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. `{HASH}` is `gitea.sh hash-id "{owner}/{repo}"` of the changed domain repo, used at the full 40 characters it prints. Write it through `jsc-gitea:wiki` following [`../../templates/skillset-page.md`](../../templates/skillset-page.md): **append** a section for this change — date, 「更新」, skill name, changed files, PR URL, the step 8.1 route verdict and verification result per item — and keep every earlier section.
- **Content page `SKILLSET_{HASH}`.** Resolve its repo with `jsc-gitea/tools/gitea.sh wiki-repo SKILLSET`, which reads `JSC_WIKI_REPO_SKILLSET` first, then `JSC_WIKI_REPO`. `{HASH}` is `gitea.sh hash-id "{owner}/{repo}"` of the changed domain repo, used at the full 40 characters it prints. Write it through `jsc-gitea:wiki` following [`../../templates/skillset-page.md`](../../templates/skillset-page.md): **append** a section for this change — date, 「更新」, skill name, changed files, PR URL, the step 8.1 route verdict and verification result per item, and the delegation verdict this run recorded: the fresh verdict with its columns, or 「沿用前一輪判定」 with the date of the judgement being reused. The row itself has no room for that note, so this section is the only place it is kept — and keep every earlier section.
- **Directory page `SKILLSET_CONTENTS`.** It lives in the CONTENTS repo, never in the SKILLSET one. `wiki-contents.sh` resolves it itself with `gitea.sh wiki-repo CONTENTS`, whose chain is `JSC_WIKI_REPO_CONTENTS` then `JSC_WIKI_REPO` and never falls back to `JSC_WIKI_REPO_SKILLSET`. That page is a heading-plus-bullets list and holds no markdown table: one `## SKILLSET_{HASH}` block per domain repo, every field one `- {欄位名}:{值}` line under it. Build one file holding this domain's single block, following [`../../templates/skillset-contents.md`](../../templates/skillset-contents.md), with its 異動頁 bullet written as `[SKILLSET_{HASH}]({url})` from the **absolute** URL that `gitea.sh wiki-url {SKILLSET repo} SKILLSET_{HASH}` prints. The H2 heading itself carries no link, no URL, no affix and no date — only the content page name. Every link on both pages takes that `[{text}]({url})` form; the double-bracket wiki-link form resolves only inside one wiki, so it is never used. Then run:
`jsc-gitea/tools/wiki-contents.sh upsert SKILLSET 2 "SKILLSET_{HASH}" {entry file} templates/skillset-contents.md`
@@ -63,10 +73,10 @@ Single source of guidelines: [`../../references/guidelines.md`](../../references
| status | Use it when |
| --- | --- |
| `ok` | the skill files and the behavior-list section carry the change, the checklist passes, the PR is open, `deploy-verify.md` sections 1 to 5 hold, and both wiki writes exited 0 |
| `ok` | the skill files, the behavior-list section and the `delegate-spec.tsv` row carry the change, the checklist passes, every PR this run needed is open, `deploy-verify.md` sections 1 to 5 hold, and both wiki writes exited 0 |
| `blocked` | a gate or a missing prerequisite stopped the run before any file changed — `sync-domains.sh` never reached exit 0, or no skill could be listed to pick from |
| `failed` | the run broke mid-way — the step 6 checklist loop kept failing, or a wiki write failed again after its one retry |
| `degraded` | the update landed with a part missing — the content page was written while its `SKILLSET_CONTENTS` block was not, or a CLI could not be verified and the reason was recorded |
| `degraded` | the update landed with a part missing — the content page was written while its `SKILLSET_CONTENTS` block was not, the skill's PR is open while the `delegate-spec.tsv` PR is not, or a CLI could not be verified and the reason was recorded |
| `aborted` | the user stopped the run, or a prerequisite turned out not to hold and this skill stopped on its own |
`{exit code}` is this run's own result as a number: `0` for `ok`, non-zero otherwise. `detail` is optional, one line, at most 200 characters.