取消 [[頁名]] 與 [[顯示文字|頁名]] 兩種同 wiki 寫法,不再分「同存取庫」與 「跨存取庫」兩條規則。那種寫法只在自己那個 wiki 內解析,寫錯不報錯,畫面上 看起來像普通文字或死連結,巡不到也修不了。 連結寫進頁面前先過 jsc-gitea 的 link-check.sh,結束碼 0 才寫。驗證一律走 API, 不看網頁狀態碼:私有存取庫的網頁網址對未登入請求一律回 404,拿狀態碼判會把 好連結判成壞的。認證失敗回 7,與死連結的 1 分開,免得金鑰一過期就把還在的頁 整批判死。
15 KiB
name, description
| name | description |
|---|---|
| maintain | SDLC maintenance stage. Gate on capability tags enforced in code by sdlc-gate (maintenance requires no specific tag, but the actual model id must be determinable from the transcript), read projects still inside their maintenance window from MAINTAIN_CONTENTS, which sits in the separate CONTENTS wiki repo, then run one sub agent per project: switch to develop or master, propose at least five maintenance actions, commit to a new branch, push, and PR. Write a jsc-log:worklog entry per finished project and update the last-maintained timestamp afterward through jsc-gitea/tools/wiki-contents.sh, then close with tools/stage-report.sh - model tag verdict, worklog link, every wiki link written. Use for periodic upkeep of delivered projects inside their maintenance window; not for projects still mid-implementation or not yet registered in MAINTAIN_CONTENTS. |
maintain
Goal: run routine maintenance for every project in the maintenance contents page.
MAINTAIN_CONTENTS lives in the CONTENTS wiki repo, the one jsc-gitea/tools/gitea.sh wiki-repo CONTENTS resolves: JSC_WIKI_REPO_CONTENTS first, JSC_WIKI_REPO second, exit 3 when neither is set; it never falls back to JSC_WIKI_REPO_MAINTAIN. MAINTAIN is the one page type with no content page, so this directory page is the whole record — read it and write it there, and nowhere else. Every link it carries is written as [{text}]({url}), and the {url} is the absolute URL from gitea.sh wiki-url {that type's repo} {page} — one link syntax, whichever wiki the page sits in. The syntax and the check that runs before every write: "Every link is checked before it reaches a page" below. Branch on wiki-url's exit code every time — this is the same branching jsc-log:worklog runs over the same call:
| Exit | What this stage does |
|---|---|
| 0 | use the URL it printed, verbatim |
| 4 | that page is not on the wiki — leave the cell with the literal 「無」 and say so, or, when the page was supposed to have been written by this run, go back and write it before coming here again |
| 5 | the page exists but carries no html_url — stop and report it, and never assemble the URL by hand; a hand-built path is not the one Gitea serves |
| 7 | the key is invalid or lacks permission (HTTP 401/403) — stop and report the key problem. Never read this as exit 4 and never fill 「無」: the page is alive, and 「無」 records a live page as one that does not exist |
| 8 | any other API failure — stop and report that status, and do not retry the same call unchanged |
Never let an empty string stand in for the URL: a cell that is empty names a page nobody can open, and the next run rewrites that row as if it were correct.
All wiki reads and writes go through jsc-gitea:wiki. A failed wiki read or write stops this stage: report which page and which operation failed, never carry on against a page you could not read, and never report a page as saved when the write failed. Step 5 still runs after such a stop.
Steps
- Model gate and stage lock — run
jsc-cli/tools/model-tags.sh sync, thenjsc-hooks/hooks/sdlc-gate.sh lock maintain. This stage requires no specific capability tag; the gate passes as long as the script can determine the actual model id. Rules:references/model-gate.md. Completion condition: the script exited 0, and you have reported the stage, the required tag, the actual model id it read from the transcript, and the verdict. - Read
MAINTAIN_CONTENTSviajsc-gitea:wiki, out of the CONTENTS wiki repo (gitea.sh wiki-repo CONTENTS, never the MAINTAIN one), and filter projects still inside their maintenance window: start date ≤ today, and (end date is NULL or ≥ today). Completion condition: you have listed every in-window project with its{owner}/{repo}and window dates, or reported that none is in window and stopped. - Align every project in one batch first, then run one sub agent per project. Completion condition: every project listed in step 2 has its sub agent finished, and each one ends in either a PR link or a recorded skip reason.
-
Batch prefetch, run by the main agent before any sub agent starts. For every in-window project from step 2, run
git fetch --prune origin, then put it on its maintenance branch and align it withorigin/{branch}. The projects are independent — run this batch concurrently, and hand each sub agent the branch name and the aligned commit sha instead of letting it fetch again. Which branch that is, the remote-is-the-basis rule, the diverged case and the never-pull-never-reset rule all live inreferences/branch.md; never guess the branch name. From sub-step 3.2 onward the flow is one project at a time, sequential, so that 3.5's work log rule holds. Completion condition: every project's HEAD points at the same commit asorigin/{branch}, or its gap is reported and that project is skipped and left out of the sub agent runs. -
From here on, one project at a time, and each project's maintenance MUST run as a sub agent. Propose at least five maintenance methods, then let the user pick per
jsc-ask:askrules — every option states its impact scope (which files it touches, whether it can break the build, how much review it costs). Candidates:- dependency updates (reuse
jsc-pkg:pkg-update) - security vulnerability scan and patching
- dead code and stale comment cleanup
- test coverage reinforcement
- docs and README synchronization
- build warning elimination
Completion condition: the user has picked the methods to apply, and every picked method is either applied or reported with the reason it could not be.
- dependency updates (reuse
-
A code comment states why the code is written this way; it never states where the work is tracked. Issue numbers, commit hashes, branch names, people's names and
@mentions stay out of every code comment this project's maintenance touches — including the comments the cleanup method rewrites. Full list and the allowed exceptions:jsc-review/references/comment-scope.md. Two passes already cover the diff, so run no separate manual sweep of your own:jsc-hooks/hooks/comment-scope.shcompares each file after it is written and prints a warning — fix the flagged line at once, then carry on — andjsc-git:commitsweeps the whole working tree again in step 3.4, before anything is committed. Coverage is not the same on every CLI: only claude gets the per-file warning as the file is written. On codex, kiro, copilot and antigravity the hook fires late — at the end of the turn on codex, at the next prompt submit on kiro, at the end of the session on copilot and antigravity — so the pre-commit sweep in step 3.4 is the only pass on all four that lands in time to keep a flagged comment out of the commit. Completion condition: every warning the hook printed is fixed, and the step 3.4 sweep reported no remaining comment line carrying an issue number, a commit hash, a branch name, a person's name or an@mention. -
Commit the changes to a new branch per
jsc-git:commit, push, then open a PR perjsc-git:prback to the branch of step 3.1, passing it explicitly as the base. Completion condition: the PR exists, and you have reported it with the table format injsc-meta/references/pr-report.md. -
One project's maintenance is one finished task — call
jsc-log:worklogright after its PR is open. A task is one of three things: one work package, one round of PR-comment fixes, or one standalone fix commit; this stage produces the third kind, one per project. Never let the stage end and then write a single catch-up entry, and never let a second project start before the first one's entry is saved — by then the elapsed time, the token counts and the difficulties are gone. Every entry appends to the sameLOG_{HASH}page. Content parked earlier bytools/stage-report.sh --pending-fileis merged into that same write and cleared only once the write succeeds; parked content is not a written log. Completion condition: this project's entry is saved onLOG_{HASH}before the next project's sub agent starts. -
Update the project's last-maintained field (the zh-TW column 「前次維護時間」) in
MAINTAIN_CONTENTSto today withjsc-gitea/tools/wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo} {row file} templates/maintain-contents.md, never by hand-editing the page. Rebuild that project's row from the one the page already holds, change only the 「前次維護時間」 cell, and keep every other cell byte-for-byte as it was; the key is column 1, the repository name, written exactly as the row file writes it. A row that carries a link goes throughjsc-gitea/tools/link-check.shbefore the upsert, and is upserted only on exit 0 — see "Every link is checked before it reaches a page" below. Branch on the upsert's exit code per "Contents pages are appended, never overwritten" below. Completion condition: the script exited 0, every link in the rebuilt row was cleared by alink-check.shrun that exited 0,MAINTAIN_CONTENTSshows today's date in 「前次維護時間」 for that project, and every other project's row is byte-for-byte unchanged.
-
- The main agent reports the summary: maintenance methods applied per project, PR table rows, and failure reasons. The report and all generated wiki content, commits, and PR descriptions stay Traditional Chinese per the STE100 rule. Completion condition: the summary names every project read in step 2, each with its applied methods and either a PR table row or the reason it was skipped.
- Stage report — the last thing this stage does, including when no project was in window, and when a wiki read or write failed. Run
tools/stage-report.sh maintainwith one--page TYPE:{page}per wiki page this run wrote — that is--page CONTENTS:MAINTAIN_CONTENTS, under theCONTENTStype, because the script resolves each page's repo from the TYPE you pass andMAINTAIN:would resolve the wrong repo and print no URL — plus--worklogand--worklog-headingpointing at the entries step 3.5 wrote.--pending-file {file} --log-hash {HASH}is the fallback for a stage that stopped before any project finished: it holds the content for the nextjsc-log:worklogrun, and held content is not a written log. Rules and exit codes:references/stage-report.md. Exit 1 is a warning, never a block. Completion condition: the script's output is reported to the user verbatim, and every wiki page this run wrote appears in it.
Every link is checked before it reaches a page
One syntax. Every link this stage writes — in the MAINTAIN_CONTENTS row, in a PR description, in the stage report — is written as [{text}]({url}). The {url} is the absolute URL gitea.sh wiki-url {repo} {page} printed, used verbatim: never assemble a wiki path by hand, and never write a link as [[頁名]] or [[顯示文字|頁名]]. That form resolves only inside the wiki it sits in, and it fails without an error — the reader sees plain text or a dead link, so a wrong link is neither noticed nor fixable.
Checked before it is written. Collect every link the row is about to carry, hand them all to jsc-gitea/tools/link-check.sh in one run — link-check.sh {網址}..., or the same URLs on stdin, one per line — and write only when that run exits 0. It prints one {OK|DEAD|SKIP}<TAB>{網址}<TAB>{說明} line per URL. The check goes through the API, never a web status code: a private repository's web URL answers 404 to a request carrying no key, so a status-code check marks live pages dead.
| Exit | What this stage does |
|---|---|
| 0 | every link answers — upsert the row |
| 1 | at least one link is dead — write nothing, and report the DEAD lines to the user |
| 2 | usage error: not one URL was passed — pass the links and run it again |
| 3 | the list holds a Gitea URL but GITEA_HOST is unset — report it as a setting to fix and run it again, and never skip the check instead |
| 7 | Gitea authentication failed (401/403) — stop and report the key problem. Never read this as exit 1: an expired key makes live pages look absent, and MAINTAIN_CONTENTS is the whole record of this stage, so a row rewritten on that reading loses links nothing else holds |
Completion condition: every row this stage wrote was cleared by a link-check.sh run that exited 0, and every non-zero code was branched on as this table says.
Contents pages are appended, never overwritten
MAINTAIN_CONTENTS is a shared directory in the CONTENTS wiki repo: every row on it belongs to somebody's project, and this run reads none of those rows from anywhere else. So step 3.6 is an upsert of one row — never a whole-page overwrite, and a project this run did not maintain keeps its row untouched. jsc-gitea/tools/wiki-contents.sh upsert is the one way this stage does it: it resolves the CONTENTS repo, reads the whole page, replaces the row whose key column matches and appends when none matches, then writes the page back.
Branch on its exit code:
| Exit | What this step does |
|---|---|
| 0 | the row is in place — carry the updated or added word it printed into the stage report |
| 1 | the write failed, or the page holds no markdown table — report it as a failed write and go to step 5 as a failure |
| 2 | an argument was rejected (unknown type, key column, missing row file) — fix the argument and run it again; nothing was written |
| 3 | no CONTENTS wiki repo is configured — stop and report JSC_WIKI_REPO_CONTENTS and JSC_WIKI_REPO as the two variables to set. MAINTAIN has no content page, so nothing of this stage's record survives elsewhere: the PR is open but the maintenance date is unrecorded, and it is reported that way |
| 4 | the directory page is absent and no template was passed. Step 3.6 always passes templates/maintain-contents.md, so this code does not come out of this skill's call — a wrong template path is rejected as 2, not as 4. Seeing it anyway means the template file is not where the plugin puts it: confirm the plugin installation is complete and run it again, and never answer it by dropping the template argument. An absent page also means step 2 had no project to maintain |
| 7 | the key is invalid or lacks permission — stop and report the key problem; the script wrote nothing, which is what keeps every other project's row alive |
| 8 | any other API failure — stop and report that status, and do not retry the same call unchanged |
Why 7 and 8 abort: both mean the old content is unknown, not that the page is missing. Reading either as "not there yet" would write a fresh template over a live directory, and every other project's maintenance window is gone — the write carries no merge and no backup. Content pages, which belong to one subject each and live in their own type's repo, are the opposite case and may be rewritten whole. The distinction is the page, not the write.
Completion condition: the MAINTAIN_CONTENTS write names the wiki-contents.sh exit code it branched on, and the directory page was created on no code other than 4.