三份 manifest 取分支上的較高版號:develop 那邊到 0.3.6,這條分支本來就是為了 讓開那一號才升到 0.3.7,取低的等於把版號往回退。 行為清單第 10 行兩邊各改了同一行的不同地方,合起來留:develop 加的是委派 清單檢核多判一種填錯的 probe、以及修法要回該技能的存取庫核對過再改寫;這條 分支加的是第一組多跑一項腳本路徑檢查。兩件事互不相干,取任一邊都會弄丟另 一邊。外部呼叫那一行 develop 沒動過,直接取分支上的。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
43 KiB
name, description
| name | description |
|---|---|
| skill-check | Routine compliance, script, hook, flow-efficiency, and cost-efficiency audit of the whole jsc skill set with no change request in hand. Sync every domain repo from the Gitea canonical marketplace, then run three parallel groups - lint-scripts.sh plus lint-frontmatter.sh plus check-behaviors.sh plus ste100-lint.sh plus check-link-format.sh plus check-wiki-rules.sh plus check-page-name.sh plus check-delegate.sh plus check-skill-paths.sh plus hook smoke, the guidelines.md checklist audit, and an optimization review that first reads each domain's SKILLSET_{HASH} so suggestions already applied or deferred are never re-scanned or re-asked, then covers parallelism, tool extraction, repeated interaction, redundant checks, misplaced gates, and avoidable token, sub-agent, API, scan, or interaction cost. Confirm compliance fixes and optimization suggestions before applying them, recording each decision with its date, re-check until accepted fixes pass, then open a PR per affected repo via jsc-git pr. Close by appending the round's result to every changed domain's SKILLSET_{HASH} and its SKILLSET_CONTENTS block, or to the plugins/meta page when no domain was changed. Use for periodic or on-demand skill-set checks; not for applying a change request (use skillset-update) or editing one skill (use skill-update). |
skill-check — audit compliance, flow efficiency, and cost efficiency
Single source of guidelines: ../../references/guidelines.md.
Flow
-
Run
tools/sync-domains.shto sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints onedomain<TAB>pathline per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned. Exit 1 means the root could not be derived,gitea.shwas not found, or the canonical marketplace was unreadable; when stderr says the root could not be derived, setJSC_PLUGINS_ROOTto the directory that holds the domain repos and rerun, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there instead of the domain workspace. Resolve 2 and 1 before continuing.The three review groups of step 2 all read this synced tree, so the sync finishes first.
-
Run the three review groups over the synced repos. They are independent — every one only reads, none writes a file — so launch all three in parallel and merge their results in step 3.
Group 1 — validate scripts, frontmatter, behavior lists, language, wiki rules, page names, the delegation list, and hooks.
-
For every synced domain repo, run
tools/lint-scripts.sh {domain-path}. One run per domain, and the runs go in parallel — no domain's verdict depends on another's. The tool covers three checks in one pass:sh -nsyntax, executable bit, and an exit-code declaration in the file header. Route each exit code: 0 — the domain's scripts pass all three; 1 — the failing items are printed as{file}:{check}:{detail}, so report each one; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the path is missing or the domain has neithertools/norhooks/. Record exit 3 as 「無腳本可掃」; a domain with no script directory is not a failure, but exit 3 is never a pass. -
For every synced domain repo, run
tools/lint-frontmatter.sh {domain-path}. One run per domain, and the runs go in parallel alongside thelint-scripts.shruns. It parses the frontmatter of everyskills/*/SKILL.mdwithout a YAML library — paired---delimiters, the requirednameanddescriptionkeys, unquoted scalars carrying a colon-space or ending in a colon, unquoted scalars opening with&,*,!,|,>,%,@or a backtick, and quoted scalars that never close. Route each exit code: 0 — every SKILL.md in that domain parses; 1 — the failures are printed on stderr as{檔案}:{鍵}:{說明}, so report every one as a compliance failure with the file and key it belongs to; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the domain path orskills/is missing, orskills/holds noSKILL.md. Record exit 3 as 「無 frontmatter 可掃」with the cause from stderr and carry it into the step 3 merge; exit 3 is never a pass. This check exists because a broken frontmatter makes Antigravity drop the whole skill with no error message at all — 34 skills on disk loaded as 28, and only a file-by-file comparison found it. -
For every synced domain repo, run
tools/check-behaviors.sh {domain-path}. One run per domain, and the runs go in parallel alongside thelint-scripts.shruns — no domain's verdict depends on another's. It comparesreferences/behaviors.mdagainstskills/: section per skill, dictionary order, one table per section, five rows, no empty content cell. Route each exit code: 0 — that domain's behavior list matches; 1 — the mismatches are printed on stderr as{檔案}:{技能名}:{說明}, so report every one as a compliance failure with the skill it belongs to; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, becausereferences/behaviors.mdis missing,skills/is missing, or noSKILL.mdwas found. Record exit 3 as 「無清單可查」with the cause from stderr and carry it into the step 3 merge; a domain with no behavior list is a compliance failure, and exit 3 is never a pass. -
For every synced domain repo, run
tools/ste100-lint.sh {domain-path}. One run per domain, and the runs go in parallel alongside the other group 1 runs. Route each exit code: 0 — every scanned file in that domain passes; 1 — the hits are printed on stdout as{檔案}:{行號}:{類別}:{命中內容}, so report every one as a compliance failure with the category it belongs to, after checking the two documented false-positive classes (a path or branch name holding a slash between Chinese characters, and English items listed with a slash); 2 — no scan target was given, which is a caller defect here, so fix the argument and rerun. The tool has no exit 3: an empty run returns 2 rather than a silent pass. This check lives in group 1 because the audit checklist demands 「tools/ste100-lint.sh對該 domain 全綠」 for every domain — leaving it to the group 2 sub agents meant ten agents each ran it their own way, and the main agent never held one comparable verdict per domain. -
For every synced domain repo, run
tools/check-link-format.sh {domain-path}. One run per domain, and the runs go in parallel alongside the other group 1 runs. It reads every*.mdof that domain and reports any link still written in the double-bracket wiki-link form, which resolves only inside one wiki and dead-links everywhere else. Route each exit code: 0 — every link in that domain is written as[{text}]({url}); 1 — the hits are printed on stdout as{檔案}:{行號}:{命中內容}, so report every one as a compliance failure; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the domain path is missing or holds no*.md. Record exit 3 as 「無文件可掃」with the cause from stderr; it is never a pass. The tool strips inline code and fenced code blocks before judging, so a document that quotes the forbidden form while explaining it, and a shell test expression inside an example, both stay clean. -
Run the two wiki-rule checkers once each, not per domain — both judge shared rules, so a second run adds nothing:
jsc-gitea/tools/check-wiki-rules.sh, which verifies wiki repo resolution and thehash-idrule for every page type. It takes no argument. Route each exit code: 0 — printedOKon stdout, every item passed; 1 — the first mismatch is printed on stderr as{項目}: want=… got=…and the script stops there, so report that item and rerun after the fix, because the remaining items were never reached. Those are its only two codes. Until this audit, no flow in the whole repository ever called it.tools/check-page-name.sh {root}, where{root}is the directory holding the domain repos — the parent directory of the pathstools/sync-domains.shprinted in step 1, so no extra derivation is needed. It compares the page-name pattern in its three copies:jsc-gitea/tools/page-name.sh(the canonical one),jsc-hooks/hooks/comment-scope.shandjsc-log/tools/worklog-pending.sh. Route each exit code: 0 — the three agree; 1 — the mismatches are printed on stderr as{檔案}:{說明}, so report each one as a compliance failure, and a copy that could not be found is one of those lines; 2 — usage error, the tool takes exactly one argument; 3 — none of the three copies was found, so the root is wrong: fix it and rerun. Record exit 3 as 「什麼都沒查」; it is never a pass. The three copies stay separate on purpose — a hook must be self-contained and may not depend on another plugin's path at run time — so consistency is checked here instead of shared in a function.
-
Run
tools/check-delegate.sh {root}once for the whole round, with the same{root}item 6 passed tocheck-page-name.sh. It compares the delegation listtools/delegate-spec.tsvagainst the skillstools/list-skills.shfinds on this machine: one skill one row, twelve columns, every mandatory column filled, everynextnaming a skill that exists, and everyprobeeither a runnable read-only command, apending:{reason}, or a-on the rows that take one. It belongs in group 1 for the same reasonste100-lint.shdoes — it is a deterministic script verdict, and it is judged once for the whole round rather than per domain, because the list is a single file covering every domain. Handing it to the group 2 sub agents would have ten agents run the same script over the same file and report ten copies of the same lines, with no single verdict anywhere; handing it to group 3 would turn a pass-or-fail check into a suggestion. Route each exit code:- 0 — the list and the machine's skills correspond one to one and every mandatory column is filled. A run that printed lines on stdout and exited 0 passed. Those lines are hints, not compliance failures, and they are printed on stdout precisely so they are told apart from the failures on stderr:
origin=seedmarks a row seeded from the earlier inventory that has not been through the decision tree yet, a version-behind line marks a row whose recordedversiontrails its domain's current one, aprobe=pending:line marks a delegated slice whose read-only entry point is not wired yet, and a line saying aprobedomain is not installed here marks a script this machine cannot check. The version number is per domain, so one skill's change marks every other skill of that domain — counting those as failures paints whole domains red on every release, and the hint stops being read at all. Report the hint count and the rows, and open no decision-tree item for them. - 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, a
nextnaming a skill that does not exist, or aprobein the wrong shape — a command on a row whosewayholdsinvoke, a-on a row whosewayholds onlypatrolorremind, a dollar sign or tilde, an unknown substitution point, or a script that does not exist. Every one is printed on stderr as{清單路徑}:{domain}/{技能名}:{說明}. Report each as a compliance failure, named by the skill it belongs to. A missing row means the assistant is blind to that skill; an extra row means it will trigger a skill that cannot be called, and a failing trigger retries instead of pausing. A wrongprobefails every unattended round in the same silent way, and the command-on-an-invoke-row case is worse than a failure: the assistant runs a bare script where the whole skill was supposed to run, and the round looks clean. - 2 — usage error: the script takes at most one argument. Fix the call and rerun; this is a defect in this skill, not a finding about the skill set.
- 3 — nothing was checked, because
tools/delegate-spec.tsvis missing, the root could not be derived, orlist-skills.shlisted no skill. Record it as 「無委派清單可查」 with the cause from stderr and carry it into the step 3 merge; exit 3 is never a pass, because a check that read nothing reports neither a missing row nor an extra one.
This verdict is not one of the guidelines.md audit-checklist items, so it stays out of the nine that step 3 merges into every domain's checklist and is reported on its own line, one line for the round.
- 0 — the list and the machine's skills correspond one to one and every mandatory column is filled. A run that printed lines on stdout and exited 0 passed. Those lines are hints, not compliance failures, and they are printed on stdout precisely so they are told apart from the failures on stderr:
-
For every synced domain repo, run
tools/check-skill-paths.sh {domain-path}. One run per domain, and the runs go in parallel alongside the other group 1 per-domain runs. It reads everyskills/*/SKILL.mdandreferences/*.mdand resolves eachtools/…orhooks/…script path written in them.lint-scripts.shproves a script exists inside its own repo; this one proves the path as written reaches it. Route each exit code: 0 — no path in that domain points at a file that is not there, and the hint lines it printed are counted separately from failures; 1 — themissinglines name paths that resolve to nothing, so report each as a compliance failure with its file and line; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the domain path is missing or it has neitherskills/*/SKILL.mdnorreferences/*.md. Record exit 3 as 「無文件可掃」; exit 3 is never a pass. Itsunrootedandunknownlines are hints, never failures —unrootedmarks a path with no plugin directory name, which resolves against whatever the current directory happens to be, andunknownmarks a cross-domain path whose repo is not on this machine. Carry the two hint counts into the report without turning them into decision-tree items: the whole skill set carries hundreds ofunrootedpaths, and promoting them to failures would turn every domain red at once, which reads the same as no report at all. This check exists because item 9 below was already supposed to catch this and could not: a human reading item 9 checks that the named script exists intools/, which it does, and never asks whether the path as written reaches it — so the same defect passed review every round until a path exited 127 in front of someone. This verdict is not one of the guidelines.md audit-checklist items either, so it stays out of the nine that step 3 merges into every domain's checklist and is reported on its own line, one line per domain. -
For every shell script directly named by a SKILL.md, confirm the skill routes every exit code the script's header declares.
lint-scripts.shproves the script exists and declares its codes; this check is the other half — that the caller branches on each of them. Report evidence asskill file:line -> script path. -
When the
jsc-hooksdomain is present, runjsc-hooks/tools/wire-cli.sh smoke {cli}for every CLI reported byjsc-cli/tools/detect-clis.sh; the per-CLI smokes run in parallel. When no CLI is detected, runjsc-hooks/tools/wire-cli.sh smoke codexas the minimum hook behavior check and label it 「預設 hook smoke」 in the report. Usesmoke, notpurgeor rewiring actions, and setJSC_READONLY=1for the whole audit so a mistyped sub-command is refused in code (exit 6) instead of rewiring the machine;statusandsmokeare unaffected by that variable. Route eachsmokeexit code: 0 — the run passed its own assertions; 2 — usage error, so fix the CLI code and rerun; 4 — the smoke failed, which includes the script's own result-line count not matching what it expected. Read the count from the script'slines<TAB>{數量}output line; never write the number into this skill. The script counts its own result lines and asserts them, so a hardcoded number here goes stale the moment a hook or a decision path is added — an out-of-date count in a SKILL.md is exactly what misled the previous audit. -
When a hook or script smoke fails, route it as a compliance failure with script name, exit code, output summary, and proposed fix. Do not continue to report the affected hook as compliant.
Group 2 — audit every skill of every domain against the guidelines.md audit checklist. This group MUST run as a sub agent, one sub agent per domain repo, and those sub agents run in parallel. Each sub agent reports its findings: skill, failed checklist item, evidence (file:line), proposed fix. Cover the checklist's four flow checks by name, not only the naming and language items:
- Every step number, file path and section title the skill references — inside itself and in other files — really exists (the pointer points at something).
- Every step ends in a checkable completion condition, with no vague wording.
- Every external call (script, API, other skill) states what to do on failure and routes every exit code.
- No gate the skill installs blocks the only path that lifts that gate.
Nine checklist items are already decided by group 1 and must not be re-run here:
sh -non everytools/andhooks/script, script existence with the executable bit, the hook smoke, thereferences/behaviors.mdmatch, thelint-frontmatter.shverdict, theste100-lint.shverdict, the link-format verdict fromtools/check-link-format.sh, the page-name-pattern verdict fromtools/check-page-name.sh, and the wiki repo resolution plushash-idverdict fromjsc-gitea/tools/check-wiki-rules.sh. Tell each sub agent to skip those nine and leave them blank; the main agent fills them in from the group 1 verdicts when merging in step 3. The link-checklist item has a second half no script can judge — that every link went throughjsc-gitea/tools/link-check.shbefore it was written — so each sub agent still audits that half from the skill text. The last two are one verdict for the whole round, not one per domain — group 1 runs each of those two checkers once, because both judge rules the domains share — so the merge writes that single verdict into every domain's checklist. Re-scanning the same files in every domain sub agent buys nothing — group 1 already scanned them all, with the same tools, on the same synced tree.Group 3 — a flow and cost optimization review, kept separate from the compliance audit.
Read the previous round's decisions before scanning anything. For every synced domain, resolve
jsc-gitea/tools/gitea.sh wiki-repo SKILLSET, compute the page name asSKILLSET_plusgitea.sh hash-id "{owner}/{repo}"of that domain repo, and read it throughjsc-gitea:wiki. Take the 優化建議 table of everyskill-checksection on that page: an entry whose 決議 column reads套用or延後is settled — do not scan for it again, and do not put it back into the step 3 decision tree. Only a genuinely new finding, or an entry whose recorded 決議 is自訂with the custom fix not yet in place, reaches step 3.Route every exit code of all three calls, not the read alone:
Call Exit Do gitea.sh wiki-repo SKILLSET0 The repo is in hand; go on to hash-id2 The page type was misspelled here, which is a defect in this skill and not a user setting. Fix the argument and rerun 3 Neither JSC_WIKI_REPO_SKILLSETnorJSC_WIKI_REPOis set. Name both variables, ask per thejsc-ask:askrules, then rerun. No answer means no settled list for that domain, per the rule belowgitea.sh hash-id "{owner}/{repo}"0 Use the 40 characters it printed as the page-name suffix, unshortened 1 No SHA-1 helper on this machine. Report that sha1sumorshasumhas to be installed, and never hand-compute the hash2 Empty input, so the {owner}/{repo}was never resolved. Resolve it and rerunjsc-gitea:wikiread0 The settled list is in hand 4 The page does not exist yet, so there is no previous round. Carry on with an empty settled list — the normal state for a domain audited the first time 7 The token is invalid or lacks permission 8 Some other API failure A 7, an 8, an unanswered 3, or a stopped 1 or 2 ends group 3 for that domain, and nothing else. An unread page cannot be told apart from an empty one, and treating it as empty re-asks every question the user already answered. So mark that domain 「本輪未取得已決議清單,優化建議暫不提出」, report the exit code that caused it, and run the rest of the round unchanged: group 1 and group 2 read no wiki at all, and steps 3 to 8 still apply and record every compliance fix. Stopping the whole round on a wiki failure would switch off the one routine compliance audit this skill set has — including the audit that finds a broken wiki setting.
This read exists because the audit was re-scanning and re-asking every accepted or deferred suggestion on every run — exactly the 「重複來回」 that aspect 3 below is supposed to catch, committed by the skill that defines it.
Each aspect MUST run as a sub agent, and the six aspects run in parallel with each other and with groups 1 and 2. Every sub agent gets that domain's settled list up front:
Aspect Scope 1 Parallelism Steps that run in series today but have no data dependency and can run in parallel 2 Tool extraction SKILL.md text flows with clear inputs and outputs that should move to tools/, including hook-enforceable rules that still rely on prompts3 Repeated interaction The same user question, repository fact, wiki page, API result, or file content being collected more than once 4 Redundant checks Completion conditions or verification steps that overlap, or a later step that necessarily covers an earlier check 5 Gate timing Gates that run too early or too late, causing wasted work before a block or blocking the only path that clears the gate 6 Cost efficiency Avoidable token, sub-agent, API, file-scan, full-repo audit, or user-interaction cost that can be reduced without weakening correctness Each optimization finding reports skill, aspect, evidence (file:line), current flow step count, proposed flow step count, what time or interaction it saves, what cost it saves, current cost driver, proposed cost driver, whether correctness decreases, which protection would be weakened if any, the 決議 (
套用,延後or自訂) recorded in step 3, and the 決議日期 that decision was made. The last two fields start empty and are filled in by step 3; they are what step 8 writes to the wiki and what the next round reads back, so a finding that reaches step 8 with either field empty is unfinished, not optional. Cost savings may be token volume, sub-agent count, API calls, file scans, full-repo audits, or user prompts. Keep optimization findings separate from compliance failures.Completion condition for all three groups: every domain has a
lint-scripts.shverdict, alint-frontmatter.shverdict, acheck-behaviors.shverdict, anste100-lint.shverdict, acheck-link-format.shverdict and acheck-skill-paths.shverdict — the last one carrying itsunrootedandunknownhint counts separately from its failures —check-wiki-rules.sh,check-page-name.shandcheck-delegate.sheach have one verdict for the whole run —check-delegate.shcarrying its hint lines separately from its failures, or 「無委派清單可查」 where it exited 3 — every script named by a SKILL.md has an exit-code-routing verdict, and every smoked CLI has asmokeexit code plus thelinesvalue the script printed for it; every domain has a group 2 audit result that names a verdict for all checklist items — the four flow checks included, and the nine group 1 items left blank for the step 3 merge rather than re-scanned; and every one of the six aspects has returned a verdict for every domain whose settled list was read, 「無發現」 where an aspect found nothing and every settled entry of that domain excluded rather than re-reported — a domain whose pre-read failed carries 「本輪未取得已決議清單,優化建議暫不提出」 instead, and that sentence is a complete group 3 result for it. -
-
Merge the three groups, then present compliance failures and optimization findings separately via the
jsc-ask:askdecision tree. Merging means one thing in code: fill the nine skipped checklist items of every group 2 sub agent report from the matching group 1 verdicts, so each domain ends with one complete checklist and no item counted twice. Seven of the nine are per-domain verdicts, one domain to one item. The other two —check-page-name.shandcheck-wiki-rules.sh— are judged once for the whole round, and that one verdict goes into that same item of every domain's checklist; re-judging a whole-round item per domain is precisely the double counting this merge exists to stop. A domain that group 3 marked 「本輪未取得已決議清單,優化建議暫不提出」 still gets its full compliance checklist here; only its optimization findings are missing, and the merge report says so.- Compliance failure options: apply the proposed fix / skip / custom fix. Every option states its impact scope, for example skipping leaves the skill non-compliant until the next audit.
- The
check-delegate.shfailures join that same set, one decision-tree item per reported row, and they carry one extra note in their impact scope: fixing a missing row means running the delegation decision tree of../../references/delegate-criteria.mdfor that skill in step 4, which is more questions than most fixes. Its hint lines never become decision-tree items — a hint is a note about a row that is already there, and turning it into a question re-asks a settled judgement every round, which is the 「重複來回」 group 3 exists to catch. - Optimization options: apply / defer / custom. Record the chosen option in the finding's 決議 field as
套用,延後or自訂, and today's date in 決議日期. Any suggestion that weakens a protection must name the protection it removes and must not be applied unless the user explicitly accepts that tradeoff. Cost optimization may move, merge, cache, or narrow checks; it must not delete a compliance check only because it is expensive.
Completion condition: every domain's checklist is complete after the merge, with the two whole-round verdicts carrying the same value in every domain, and every compliance failure and every optimization finding has a recorded decision — every optimization finding carrying both 決議 and 決議日期.
-
Apply the confirmed fixes and accepted optimizations — the file-change part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents run in parallel: each repo's files are independent. A fix that changes a skill's behavior also updates that skill's
## {name}section in the same repo'sreferences/behaviors.md, in the same pass, so the fix and the behavior list land in one PR. A confirmedcheck-delegate.shfix is written by the main agent, never by the per-repo sub agents:tools/delegate-spec.tsvis one file for the whole skill set, and parallel agents writing one file overwrite each other's rows. A missing row is filled by running the decision tree of../../references/delegate-criteria.mdfor that skill throughjsc-ask:askand writing the answer as a row withoriginset tojudged; an extra row is deleted; a deadnextis repointed at a skill that exists; a wrongprobeis rewritten per that same file — verified against the owning repo, not guessed — and set topending:{reason}when the slice has no read-only entry point on this machine. A fix that changed a skill's behavior in this same round also re-judges that skill and moves its row'sversion. Then runtools/sync-skill-manifest.sh {domain-path}directly (no sub agent needed) for each affected domain repo to refresh that 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, theJSC-SKILLSmarkers, aSKILL.md, a manifest, or a manifestversionfield 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 underset -e, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected repo carries the changes, the matchingreferences/behaviors.mdupdate for every fix that changed a skill's behavior, thetools/delegate-spec.tsvrows for every accepted delegation fix, and the manifest bump. -
Sync the canonical marketplace — a required step, never optional. The canonical pair lives in
plugins/metaand every domain repo carries a byte-identical copy, so a fix that leaves the copies apart makes some repos register a stale plugin set. Runtools/sync-marketplace.sh {domain} {repo-url} {description}once with an existing entry's own current values (rewriting the same entry is idempotent); the script rewrites both canonical files and copies them into every domain repo. Route each exit code:- Exit 3 — written, but some domain repo is not present locally. Run
tools/sync-domains.sh, then rerun this step. - Exit 2 — usage error: the script takes exactly three arguments. Fix them and rerun.
- Exit 1 — the root could not be derived, python3 is missing, a canonical file was unreadable, or copies differ byte for byte. Read stderr and fix the named cause: install python3 for the second; for the root case set
JSC_PLUGINS_ROOTto the directory that holds the domain repos, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there. Then rerun. - Exit 0 — every copy holds identical bytes; the script verifies that itself.
Completion condition: the script exits 0 and prints the touched paths.
- Exit 3 — written, but some domain repo is not present locally. Run
-
Re-run the group 1 script, frontmatter, behavior-list, language, link-format, script-path, wiki-rule, page-name, delegation-list and hook validation, re-check the guidelines.md audit checklist for every touched skill, then re-run the optimization aspect that produced each accepted optimization. These three re-runs are as independent as the first pass, so run them in parallel and merge them the same way step 3 did. On any compliance failure, return to step 3: confirm and fix again, until all accepted compliance fixes pass. On an accepted optimization that does not produce the promised step reduction or cost reduction, or still weakens correctness beyond the recorded decision, return to step 3 for a new decision. Completion condition:
tools/lint-scripts.shexits 0 or 3 for every domain,tools/lint-frontmatter.shexits 0 for every domain — exit 3 is 「什麼都沒掃」 and never counts as a pass —tools/check-behaviors.shexits 0 for every domain,tools/ste100-lint.shexits 0 for every domain,tools/check-link-format.shexits 0 for every domain — its exit 3 is 「什麼都沒掃」 and never counts as a pass —tools/check-skill-paths.shexits 0 for every domain — its exit 3 is 「什麼都沒掃」 and never counts as a pass, and itsunrootedandunknownlines stay hints rather than becoming failures —jsc-gitea/tools/check-wiki-rules.shexits 0,tools/check-page-name.shexits 0 — its exit 3 is 「什麼都沒查」 and never counts as a pass —tools/check-delegate.shexits 0, its remaining stdout lines counted as hints rather than failures and its exit 3 read as 「無委派清單可查」 and never as a pass, every hook smoke exits 0 with thelinescount the script itself asserted, every domain's checklist passes in full — the two whole-round verdicts filled into each domain from the one run that produced them — and every accepted optimization has a matching verification result. -
Call
jsc-git:pronce per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in../../references/pr-report.md. -
Write the round's result to the wiki. This step MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents run in parallel: each domain writes its own page, and no page waits on another.
Without this step the whole audit stops at the PR and scatters. The other four
jsc-metachange skills all append toSKILLSET_{HASH};skill-checkwas the only one that did not, so the audit that produced the deferrals had nowhere to record them and step 2's group 3 had nothing to read back.-
Which page. For every domain repo actually changed in this round, resolve
gitea.sh wiki-repo SKILLSETand append one section toSKILLSET_plusgitea.sh hash-id "{owner}/{repo}"of that domain repo. Append; never overwrite — the page accumulates every change that domain has ever seen. -
When nothing changed. No domain repo changed in this round means one page, hashed from
plugins/meta, gets one section recording 「本輪無發現」 with the group verdicts that produced that conclusion. A round that found nothing still has to leave the evidence that it ran. -
What each section holds. The layout is
../../templates/skillset-page.md: date, change typeskill-check, the change request in one sentence, the skills touched, the files changed, the PR URL from step 7, the deploy-route verdict and the verification result. A section for a round that wrote delegation rows also names the skills whose rows this round judged or repointed, so the row's own missing note column is covered here. Theskill-checksection additionally carries the 優化建議 table, every row filled including 決議 and 決議日期 — that table is exactly what the next round reads in step 2's group 3. -
Directory page. Refresh that page's own block in
SKILLSET_CONTENTSwithjsc-gitea/tools/wiki-contents.sh— never by hand, and never throughjsc-gitea:wiki. 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, then run:jsc-gitea/tools/wiki-contents.sh upsert SKILLSET 2 "SKILLSET_{HASH}" {entry file} templates/skillset-contents.mdThe key is the H2 heading
SKILLSET_{HASH}, and that page name depends only on the domain repo's{owner}/{repo}, so it reads the same every round and one domain keeps exactly one block. The2is the key column: the index of the column that held the content-page link in the old markdown table, and it matters only when such an old table still has to be converted automatically — the conversion takes the last path segment of that column's link URL as the H2 heading. Count that index from the live page's own column layout, never from the template's: the liveSKILLSET_CONTENTSreads| 存放庫 | 異動報告 | 目前版本 | 最後更新 |, so the link sits in column 2 while column 1 is plain text likeplugins/ask. Passing1would make the headingplugins/ask, which never matches the keySKILLSET_{HASH}, so the existing entry is appended as a brand-new one — one domain ends up with two blocks and the older one is never updated again. The fourth argument is the whole block, not a table row. The script resolves the CONTENTS repo itself — the directory page lives there, never in the SKILLSET repo — reads the whole page, converts any leftover table to blocks, replaces the block whose heading matches, appends when none matches, and writes the page back, so every block owned by another domain stays as it was.The 異動頁 bullet is written as
[SKILLSET_{HASH}]({url}), the URL being the absolute one fromgitea.sh wiki-url {SKILLSET repo} SKILLSET_{HASH}; the H2 heading itself carries no link, no URL, no affix and no date. Every link on both pages takes that[{text}]({url})form — the double-bracket wiki-link form is never used, because it resolves only inside one wiki. Fetch that URL only after the content page is written: write the content page first — a directory block naming a page whose write failed is worse than a missing block. -
Check the links before writing. Hand every URL going onto the content page and into the directory block to
jsc-gitea/tools/link-check.shand write only when it exits 0. It verifies through the Gitea API, never a web status code: a private repo answers 404 to an unauthenticated web request, so a status-code check would call a live page dead and rewrite pages that are fine. -
Exit codes. Route every one of them. None of these calls may be read as success by default.
Call Exit Do gitea.sh wiki-repo2 The page type was misspelled. Fix the argument and rerun 3 No wiki repo is configured for that type. Name the variable ( JSC_WIKI_REPO_SKILLSETorJSC_WIKI_REPO_CONTENTS) andJSC_WIKI_REPO, ask per thejsc-ask:askrules, then rerungitea.sh hash-id1 No SHA-1 helper on this machine. Stop and report that sha1sumorshasumhas to be installed, and never hand-compute the hash2 Empty input, which means the {owner}/{repo}was never resolved. Fix that firstContent page read 0 Append into the sections already there 4 The page does not exist yet, so build it from the template 7 or 8 Stop and write nothing, because a page rebuilt on top of an unread read loses every section already on it gitea.sh wiki-url4 The content page is not there, so the write above did not succeed. Go back and write it; put no directory block in until the page exists, because a block may not name a page that failed 5 The API answered with no html_url. Stop and report it; never assemble the URL by hand from the host and the page namelink-check.sh0 Every link is reachable. Write the page 1 At least one link is dead. Write nothing, and report the DEADlines it printed2 No URL was passed, which is a defect here. Pass the links and rerun 3 GITEA_HOSTis unset. Set it and rerun; never skip the check instead7 Gitea authentication failed. Stop and report the key problem, and never read it as a dead link wiki-contents.sh upsert0 The block is in place. Report the updatedoraddedit printed, with the page it named1 The page content could not be assembled, or the write failed. A page with no matching block is not an error — that case appends. Report SKILLSET_CONTENTSas not written, together with the block content2 An argument was rejected. Fix it and rerun; nothing was written 3 No CONTENTS wiki repo is configured. Report JSC_WIKI_REPO_CONTENTSandJSC_WIKI_REPOas the two variables to set. The round's section is onSKILLSET_{HASH}and stays there4 The directory page is absent and no template was passed. Rerun with templates/skillset-contents.mdas the fifth argument7 The token is invalid or lacks permission, so the other domains' blocks are unknown. Stop, report the token problem, and create no page — writing nothing is what keeps those blocks alive 8 Some other API failure. Stop, report that status, and create no page -
On a failed write. Retry once. Still failing, hand the user the page name and the full section that was not written, so the round's result is not lost. Never close the run reporting a page as written when it was not, and never close it silently with the content only in the transcript.
Completion condition: every changed domain repo has one new section on its
SKILLSET_{HASH}and one## SKILLSET_{HASH}block inSKILLSET_CONTENTSwritten by awiki-contents.sh upsertthat exited 0, or — where nothing changed — theplugins/metapage carries the 「本輪無發現」 section and its block on the same terms; every content-page write is confirmed by a successful read-back or reported as not written with its full content handed back. -
-
Report this round's outcome to the local event stream — the last step of every run, the ones that stop early included. Run:
jsc-hooks/tools/report-status.sh skill-end jsc-meta:skill-check {status} {exit code} [detail]Resolve
jsc-hooksfrom thedomain<TAB>pathrow step 1 printed for thehooksdomain, the same way this skill resolves every other cross-plugin script. When that script is not on this machine, skip this step in silence and close the round as normal. A reporting path that is absent must never fail the run it reports on, and this call's own exit code never changes what this skill reports.Pick
{status}from what the round actually did:status Use it when okevery domain ended with a complete checklist, check-delegate.shexited 0 for the round, every accepted fix passed its re-check, every affected repo has a PR URL, and every wiki write exited 0blockeda gate or a missing prerequisite stopped the round before anything was audited — sync-domains.shnever reached exit 0, or the call itself was refusedfailedthe round broke mid-way — a re-check in step 6 kept failing, or a wiki write failed again after its one retry degradedthe round finished with a part missing — a domain carries 「本輪未取得已決議清單,優化建議暫不提出」, the delegation check came back 「無委派清單可查」, or a content page was written while its SKILLSET_CONTENTSblock was notabortedthe user stopped the round, or a prerequisite turned out not to hold and this skill stopped on its own {exit code}is this round's own result as a number:0forok, non-zero otherwise.detailis optional, one line, at most 200 characters.The matching
skill-startcomes free from the hook, which fires when the skill loads. The audit itself happens in the model turns after that, so no hook can see how the round ended — astartwith noendreads as an abort, which is why writing theendis this skill's own job.Completion condition: one
skill-endline for this round is appended to$JSC_HOME/usage/events.jsonl, or the script was absent and the final report says so.