連結一律寫成 [文字](絕對網址),並在寫入前驗證連得到 #58

Merged
admin merged 2 commits from feat/link-verification/main into develop 2026-09-02 06:46:24 +00:00
17 changed files with 214 additions and 56 deletions
Showing only changes of commit f4e489ceb4 - Show all commits
+1 -1
View File
@@ -69,7 +69,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安
Wiki 位置:**目錄頁與內容頁分屬不同存取庫**。五個目錄頁 `PLAN_CONTENTS`、`ANALYZE_CONTENTS`、`REPO_CONTENTS`、`DELIVER_CONTENTS`、`MAINTAIN_CONTENTS` 一律由 `gitea.sh wiki-repo CONTENTS` 解析:只讀 `JSC_WIKI_REPO_CONTENTS`,再退回 `JSC_WIKI_REPO`,兩者都沒設定就 exit 3,**不退回各自的頁型變數**。內容頁各走自己的頁型:`PLAN_{HASH}` 只讀 `JSC_WIKI_REPO_PLAN`、`ANALYZE_{HASH}` 只讀 `JSC_WIKI_REPO_ANALYZE`、`REPO_{HASH}` 只讀 `JSC_WIKI_REPO_REPO`、`DELIVER_{HASH}` 只讀 `JSC_WIKI_REPO_DELIVER`,各自再退回 `JSC_WIKI_REPO`。`MAINTAIN` 是唯一只有目錄頁、沒有內容頁的型別,整個型別都在 CONTENTS 存取庫。不同類型不可互相代用;兩者都未設定才詢問(見 `jsc-gitea`)。
目錄頁寫入與連結:一律用 `jsc-gitea/tools/wiki-contents.sh upsert {TYPE} {鍵欄} {鍵} {列檔} {範本}` 單列 upsert,禁止手工整頁覆蓋(結束碼 `0`=已更新或已新增、`1`=寫入失敗、`2`=用法錯誤、`3`=CONTENTS 存取庫未設定、`4`=頁不存在且沒給範本、`7`=金鑰失效、`8`=其他 API 失敗)。目錄頁指向內容頁的連結一律用 `gitea.sh wiki-url` 給的絕對網址,`[[...]]` 只在同一個 wiki 內解析,跨存取庫會變死連結。階段回報 `tools/stage-report.sh --page TYPE:PAGE` 的目錄頁一律填 `CONTENTS:` 這個型別,填舊型別會解到錯的存取庫、印不出網址。
目錄頁寫入與連結:一律用 `jsc-gitea/tools/wiki-contents.sh upsert {TYPE} {鍵欄} {鍵} {列檔} {範本}` 單列 upsert,禁止手工整頁覆蓋(結束碼 `0`=已更新或已新增、`1`=寫入失敗、`2`=用法錯誤、`3`=CONTENTS 存取庫未設定、`4`=頁不存在且沒給範本、`7`=金鑰失效、`8`=其他 API 失敗)。連結一律寫成 `[{文字}]({連結})`,連結取自 `gitea.sh wiki-url` 的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,巡不到也修不了。寫進任何頁面前,每個連結先過 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入;有 DEAD 就不寫並回報(`1`=有連不到、`2`=沒給網址、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效要停下回報,不得當成連不到)。階段回報 `tools/stage-report.sh --page TYPE:PAGE` 的目錄頁一律填 `CONTENTS:` 這個型別,填舊型別會解到錯的存取庫、印不出網址。
HASH 規則:`{owner}/{repo}` 的共用 wiki hash 一律由 `jsc-gitea/tools/hash-id` 計算(見 `jsc-gitea:wiki`),此 domain 不重複實作演算法。長度與大小寫的正本也在那裡(現行為完整 40 碼大寫十六進位),本檔不複述規格,只要求一件事:`hash-id` 印出什麼就原樣用,不得自行截短。工作證 `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}` 不是 wiki 頁,但用的是同一支 `hash-id`,同樣原樣帶著走。
+16 -16
View File
@@ -7,37 +7,37 @@
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 計畫已經寫進 `PLAN_CONTENTS`(CONTENTS 存取庫)、狀態是「未分析」,要把它拆成工作包時用。也用於延伸既有的 `ANALYZE_{HASH}` 分析頁。要寫程式碼時不用,那是 `implement`。`PLAN_CONTENTS` 還沒有計畫時不用,先跑 `plan`。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock analyze` 過模型閘門,本階段要 `reasoning-max` 標籤、用 `gitea.sh wiki-repo CONTENTS` 解出目錄頁存取庫,並行讀 `PLAN_CONTENTS` 與 `ANALYZE_CONTENTS`、讓使用者選延伸既有分析或分析新計畫、跑 `git fetch --prune origin` 後確認來源分支,並核對 HEAD 與 `origin/{source-branch}` 指到同一個 commit,有落差就停下回報、逐則使用者故事問到共識、先查 `REPO_{HASH}` 盤點頁(雜湊取自該存取庫自己的 `{owner}/{repo}`)決定複用,資料過期就開 sub agent 重新盤點,把 `REPO_{HASH}` 寫回 REPO 存取庫、`REPO_CONTENTS` 用 `wiki-contents.sh upsert REPO 1 {owner}/{repo}` 寫回、做 WBS 拆工作包並標相依,交付工作包固定編為 `WP-01` 且獨立不併入實作包、用 CPM 估工時與天數,標出要徑並依 `references/cpm-chart.md` 畫 mermaid 甘特圖、寫使用者故事驗收計畫,再逐包寫 TDD 待辦、一次寫入 `ANALYZE_{HASH}`(ANALYZE 存取庫),再用 `wiki-contents.sh upsert ANALYZE 3 {HASH}` 與 `wiki-contents.sh upsert PLAN 4 {HASH}` 各自單列 upsert、目錄列的分析頁與盤點頁連結一律取自 `gitea.sh wiki-url` 的絕對網址,並依退出碼分流(`0` 用它印出的網址,`4` 回頭補寫那頁再回來,`5`、`7`、`8` 停下回報,一律不自行組網址、也不留空白連結)、目錄列檔與待寫日誌檔一律用 Bash 的 heredoc 或 `mktemp` 產在暫存目錄,不走 Write 或 Edit 工具,因為 `write-guard.sh` 的 stage 模式只看階段鎖不看路徑,走工具會被自己的閘門擋下、跑 `tools/stage-report.sh analyze` 收尾回報,目錄頁一律用 `--page CONTENTS:{頁名}`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`jsc-hooks/hooks/write-guard.sh`(claude 的 `PreToolUse` 擋寫入)、`jsc-gitea:wiki`、`jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh` 的 `wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-ask:ask`、`tools/stage-report.sh`、`git fetch --prune origin`、`git branch -r`、`git rev-list --left-right --count`。 |
| 完成條件 | 模型閘門退出 0 並回報實際模型 id、來源分支經使用者確認且與遠端一致、每則使用者故事達成共識、每個複用決策連理由記進「複用決策」欄、每則故事對應至少一個編號工作包、每包有工時與天數、要徑與甘特圖齊備、每個實作包至少一則測試先行的 `[ ]` 待辦、`ANALYZE_{HASH}` 存進 wiki 且未決項欄有值(沒有就寫「無」)、每個目錄列的頁面連結都來自退出 0 的 `wiki-url`,非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼、每次目錄頁寫入都講出 `wiki-contents.sh` 的退出碼並依碼分流(`0` 續行,`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、`stage-report.sh` 的輸出原樣貼給使用者。提前停下也要跑收尾回報。 |
| 可驗證跡象 | ANALYZE 存取庫多一頁或更新一頁 `ANALYZE_{HASH}`;CONTENTS 存取庫的 `ANALYZE_CONTENTS` 多一列該分析,連結是絕對網址;`PLAN_CONTENTS` 該計畫那列狀態變成「已分析」;重新盤點時 REPO 存取庫多一頁 `REPO_{HASH}`,`REPO_CONTENTS` 多一列;`$JSC_HOME/sessions/{工作階段 id}.stage` 是 `sdlc-gate.sh lock` 寫的階段鎖狀態檔;還沒有工作日誌時,`$JSC_HOME/worklog-pending/{HASH}/` 下有暫存的日誌內容檔。工作目錄的檔案一律不動,程式碼沒有任何改動。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock analyze` 過模型閘門,本階段要 `reasoning-max` 標籤、用 `gitea.sh wiki-repo CONTENTS` 解出目錄頁存取庫,並行讀 `PLAN_CONTENTS` 與 `ANALYZE_CONTENTS`、讓使用者選延伸既有分析或分析新計畫、跑 `git fetch --prune origin` 後確認來源分支,並核對 HEAD 與 `origin/{source-branch}` 指到同一個 commit,有落差就停下回報、逐則使用者故事問到共識、先查 `REPO_{HASH}` 盤點頁(雜湊取自該存取庫自己的 `{owner}/{repo}`)決定複用,資料過期就開 sub agent 重新盤點,把 `REPO_{HASH}` 寫回 REPO 存取庫、`REPO_CONTENTS` 用 `wiki-contents.sh upsert REPO 1 {owner}/{repo}` 寫回、做 WBS 拆工作包並標相依,交付工作包固定編為 `WP-01` 且獨立不併入實作包、用 CPM 估工時與天數,標出要徑並依 `references/cpm-chart.md` 畫 mermaid 甘特圖、寫使用者故事驗收計畫,再逐包寫 TDD 待辦、一次寫入 `ANALYZE_{HASH}`(ANALYZE 存取庫),再用 `wiki-contents.sh upsert ANALYZE 3 {HASH}` 與 `wiki-contents.sh upsert PLAN 4 {HASH}` 各自單列 upsert、目錄列的分析頁與盤點頁連結一律取自 `gitea.sh wiki-url` 的絕對網址,並依退出碼分流(`0` 用它印出的網址,`4` 回頭補寫那頁再回來,`5`、`7`、`8` 停下回報,一律不自行組網址、也不留空白連結)、每個要放進頁面或目錄列的連結一律寫成 `[{文字}]({連結})`,不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`,而且寫入前先整批交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫,有 DEAD 就整筆不寫並回報連不到的清單(`2` 補參數重跑、`3` 先設好 `GITEA_HOST`、`7` 金鑰失效停下回報,不得當成連不到)、目錄列檔與待寫日誌檔一律用 Bash 的 heredoc 或 `mktemp` 產在暫存目錄,不走 Write 或 Edit 工具,因為 `write-guard.sh` 的 stage 模式只看階段鎖不看路徑,走工具會被自己的閘門擋下、跑 `tools/stage-report.sh analyze` 收尾回報,目錄頁一律用 `--page CONTENTS:{頁名}`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`jsc-hooks/hooks/write-guard.sh`(claude 的 `PreToolUse` 擋寫入)、`jsc-gitea:wiki`、`jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh` 的 `wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/link-check.sh`、`jsc-ask:ask`、`tools/stage-report.sh`、`git fetch --prune origin`、`git branch -r`、`git rev-list --left-right --count`。 |
| 完成條件 | 模型閘門退出 0 並回報實際模型 id、來源分支經使用者確認且與遠端一致、每則使用者故事達成共識、每個複用決策連理由記進「複用決策」欄、每則故事對應至少一個編號工作包、每包有工時與天數、要徑與甘特圖齊備、每個實作包至少一則測試先行的 `[ ]` 待辦、`ANALYZE_{HASH}` 存進 wiki 且未決項欄有值(沒有就寫「無」)、每個目錄列的頁面連結都來自退出 0 的 `wiki-url`,非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼、每一頁與每一列寫出去之前都經 `link-check.sh` 退出 0 驗過,連結格式一律是 `[{文字}]({連結})`、每次目錄頁寫入都講出 `wiki-contents.sh` 的退出碼並依碼分流(`0` 續行,`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、`stage-report.sh` 的輸出原樣貼給使用者。提前停下也要跑收尾回報。 |
| 可驗證跡象 | ANALYZE 存取庫多一頁或更新一頁 `ANALYZE_{HASH}`;CONTENTS 存取庫的 `ANALYZE_CONTENTS` 多一列該分析,連結是絕對網址;`PLAN_CONTENTS` 該計畫那列狀態變成「已分析」;重新盤點時 REPO 存取庫多一頁 `REPO_{HASH}`,`REPO_CONTENTS` 多一列;`$JSC_HOME/sessions/{工作階段 id}.stage` 是 `sdlc-gate.sh lock` 寫的階段鎖狀態檔;還沒有工作日誌時,`$JSC_HOME/worklog-pending/{HASH}/` 下有暫存的日誌內容檔;`stage-report.sh` 的「寫入的 wiki 頁」表格多一欄連結驗證,逐列標「通過、無可查端點、連不到、未驗證」,頁面上找不到 `[[...]]` 寫法的連結。工作目錄的檔案一律不動,程式碼沒有任何改動。 |
## implement
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 分析頁已經有工作包與 TDD 待辦,要動手寫程式碼時用。也用於回頭處理既有工作包 PR 的留言。規劃或分析階段不用。`ANALYZE_CONTENTS` 沒有未完成分析頁時不用。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock implement` 過模型閘門,本階段要 `coding` 標籤、從 CONTENTS 存取庫讀 `ANALYZE_CONTENTS`、從 ANALYZE 存取庫讀每一頁未完成分析頁,這份資料後續步驟重用不再讀第二次、對每支未合併 PR 並行跑 `wp-gate.sh check` 取狀態與留言、動任何 PR 之前先跑 `wp-gate.sh owns` 確認歸屬,`foreign` 就放手、每則留言先經 `jsc-ask:ask` 取得共識,再由 sub agent 在同一個 worktree 內修、推同一條工作分支、逐則留言用 `gitea.sh comment-reply` 回覆處置結果,把 `latest=` 時間寫回分析頁 PR 欄、一輪留言修正算一個任務,當下寫一筆 `jsc-log:worklog`、列出可挑的工作包,每個候選並行跑 `wp-gate.sh check-deps` 過相依閘門,只有 `ready` 進選項、使用者挑定後跑 `wp-gate.sh claim` 登錄歸屬、確認來源分支,它同時是 worktree 基準與 PR 目標,遠端找不到就停下回報、產生 `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}` 工作證並寫回分析頁「工作證」欄,同時改工作階段名稱、交付包先問交付內容型別並寫回「交付型別」欄、`git fetch --prune origin` 後從 `origin/{source-branch}` 建 worktree,多存取庫並行建、逐項待辦各開一個 sub agent 跑 TDD,主代理每完成一項就把 `[ ]` 翻成 `[x]` 並存回 wiki、收尾並行跑 `jsc-review:code-review` 與 `jsc-review:api-doc`,後者先用 `swagger-detect.sh` 判定,退出碼 1 就明確略過並回報、跑 `jsc-git:pr` 開 PR 回來源分支,把 PR 連結寫回分析頁,再跑 `wp-gate.sh lock` 上鎖、寫一筆工作日誌,接著用 `pr-watch.sh` 輪詢等合併,退出碼 10 就回頭跑同一套留言處理、合併後移除 worktree 並把工作包標為完成、問交付格式並產出 `DELIVER_{HASH}` wiki 頁(寫進 DELIVER 存取庫,雜湊取自 `{owner}/{repo}` 加工作包編號,一包一頁不互相覆蓋)或 Gitea issue 留言,交付頁再用 `wiki-contents.sh upsert DELIVER 6 {HASH}` 把 `DELIVER_CONTENTS` 那一列寫回,連結取自 `gitea.sh wiki-url` 的絕對網址並依退出碼分流(`0` 用它印出的網址,`4` 回頭補寫交付頁再回來,`5`、`7`、`8` 停下回報,一律不自行組網址、也不留空白連結)、問要不要登錄維護並用 `wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo}` 寫回 `MAINTAIN_CONTENTS`、跑 `tools/stage-report.sh implement` 收尾回報,目錄頁一律用 `--page CONTENTS:{頁名}`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`tools/wp-gate.sh` 的 `check`、`check-deps`、`claim`、`lock`、`owns`、`tools/stage-report.sh`、`jsc-gitea:wiki`、`jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh`(`wiki-repo`、`wiki-url`、`comment-reply` 與 issue 留言 API)、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/pr-watch.sh`、`jsc-ask:ask`、`jsc-review:code-review`、`jsc-review:api-doc`、`jsc-review/tools/swagger-detect.sh`、`jsc-hooks/hooks/comment-scope.sh`、`jsc-git:commit`、`jsc-git:pr`、`jsc-log:worklog`、`git fetch`、`git worktree`。 |
| 完成條件 | 模型閘門退出 0、每支未合併 PR 的留言都有處置與回覆、挑中的工作包經 `check-deps` 判為 `ready` 並 `claim` 成功、來源分支經確認並記進分析頁、工作證寫上 wiki、該包每一項待辦在 wiki 上都是 `[x]`、兩個收尾稽核都放行(`api-doc` 回報略過也算放行)、PR 開好且 `wp-gate.sh lock` 回 `status=locked`、工作日誌已寫、`pr-watch.sh` 退出 0 且 `wp-gate.sh check` 回 `status=merged`、交付文件已產出、交付頁那列的連結來自退出 0 的 `wiki-url`,非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼、維護登錄問題已回答、每次目錄頁寫入都講出 `wiki-contents.sh` 的退出碼並依碼分流(`0` 續行,`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、`stage-report.sh` 的輸出原樣貼出並列出 worktree 與三條分支。提前停下也要跑收尾回報。 |
| 可驗證跡象 | 開出一條工作分支,並有一支回到來源分支的 PR;ANALYZE 存取庫的分析頁 `ANALYZE_{HASH}` 的「工作證」欄(值是 `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}`,帶著 `hash-id` 原樣印出的雜湊)、「交付型別」欄、PR 欄、待辦勾選狀態都更新過;DELIVER 存取庫多一頁 `DELIVER_{HASH}`,或該 issue 下多一則留言;CONTENTS 存取庫的 `DELIVER_CONTENTS` 多一列,連結是絕對網址;使用者同意登錄時 `MAINTAIN_CONTENTS` 多一列;`LOG_{HASH}` 每完成一個任務多一筆條目;`$JSC_HOME/wp/{owner}-{repo}.claim` 與 `$JSC_HOME/wp/{owner}-{repo}-{index}.pr` 兩個狀態檔;`{cwd}/.worktree/{分析-HASH}/{工作包編號}/{repo}` 目錄,PR 合併後被移除;該存取庫 `.git/info/exclude` 多一筆 `.worktree/`;PR 每則留言底下有回覆。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock implement` 過模型閘門,本階段要 `coding` 標籤、從 CONTENTS 存取庫讀 `ANALYZE_CONTENTS`、從 ANALYZE 存取庫讀每一頁未完成分析頁,這份資料後續步驟重用不再讀第二次、對每支未合併 PR 並行跑 `wp-gate.sh check` 取狀態與留言、動任何 PR 之前先跑 `wp-gate.sh owns` 確認歸屬,`foreign` 就放手、每則留言先經 `jsc-ask:ask` 取得共識,再由 sub agent 在同一個 worktree 內修、推同一條工作分支、逐則留言用 `gitea.sh comment-reply` 回覆處置結果,把 `latest=` 時間寫回分析頁 PR 欄、一輪留言修正算一個任務,當下寫一筆 `jsc-log:worklog`、列出可挑的工作包,每個候選並行跑 `wp-gate.sh check-deps` 過相依閘門,只有 `ready` 進選項、使用者挑定後跑 `wp-gate.sh claim` 登錄歸屬、確認來源分支,它同時是 worktree 基準與 PR 目標,遠端找不到就停下回報、產生 `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}` 工作證並寫回分析頁「工作證」欄,同時改工作階段名稱、交付包先問交付內容型別並寫回「交付型別」欄、`git fetch --prune origin` 後從 `origin/{source-branch}` 建 worktree,多存取庫並行建、逐項待辦各開一個 sub agent 跑 TDD,主代理每完成一項就把 `[ ]` 翻成 `[x]` 並存回 wiki、收尾並行跑 `jsc-review:code-review` 與 `jsc-review:api-doc`,後者先用 `swagger-detect.sh` 判定,退出碼 1 就明確略過並回報、跑 `jsc-git:pr` 開 PR 回來源分支,把 PR 連結寫回分析頁,再跑 `wp-gate.sh lock` 上鎖、寫一筆工作日誌,接著用 `pr-watch.sh` 輪詢等合併,退出碼 10 就回頭跑同一套留言處理、合併後移除 worktree 並把工作包標為完成、問交付格式並產出 `DELIVER_{HASH}` wiki 頁(寫進 DELIVER 存取庫,雜湊取自 `{owner}/{repo}` 加工作包編號,一包一頁不互相覆蓋)或 Gitea issue 留言,交付頁再用 `wiki-contents.sh upsert DELIVER 6 {HASH}` 把 `DELIVER_CONTENTS` 那一列寫回,連結取自 `gitea.sh wiki-url` 的絕對網址並依退出碼分流(`0` 用它印出的網址,`4` 回頭補寫交付頁再回來,`5`、`7`、`8` 停下回報,一律不自行組網址、也不留空白連結)、每個要放進頁面、目錄列、PR 欄或 issue 留言的連結一律寫成 `[{文字}]({連結})`,不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`,而且寫入前先整批交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫,有 DEAD 就整筆不寫並回報連不到的清單(`2` 補參數重跑、`3` 先設好 `GITEA_HOST`、`7` 金鑰失效停下回報,不得當成連不到)、問要不要登錄維護並用 `wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo}` 寫回 `MAINTAIN_CONTENTS`、跑 `tools/stage-report.sh implement` 收尾回報,目錄頁一律用 `--page CONTENTS:{頁名}`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`tools/wp-gate.sh` 的 `check`、`check-deps`、`claim`、`lock`、`owns`、`tools/stage-report.sh`、`jsc-gitea:wiki`、`jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh`(`wiki-repo`、`wiki-url`、`comment-reply` 與 issue 留言 API)、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/link-check.sh`、`jsc-gitea/tools/pr-watch.sh`、`jsc-ask:ask`、`jsc-review:code-review`、`jsc-review:api-doc`、`jsc-review/tools/swagger-detect.sh`、`jsc-hooks/hooks/comment-scope.sh`、`jsc-git:commit`、`jsc-git:pr`、`jsc-log:worklog`、`git fetch`、`git worktree`。 |
| 完成條件 | 模型閘門退出 0、每支未合併 PR 的留言都有處置與回覆、挑中的工作包經 `check-deps` 判為 `ready` 並 `claim` 成功、來源分支經確認並記進分析頁、工作證寫上 wiki、該包每一項待辦在 wiki 上都是 `[x]`、兩個收尾稽核都放行(`api-doc` 回報略過也算放行)、PR 開好且 `wp-gate.sh lock` 回 `status=locked`、工作日誌已寫、`pr-watch.sh` 退出 0 且 `wp-gate.sh check` 回 `status=merged`、交付文件已產出、交付頁那列的連結來自退出 0 的 `wiki-url`,非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼、每一頁、每一列與每一則 issue 留言寫出去之前都經 `link-check.sh` 退出 0 驗過,連結格式一律是 `[{文字}]({連結})`、維護登錄問題已回答、每次目錄頁寫入都講出 `wiki-contents.sh` 的退出碼並依碼分流(`0` 續行,`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、`stage-report.sh` 的輸出原樣貼出並列出 worktree 與三條分支。提前停下也要跑收尾回報。 |
| 可驗證跡象 | 開出一條工作分支,並有一支回到來源分支的 PR;ANALYZE 存取庫的分析頁 `ANALYZE_{HASH}` 的「工作證」欄(值是 `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}`,帶著 `hash-id` 原樣印出的雜湊)、「交付型別」欄、PR 欄、待辦勾選狀態都更新過;DELIVER 存取庫多一頁 `DELIVER_{HASH}`,或該 issue 下多一則留言;CONTENTS 存取庫的 `DELIVER_CONTENTS` 多一列,連結是絕對網址;使用者同意登錄時 `MAINTAIN_CONTENTS` 多一列;`LOG_{HASH}` 每完成一個任務多一筆條目;`$JSC_HOME/wp/{owner}-{repo}.claim` 與 `$JSC_HOME/wp/{owner}-{repo}-{index}.pr` 兩個狀態檔;`{cwd}/.worktree/{分析-HASH}/{工作包編號}/{repo}` 目錄,PR 合併後被移除;該存取庫 `.git/info/exclude` 多一筆 `.worktree/`;PR 每則留言底下有回覆;`stage-report.sh` 的「寫入的 wiki 頁」表格多一欄連結驗證,逐列標「通過、無可查端點、連不到、未驗證」,寫出去的頁面與留言都找不到 `[[...]]` 寫法的連結。 |
## maintain
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 已交付、且已登錄在 `MAINTAIN_CONTENTS`(CONTENTS 存取庫)的專案要做定期保養時用。還在實作中的專案不用。沒有登錄進 `MAINTAIN_CONTENTS` 的專案不用。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock maintain` 過模型閘門,本階段不要求特定標籤,只要判定得出實際模型 id、用 `gitea.sh wiki-repo CONTENTS` 解出存取庫後讀 `MAINTAIN_CONTENTS`(`MAINTAIN` 只有目錄頁、沒有內容頁),篩出還在維護期內的專案(起始日不晚於今天,結束日為空或不早於今天)、主代理先並行對每個專案跑 `git fetch --prune origin`,切到維護分支並與 `origin/{branch}` 對齊,有落差就回報並略過該專案、之後一個專案一個專案跑,每個專案的維護都開一個 sub agent、每個專案提出至少五項維護做法給使用者挑、把改動用 `jsc-git:commit` 提交到新分支,推送後用 `jsc-git:pr` 開 PR 回步驟 3.1 那條分支、PR 開好當下寫一筆 `jsc-log:worklog`、用 `wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo}` 把該專案在 `MAINTAIN_CONTENTS` 的「前次維護時間」更新成今天,其餘欄位原樣保留、該頁指向別型別頁的連結一律取自 `gitea.sh wiki-url` 的絕對網址並依退出碼分流(`0` 用它印出的網址,`4` 填「無」或回頭補寫那頁,`5`、`7`、`8` 停下回報,`7` 絕不當成 `4` 填「無」,一律不自行組網址、也不留空白連結)、主代理彙整每個專案的做法、PR 表格列與失敗原因、跑 `tools/stage-report.sh maintain` 收尾回報,目錄頁用 `--page CONTENTS:MAINTAIN_CONTENTS`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`jsc-gitea:wiki`、`jsc-gitea/tools/gitea.sh` 的 `wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-ask:ask`、`jsc-git:commit`、`jsc-git:pr`、`jsc-log:worklog`、`jsc-pkg:pkg-update`(選了套件更新才用)、`jsc-hooks/hooks/comment-scope.sh`、`tools/stage-report.sh`、`git fetch --prune origin`。 |
| 完成條件 | 模型閘門退出 0、步驟 2 列出的每個在期專案都跑完自己的 sub agent,各自收在一條 PR 連結或一個記錄下來的略過原因、每個完成的專案都有一筆工作日誌,而且下一個專案開始前就存好、`wiki-contents.sh` 退出 0 且退出碼有講出來(`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、每個跨型別連結都來自退出 0 的 `wiki-url`,非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼、`MAINTAIN_CONTENTS` 該專案的「前次維護時間」是今天,其他專案那幾列一個位元組都沒變、彙整報告涵蓋每個專案、`stage-report.sh` 的輸出原樣貼給使用者。沒有專案在期時,一樣要跑收尾回報。 |
| 可驗證跡象 | 每個維護過的專案多一條新分支與一支回到 `develop` 或 `master` 的 PR;CONTENTS 存取庫的 `MAINTAIN_CONTENTS` 對應那列的「前次維護時間」變成今天;`LOG_{HASH}` 每個完成的專案多一筆條目;`$JSC_HOME/sessions/{工作階段 id}.stage` 是 `sdlc-gate.sh lock` 寫的階段鎖狀態檔;一個專案都沒完成時,`$JSC_HOME/worklog-pending/{HASH}/` 下有暫存的日誌內容檔。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock maintain` 過模型閘門,本階段不要求特定標籤,只要判定得出實際模型 id、用 `gitea.sh wiki-repo CONTENTS` 解出存取庫後讀 `MAINTAIN_CONTENTS`(`MAINTAIN` 只有目錄頁、沒有內容頁),篩出還在維護期內的專案(起始日不晚於今天,結束日為空或不早於今天)、主代理先並行對每個專案跑 `git fetch --prune origin`,切到維護分支並與 `origin/{branch}` 對齊,有落差就回報並略過該專案、之後一個專案一個專案跑,每個專案的維護都開一個 sub agent、每個專案提出至少五項維護做法給使用者挑、把改動用 `jsc-git:commit` 提交到新分支,推送後用 `jsc-git:pr` 開 PR 回步驟 3.1 那條分支、PR 開好當下寫一筆 `jsc-log:worklog`、用 `wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo}` 把該專案在 `MAINTAIN_CONTENTS` 的「前次維護時間」更新成今天,其餘欄位原樣保留、該頁指向別型別頁的連結一律取自 `gitea.sh wiki-url` 的絕對網址並依退出碼分流(`0` 用它印出的網址,`4` 填「無」或回頭補寫那頁,`5`、`7`、`8` 停下回報,`7` 絕不當成 `4` 填「無」,一律不自行組網址、也不留空白連結)、每個要放進該列的連結一律寫成 `[{文字}]({連結})`,不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`,而且寫入前先整批交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫,有 DEAD 就整列不寫並回報連不到的清單(`2` 補參數重跑、`3` 先設好 `GITEA_HOST`、`7` 金鑰失效停下回報,不得當成連不到)、主代理彙整每個專案的做法、PR 表格列與失敗原因、跑 `tools/stage-report.sh maintain` 收尾回報,目錄頁用 `--page CONTENTS:MAINTAIN_CONTENTS`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`jsc-gitea:wiki`、`jsc-gitea/tools/gitea.sh` 的 `wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/link-check.sh`、`jsc-ask:ask`、`jsc-git:commit`、`jsc-git:pr`、`jsc-log:worklog`、`jsc-pkg:pkg-update`(選了套件更新才用)、`jsc-hooks/hooks/comment-scope.sh`、`tools/stage-report.sh`、`git fetch --prune origin`。 |
| 完成條件 | 模型閘門退出 0、步驟 2 列出的每個在期專案都跑完自己的 sub agent,各自收在一條 PR 連結或一個記錄下來的略過原因、每個完成的專案都有一筆工作日誌,而且下一個專案開始前就存好、`wiki-contents.sh` 退出 0 且退出碼有講出來(`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、每個跨型別連結都來自退出 0 的 `wiki-url`,非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼、每一列寫回去之前都經 `link-check.sh` 退出 0 驗過,連結格式一律是 `[{文字}]({連結})`、`MAINTAIN_CONTENTS` 該專案的「前次維護時間」是今天,其他專案那幾列一個位元組都沒變、彙整報告涵蓋每個專案、`stage-report.sh` 的輸出原樣貼給使用者。沒有專案在期時,一樣要跑收尾回報。 |
| 可驗證跡象 | 每個維護過的專案多一條新分支與一支回到 `develop` 或 `master` 的 PR;CONTENTS 存取庫的 `MAINTAIN_CONTENTS` 對應那列的「前次維護時間」變成今天;`LOG_{HASH}` 每個完成的專案多一筆條目;`$JSC_HOME/sessions/{工作階段 id}.stage` 是 `sdlc-gate.sh lock` 寫的階段鎖狀態檔;一個專案都沒完成時,`$JSC_HOME/worklog-pending/{HASH}/` 下有暫存的日誌內容檔;`stage-report.sh` 的「寫入的 wiki 頁」表格多一欄連結驗證,逐列標「通過、無可查端點、連不到、未驗證」,該頁找不到 `[[...]]` 寫法的連結。 |
## plan
| 項目 | 內容 |
| --- | --- |
| 觸發時機 | 使用者要開新計畫、或要補強既有計畫時用。做分析或寫程式碼時不用。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock plan` 過模型閘門,本階段要 `reasoning-max` 標籤、用 `gitea.sh wiki-repo CONTENTS` 解出存取庫後讀 `PLAN_CONTENTS`,列出狀態為「未分析」的計畫與各自的 HASH、讓使用者選延伸既有計畫或建立新計畫、依 `references/consensus.md` 跑決策樹,把目標、範圍、可行性三項問到共識,每個答案都要導出下一個問題、把共識寫成「身為⋯⋯我想要⋯⋯以便⋯⋯」格式的使用者故事、套 `templates/plan-page.md` 寫入 `PLAN_{HASH}`(PLAN 存取庫)、用 `wiki-contents.sh upsert PLAN 4 {HASH}` 把這份計畫在 `PLAN_CONTENTS` 的那一列 upsert,狀態填「未分析」、計畫頁連結取自 `gitea.sh wiki-url` 的絕對網址並依退出碼分流(`0` 用它印出的網址,`4` 回頭補寫計畫頁再回來,`5`、`7`、`8` 停下回報,一律不自行組網址、也不留空白連結)、目錄列檔與待寫日誌檔一律用 Bash 的 heredoc 或 `mktemp` 產在暫存目錄,不走 Write 或 Edit 工具,因為 `write-guard.sh` 的 stage 模式只看階段鎖不看路徑,走工具會被自己的閘門擋下、跑 `tools/stage-report.sh plan` 收尾回報,目錄頁用 `--page CONTENTS:PLAN_CONTENTS`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`jsc-hooks/hooks/write-guard.sh`(claude 的 `PreToolUse` 擋寫入)、`jsc-gitea:wiki`、`jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh` 的 `wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-ask:ask`、`tools/stage-report.sh`。 |
| 完成條件 | 模型閘門退出 0 並回報實際模型 id、目標、範圍、可行性三項都達成共識,而且使用者明確確認過覆述的摘要、每個共識項目至少對應一則使用者故事、`PLAN_{HASH}` 存進 wiki 且範本要求的每個區段都有值、沒有留下未填的佔位字、`PLAN_CONTENTS` 該列狀態是「未分析」,計畫頁那格的連結來自退出 0 的 `wiki-url`(非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼),其他列一個位元組都沒變,而且 `wiki-contents.sh` 的退出碼有講出來並依碼分流(`0` 續行,`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、`stage-report.sh` 的輸出原樣貼給使用者。提前停下也要跑收尾回報。 |
| 可驗證跡象 | PLAN 存取庫多一頁或更新一頁 `PLAN_{HASH}`;CONTENTS 存取庫的 `PLAN_CONTENTS` 多一列或更新一列,狀態是「未分析」、計畫頁欄位是絕對網址;`$JSC_HOME/sessions/{工作階段 id}.stage` 是 `sdlc-gate.sh lock` 寫的階段鎖狀態檔;還沒有工作日誌時,`$JSC_HOME/worklog-pending/{HASH}/` 下有暫存的日誌內容檔。工作目錄的檔案一律不動。 |
| 關鍵步驟 | 跑 `model-tags.sh sync` 與 `sdlc-gate.sh lock plan` 過模型閘門,本階段要 `reasoning-max` 標籤、用 `gitea.sh wiki-repo CONTENTS` 解出存取庫後讀 `PLAN_CONTENTS`,列出狀態為「未分析」的計畫與各自的 HASH、讓使用者選延伸既有計畫或建立新計畫、依 `references/consensus.md` 跑決策樹,把目標、範圍、可行性三項問到共識,每個答案都要導出下一個問題、把共識寫成「身為⋯⋯我想要⋯⋯以便⋯⋯」格式的使用者故事、套 `templates/plan-page.md` 寫入 `PLAN_{HASH}`(PLAN 存取庫)、用 `wiki-contents.sh upsert PLAN 4 {HASH}` 把這份計畫在 `PLAN_CONTENTS` 的那一列 upsert,狀態填「未分析」、計畫頁連結取自 `gitea.sh wiki-url` 的絕對網址並依退出碼分流(`0` 用它印出的網址,`4` 回頭補寫計畫頁再回來,`5`、`7`、`8` 停下回報,一律不自行組網址、也不留空白連結)、每個要放進頁面或目錄列的連結一律寫成 `[{文字}]({連結})`,不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`,而且寫入前先整批交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫,有 DEAD 就整筆不寫並回報連不到的清單(`2` 補參數重跑、`3` 先設好 `GITEA_HOST`、`7` 金鑰失效停下回報,不得當成連不到)、目錄列檔與待寫日誌檔一律用 Bash 的 heredoc 或 `mktemp` 產在暫存目錄,不走 Write 或 Edit 工具,因為 `write-guard.sh` 的 stage 模式只看階段鎖不看路徑,走工具會被自己的閘門擋下、跑 `tools/stage-report.sh plan` 收尾回報,目錄頁用 `--page CONTENTS:PLAN_CONTENTS`。 |
| 外部呼叫 | `jsc-cli/tools/model-tags.sh sync`、`jsc-hooks/hooks/sdlc-gate.sh lock`、`jsc-hooks/hooks/write-guard.sh`(claude 的 `PreToolUse` 擋寫入)、`jsc-gitea:wiki`、`jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh` 的 `wiki-repo` 與 `wiki-url`、`jsc-gitea/tools/wiki-contents.sh upsert`、`jsc-gitea/tools/link-check.sh`、`jsc-ask:ask`、`tools/stage-report.sh`。 |
| 完成條件 | 模型閘門退出 0 並回報實際模型 id、目標、範圍、可行性三項都達成共識,而且使用者明確確認過覆述的摘要、每個共識項目至少對應一則使用者故事、`PLAN_{HASH}` 存進 wiki 且範本要求的每個區段都有值、沒有留下未填的佔位字、`PLAN_CONTENTS` 該列狀態是「未分析」,計畫頁那格的連結來自退出 0 的 `wiki-url`(非 0 依 `4`、`5`、`7`、`8` 分流並講出退出碼),並經 `link-check.sh` 退出 0 驗過,格式是 `[{文字}]({連結})`,其他列一個位元組都沒變,而且 `wiki-contents.sh` 的退出碼有講出來並依碼分流(`0` 續行,`1`、`3`、`7`、`8` 停下回報,`2` 修參數重跑,`4` 在本技能一律帶範本的呼叫方式下不會出現,真的出現就確認 plugin 安裝完整後重跑)、`stage-report.sh` 的輸出原樣貼給使用者。提前停下也要跑收尾回報。 |
| 可驗證跡象 | PLAN 存取庫多一頁或更新一頁 `PLAN_{HASH}`;CONTENTS 存取庫的 `PLAN_CONTENTS` 多一列或更新一列,狀態是「未分析」、計畫頁欄位是 `[{文字}]({連結})` 格式的絕對網址;`$JSC_HOME/sessions/{工作階段 id}.stage` 是 `sdlc-gate.sh lock` 寫的階段鎖狀態檔;還沒有工作日誌時,`$JSC_HOME/worklog-pending/{HASH}/` 下有暫存的日誌內容檔;`stage-report.sh` 的「寫入的 wiki 頁」表格多一欄連結驗證,逐列標「通過、無可查端點、連不到、未驗證」,頁面上找不到 `[[...]]` 寫法的連結。工作目錄的檔案一律不動。 |
+12 -1
View File
@@ -19,6 +19,17 @@
`--page` 的 `TYPE` 是頁名前綴(`PLAN`、`ANALYZE`、`DELIVER`、`REPO`、`MAINTAIN`、`LOG`)。腳本用它解析
該類型的 wiki 存取庫,再換成絕對網址,所以跨存取庫的頁面也連得到。目錄頁(`*_CONTENTS`)也算寫入,要列。
## 連結驗證
連結一律寫成 `[{文字}]({連結})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不自行組路徑,
也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`。寫進任何頁面之前,每個連結先交給
`jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入;有 DEAD 就不寫,把連不到的清單回報給使用者。
`stage-report.sh` 印出來的那張連結清單也會再驗一次,逐列標「通過、無可查端點、連不到、未驗證」。
這一道是複查,不是上面那道關卡的替代品:頁面在收尾之前就寫完了,所以這裡只註記、不阻擋。
金鑰失效(`link-check.sh` 結束碼 7)標成「未驗證」,不標成「連不到」——把金鑰問題寫成死連結,
下一手就會照著去刪還活著的頁。
## 實作階段多回報四項
| 項目 | 選項 | 腳本自己查的部分 |
@@ -47,6 +58,6 @@
| 碼 | 意思 | 呼叫端要做的事 |
| --- | --- | --- |
| 0 | 回報完整 | 把輸出原樣貼給使用者 |
| 1 | 有警告(缺工作日誌、或實作階段沒有 PR) | 一樣把輸出貼給使用者。**這是警告不是阻擋**,階段的工作已經做完了 |
| 1 | 有警告(缺工作日誌、實作階段沒有 PR、或清單裡有連不到的連結) | 一樣把輸出貼給使用者,連不到的連結照著修。**這是警告不是阻擋**,階段的工作已經做完了 |
| 2 | 用法錯誤 | 修正參數重跑 |
| 3 | 找不到 `gitea.sh` 或 `sdlc-gate.sh` | 修好相依關係再重跑 |
+20 -8
View File
@@ -10,7 +10,7 @@ This skill is a **logic-only** stage: never output code, and **never modify any
`{HASH}` = the shared wiki hash for `{owner}/{repo}`, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). It prints the **full 40-character uppercase SHA-1** of its input — no truncation to 8 characters, no prefix rewrite. Never shorten it by hand: a shortened name points at a page nobody else writes to. `REPO_{HASH}` runs the same command over that repository's own `{owner}/{repo}`, so a multi-repository analysis holds one inventory page per repository.
**Content pages and directory pages live in different wiki repos.** `ANALYZE_{HASH}` sits in the repo `jsc-gitea/tools/gitea.sh wiki-repo ANALYZE` resolves, `REPO_{HASH}` in the one `gitea.sh wiki-repo REPO` resolves. All three directory pages — `ANALYZE_CONTENTS`, `PLAN_CONTENTS` and `REPO_CONTENTS` — sit in the repo `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_ANALYZE`, `JSC_WIKI_REPO_PLAN` or `JSC_WIKI_REPO_REPO`. Because directory and content pages sit in different wikis, every directory row links its content page by the absolute URL from `gitea.sh wiki-url {content repo} {page}`; `[[...]]` resolves only inside one wiki and would dead-link from the directory.
**Content pages and directory pages live in different wiki repos.** `ANALYZE_{HASH}` sits in the repo `jsc-gitea/tools/gitea.sh wiki-repo ANALYZE` resolves, `REPO_{HASH}` in the one `gitea.sh wiki-repo REPO` resolves. All three directory pages — `ANALYZE_CONTENTS`, `PLAN_CONTENTS` and `REPO_CONTENTS` — sit in the repo `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_ANALYZE`, `JSC_WIKI_REPO_PLAN` or `JSC_WIKI_REPO_REPO`. Every directory row links its content page by the absolute URL from `gitea.sh wiki-url {content repo} {page}`, written as `[{text}]({url})` — one link syntax, whichever wiki the two pages sit in. The syntax and the check that runs before every write: "Every link is checked before it reaches a page" below.
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 11 still runs after such a stop.
@@ -28,7 +28,7 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
1. Every file in the working directory, at the commit `origin/{source-branch}` points to (verified in step 4).
2. **Reuse an existing method or endpoint unless its logic cannot satisfy the requirement**:
- Check the `REPO_{HASH}` inventory page first, in the REPO wiki repo. **Its `{HASH}` is `hash-id` over that repository's own `{owner}/{repo}`** — one inventory page per repository, so a multi-repository analysis holds one `REPO_{HASH}` per repository and never one shared page. Re-inventory when the feature or endpoint is missing, or when the recorded commit sha differs from the current one.
- Re-inventory **MUST run as a sub agent**: analyze the repository's features and endpoints, attach the current commit sha, write back to `REPO_{HASH}` with `templates/repo-page.md`, then upsert this repository's row in `REPO_CONTENTS` with `jsc-gitea/tools/wiki-contents.sh upsert REPO 1 {owner}/{repo} {row file} templates/repo-contents.md`. The row follows `templates/repo-contents.md`, and the key is column 1, the repository name, written exactly as the row file writes it. Its 盤點頁 cell holds the absolute URL from `gitea.sh wiki-url {REPO repo} REPO_{HASH}` — take that URL first and branch on the exit code per "Every cross-repo link comes from `wiki-url`" below, because the row must never carry an empty link cell.
- Re-inventory **MUST run as a sub agent**: analyze the repository's features and endpoints, attach the current commit sha, write back to `REPO_{HASH}` with `templates/repo-page.md`, then upsert this repository's row in `REPO_CONTENTS` with `jsc-gitea/tools/wiki-contents.sh upsert REPO 1 {owner}/{repo} {row file} templates/repo-contents.md`. The row follows `templates/repo-contents.md`, and the key is column 1, the repository name, written exactly as the row file writes it. Its 盤點頁 cell holds the absolute URL from `gitea.sh wiki-url {REPO repo} REPO_{HASH}`, written as `[{text}]({url})` — take that URL first, check it with `jsc-gitea/tools/link-check.sh`, and branch on both exit codes per "Every link comes from `wiki-url`, and is checked before it is written" below, because the row must never carry an empty or dead link cell.
- Never hand-edit `REPO_CONTENTS`, and never touch a row belonging to another repository. Branch on the script's exit code — see "Contents pages are appended, never overwritten" below. `REPO_{HASH}` is a content page for one repository, so rewriting it whole is correct; the directory page around it is not.
- For each reuse candidate, confirm the file path and method name first, then analyze whether its logic fits the requirement. Reject a candidate only for a stated reason, and record both the candidate and that reason in the analysis page's 複用決策 field.
@@ -41,11 +41,11 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
2. Every scenario states its input, its expected result, its data source and the work package it belongs to.
3. **Every TDD todo is one whole cycle**: one seam, one failing test first, one minimal implementation, one green verification, and a post-green refactor where it is needed. Never split the red test, the minimal implementation and the green verification into separate todos. Seams and anti-patterns: `references/tdd.md`.
4. Completion condition: every user story has scenarios under `## 使用者故事驗收計畫`; every scenario carries an explicit acceptance method and data source; the scenario count matches the story's complexity; every work package has its own subsection under `## 測試計畫(TDD)`; every implementation work package holds at least one test-first `[ ]` todo; and every todo states its seam, its acceptance scenario, the behaviour the test asserts, the minimal implementation scope and how green is verified. A pure delivery package may use document-verification or sample-data-verification todos instead, and still states the test evidence or the review evidence that proves the spec is usable.
10. **Write the analysis page and its catalogue entries in one wiki pass.** Apply `templates/analyze-page.md` to create or update the analysis page **in the ANALYZE wiki repo** and write it back via `jsc-gitea:wiki`; the page content is Traditional Chinese, exactly as the template dictates. Both directory rows then go through `jsc-gitea/tools/wiki-contents.sh`, never a hand-edited page:
- `jsc-gitea/tools/wiki-contents.sh upsert ANALYZE 3 {HASH} {row file} templates/analyze-contents.md` — the row follows `templates/analyze-contents.md` and the key is the HASH column, column 3. Its 分析頁 cell holds the absolute URL from `gitea.sh wiki-url {ANALYZE repo} ANALYZE_{HASH}`: take that URL after the analysis page is saved and branch on the exit code per "Every cross-repo link comes from `wiki-url`" below, because the row must never carry an empty link cell.
10. **Write the analysis page and its catalogue entries in one wiki pass.** Apply `templates/analyze-page.md` to create or update the analysis page **in the ANALYZE wiki repo** and write it back via `jsc-gitea:wiki`; the page content is Traditional Chinese, exactly as the template dictates. **Every link the page carries — the plan page, the inventory page, issues, anything external — is written as `[{text}]({url})` and passes `jsc-gitea/tools/link-check.sh` before the write**, per "Every link comes from `wiki-url`, and is checked before it is written" below. Both directory rows then go through `jsc-gitea/tools/wiki-contents.sh`, never a hand-edited page:
- `jsc-gitea/tools/wiki-contents.sh upsert ANALYZE 3 {HASH} {row file} templates/analyze-contents.md` — the row follows `templates/analyze-contents.md` and the key is the HASH column, column 3. Its 分析頁 cell holds the absolute URL from `gitea.sh wiki-url {ANALYZE repo} ANALYZE_{HASH}`, written as `[{text}]({url})`: take that URL after the analysis page is saved, check it with `jsc-gitea/tools/link-check.sh`, and branch on both exit codes per "Every link comes from `wiki-url`, and is checked before it is written" below, because the row must never carry an empty or dead link cell.
- `jsc-gitea/tools/wiki-contents.sh upsert PLAN 4 {HASH} {row file} templates/plan-contents.md` — rebuild that plan's row from the one the page already holds, change only its status to the literal 「已分析」, and keep every other cell (including the absolute plan-page link) byte-for-byte as it was. The key is the HASH column, column 4.
Both runs branch on the exit code per "Contents pages are appended, never overwritten" below. Completion condition: the analysis page is saved on the wiki carrying every section the template dictates — the source branch, the head sha and the 未決項 section (「無」 when there is none) included — every `wiki-url` call this step made returned 0 and its URL is the one in the row, both `wiki-contents.sh` runs exited 0, and `ANALYZE_CONTENTS` shows this analysis's row while `PLAN_CONTENTS` shows the literal 「已分析」.
Both runs branch on the exit code per "Contents pages are appended, never overwritten" below. Completion condition: the analysis page is saved on the wiki carrying every section the template dictates — the source branch, the head sha and the 未決項 section (「無」 when there is none) included — every `wiki-url` call this step made returned 0 and its URL is the one in the row, every link written by this step was cleared by a `link-check.sh` run that exited 0, both `wiki-contents.sh` runs exited 0, and `ANALYZE_CONTENTS` shows this analysis's row while `PLAN_CONTENTS` shows the literal 「已分析」.
11. **Stage report — the last thing this stage does, including every early stop** (the model gate blocked, the working tree did not match `origin/{source-branch}`, no plan was selectable, a wiki read or write failed). Run `tools/stage-report.sh analyze` with one `--page TYPE:{page}` per wiki page this run wrote — `--page ANALYZE:ANALYZE_{HASH}`, `--page CONTENTS:ANALYZE_CONTENTS`, `--page CONTENTS:PLAN_CONTENTS`, and `--page REPO:REPO_{HASH}` plus `--page CONTENTS:REPO_CONTENTS` when a re-inventory happened. **Every directory page takes the `CONTENTS` type**: the script resolves each page's repo from the TYPE you pass, and a directory page passed under its old type resolves the wrong repo and prints no URL. Add `--worklog` and `--worklog-heading` when a work log entry exists. No work log yet: write this stage's log content to a file — with a Bash heredoc or `mktemp` per Hard limits, never with `Write` or `Edit` — and pass `--pending-file {file} --log-hash {HASH}` so it is held for the next `jsc-log:worklog` run. 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.
## Contents pages are appended, never overwritten
@@ -68,9 +68,11 @@ Why 7 and 8 abort: both mean the old content is unknown, not that the page is mi
Completion condition: every directory-page write this stage made names the `wiki-contents.sh` exit code it branched on, and no directory page was created on any code other than 4.
## Every cross-repo link comes from `wiki-url`
## Every link comes from `wiki-url`, and is checked before it is written
Both directory rows this stage writes — `REPO_CONTENTS` in step 5.2 and `ANALYZE_CONTENTS` in step 10 — link a content page that lives in another wiki repo, so the cell holds the absolute URL `gitea.sh wiki-url {content repo} {page}` printed and nothing else. Run it **after** that content page is saved, and branch on its exit code; this is the same branching `jsc-log:worklog` runs over the same call.
**One syntax.** Every link this stage writes — on `ANALYZE_{HASH}` and `REPO_{HASH}`, in the `ANALYZE_CONTENTS`, `PLAN_CONTENTS` and `REPO_CONTENTS` rows, 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.
Run `wiki-url` **after** the content page it names is saved, and branch on its exit code; this is the same branching `jsc-log:worklog` runs over the same call.
| Exit | What this step does |
| --- | --- |
@@ -82,7 +84,17 @@ Both directory rows this stage writes — `REPO_CONTENTS` in step 5.2 and `ANALY
Never let an empty string stand in for the URL. A row whose link cell is empty is a directory entry that points nowhere, the reader has no way to reach the page it names, and the next run replaces that row as if it were correct.
Completion condition: every row this stage upserted carries a URL that came out of a `wiki-url` run that exited 0, and every non-zero code was branched on as this table says.
**Checked before it is written.** Collect every link the page or 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 step does |
| --- | --- |
| 0 | every link answers — write the page or 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 a row rewritten on that reading loses the links that were fine |
Completion condition: every row this stage upserted carries a URL that came out of a `wiki-url` run that exited 0, every page and row this stage wrote was cleared by a `link-check.sh` run that exited 0, and every non-zero code from either script was branched on as these tables say.
## Delivery package is WP-01
+23 -7
View File
@@ -9,7 +9,7 @@ Goal: complete the analysis page's todos one by one; **update the wiki status im
`jsc-gitea/tools/hash-id` prints the **full 40-character uppercase SHA-1** of its input — no truncation to 8 characters, no prefix rewrite. Every `{HASH}` this stage builds carries that full length: the page names, the worktree path and the work ticket of step 6 alike. Never shorten one by hand.
**Content pages and directory pages live in different wiki repos.** `ANALYZE_{HASH}` sits in the repo `jsc-gitea/tools/gitea.sh wiki-repo ANALYZE` resolves and `DELIVER_{HASH}` in the one `gitea.sh wiki-repo DELIVER` resolves, while the directory pages `ANALYZE_CONTENTS`, `DELIVER_CONTENTS` and `MAINTAIN_CONTENTS` all sit in the repo `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 the page type's own variable. Because directory and content pages sit in different wikis, every directory row links its content page by the absolute URL from `gitea.sh wiki-url {content repo} {page}`; `[[...]]` resolves only inside one wiki and would dead-link from the directory.
**Content pages and directory pages live in different wiki repos.** `ANALYZE_{HASH}` sits in the repo `jsc-gitea/tools/gitea.sh wiki-repo ANALYZE` resolves and `DELIVER_{HASH}` in the one `gitea.sh wiki-repo DELIVER` resolves, while the directory pages `ANALYZE_CONTENTS`, `DELIVER_CONTENTS` and `MAINTAIN_CONTENTS` all sit in the repo `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 the page type's own variable. Every directory row links its content page by the absolute URL from `gitea.sh wiki-url {content repo} {page}`, written as `[{text}]({url})` — one link syntax, whichever wiki the two pages sit in. The syntax and the check that runs before every write: "Every link is checked before it reaches a page" below.
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 13 still runs after such a stop.
@@ -64,7 +64,7 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
3. **Start both audits together and let them run side by side** — they read the same diff and neither one's verdict changes the other's input, so the closing wait costs one audit, not two. Completion condition: both audits have returned, and both cleared — a pass from `code-review`, and either a pass or a reported skip from the API document audit.
11. **One work package finished → commit, push, PR back to the source branch, then hold on that PR until it merges**:
1. Call `jsc-git:pr` from inside the worktree, **passing `{source-branch}` as the base branch**. One package, one PR; the branch ladder and how the base is derived are in `references/branch.md`.
2. Write the PR URL and number into that work package's PR column on the analysis page and save it back to the wiki, so the next run of this skill can find it (step 2).
2. Write the PR URL and number into that work package's PR column on the analysis page as `[{text}]({url})`, **checked with `jsc-gitea/tools/link-check.sh` before the save** — see "Every link is checked before it reaches a page" below — and save the page back to the wiki, so the next run of this skill can find it (step 2).
3. Run `jsc-sdlc/tools/wp-gate.sh lock {owner}/{repo} {index} --wp {wp-number}`. The lock is what makes step 2's gate hold across work sessions — a new session starts blocked until that PR merges — and `--wp` is what hangs this PR on the claim from step 4.5, so `owns` can keep other sessions off it. Leave `--wp` out and every session is free to touch this PR. Branch on the exit code: exit 0 (`status=locked`) → proceed. Exit 2 (`status=usage`) → fix the arguments and run it again. Exit 3 (`status=missing-dep`) → the lock could not be recorded; report it and stop, because without the lock the next session starts unblocked on a PR that has not merged. Completion condition: the script printed `status=locked` and named the work package.
4. **The work package is finished the moment its PR is open — call `jsc-log:worklog` now**, under the Rules section's one-task-one-entry rule, reusing the PLAN and ANALYZE wiki repositories and page URLs resolved for this package in step 2.10. Completion condition: the entry is saved on `LOG_{HASH}` before the wait starts.
5. **Wait for the merge with `jsc-gitea/tools/pr-watch.sh {owner}/{repo} {index}`** — it polls every 60 seconds (`JSC_PR_WATCH_INTERVAL` overrides), never times out, and never repeats a comment it already reported. Branch on its exit code:
@@ -77,7 +77,7 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
12. **Deliver the document, then register for maintenance** — one closing pass over the two questions this stage owes the user. A finished work package is a delivery, so **always ask before producing it; never pick a format silently and never skip either question**:
1. Ask per `jsc-ask:ask` rules which delivery format to produce. The options are fixed: **a `DELIVER_{HASH}` wiki page** or **a Gitea issue comment**. State the impact scope on each (the wiki page lives beside the plan and analysis pages; the issue comment reaches whoever follows that issue).
2. Both formats use the same structure — `templates/deliver-page.md`, in Traditional Chinese. Only the destination differs. Sample values and personal-data handling: `references/deliver-formats.md`.
3. Wiki page: write `DELIVER_{HASH}` into the DELIVER wiki repo through `jsc-gitea:wiki`, where `{HASH}` comes from `jsc-gitea/tools/hash-id` over `{owner}/{repo}` plus the work package number (for example `plugins/sdlc#WP-01`), so each work package gets its own page instead of overwriting the previous one. That input is what keeps the pages apart, and the full 40 characters `hash-id` prints are what the page is named — hashing the repository alone, or shortening the result, puts two work packages on one page again. Then upsert this work package's row with `jsc-gitea/tools/wiki-contents.sh upsert DELIVER 6 {HASH} {row file} templates/deliver-contents.md`: the row follows `templates/deliver-contents.md` and the key is the HASH column, column 6, written exactly as the row file writes it. Its 交付頁 cell holds the absolute URL from `gitea.sh wiki-url {DELIVER repo} DELIVER_{HASH}` — run that **after** the delivery page is saved and branch on its exit code, the same branching `jsc-log:worklog` runs over the same call:
3. Wiki page: write `DELIVER_{HASH}` into the DELIVER wiki repo through `jsc-gitea:wiki` — **every link that page carries is written as `[{text}]({url})` and passes `jsc-gitea/tools/link-check.sh` before the write**, per "Every link is checked before it reaches a page" below — where `{HASH}` comes from `jsc-gitea/tools/hash-id` over `{owner}/{repo}` plus the work package number (for example `plugins/sdlc#WP-01`), so each work package gets its own page instead of overwriting the previous one. That input is what keeps the pages apart, and the full 40 characters `hash-id` prints are what the page is named — hashing the repository alone, or shortening the result, puts two work packages on one page again. Then upsert this work package's row with `jsc-gitea/tools/wiki-contents.sh upsert DELIVER 6 {HASH} {row file} templates/deliver-contents.md`: the row follows `templates/deliver-contents.md` and the key is the HASH column, column 6, written exactly as the row file writes it. Its 交付頁 cell holds the absolute URL from `gitea.sh wiki-url {DELIVER repo} DELIVER_{HASH}`, written as `[{text}]({url})` — run that **after** the delivery page is saved and branch on its exit code, the same branching `jsc-log:worklog` runs over the same call:
| Exit | What this step does |
| --- | --- |
@@ -87,10 +87,10 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
| 7 | the key is invalid or lacks permission (HTTP 401/403) — stop and report the key problem. Never read this as exit 4: the delivery page is alive, and treating it as absent records a delivered work package as one that was never delivered |
| 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 row whose 交付頁 cell is empty names a delivery nobody can open, and the next run replaces that row as if it were correct. Branch on the upsert's own exit code per "Contents pages are appended, never overwritten" below, and never hand-edit the directory page.
4. Issue comment: confirm the issue number with the user (propose the one referenced by the work package or the PR; never guess), then post via `jsc-gitea/tools/gitea.sh api POST /repos/{owner}/{repo}/issues/{n}/comments` with the body passed in as a UTF-8 file — real newlines, never a literal `\n`.
5. With the delivery produced, ask per `jsc-ask:ask` rules whether to register this project for maintenance: upsert this repository's row with `jsc-gitea/tools/wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo} {row file} templates/maintain-contents.md`, the row built from `templates/maintain-contents.md` and the key column 1, the repository name. Required: repository `{owner}/{repo}`, maintenance method, start date. Optional: end date (NULL = maintain forever), last-maintained time. `MAINTAIN` has no content page — this directory page is the whole record — so branch on the exit code per "Contents pages are appended, never overwritten" below and never hand-edit it.
6. Completion condition: the chosen delivery format has actually been produced and you have reported where it landed (wiki page name, or the comment URL); on the wiki-page format the `wiki-url` call returned 0 and its URL is the one in the 交付頁 cell, with any non-zero code branched on as sub-step 12.3 says; **and** the user has answered the maintenance question with a chosen registration saved on the wiki.
Never let an empty string stand in for the URL: a row whose 交付頁 cell is empty names a delivery nobody can open, and the next run replaces that row as if it were correct. **Check that URL with `jsc-gitea/tools/link-check.sh` and build the row only on exit 0** — a link that does not answer never goes into a directory everyone else reads. Branch on the upsert's own exit code per "Contents pages are appended, never overwritten" below, and never hand-edit the directory page.
4. Issue comment: confirm the issue number with the user (propose the one referenced by the work package or the PR; never guess), then post via `jsc-gitea/tools/gitea.sh api POST /repos/{owner}/{repo}/issues/{n}/comments` with the body passed in as a UTF-8 file — real newlines, never a literal `\n`. Every link in that body is written as `[{text}]({url})` and passes `jsc-gitea/tools/link-check.sh` before the post: a comment is as hard to correct as a page once other people have read it.
5. With the delivery produced, ask per `jsc-ask:ask` rules whether to register this project for maintenance — any link that row carries is written as `[{text}]({url})` and passes `jsc-gitea/tools/link-check.sh` before the upsert: upsert this repository's row with `jsc-gitea/tools/wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo} {row file} templates/maintain-contents.md`, the row built from `templates/maintain-contents.md` and the key column 1, the repository name. Required: repository `{owner}/{repo}`, maintenance method, start date. Optional: end date (NULL = maintain forever), last-maintained time. `MAINTAIN` has no content page — this directory page is the whole record — so branch on the exit code per "Contents pages are appended, never overwritten" below and never hand-edit it.
6. Completion condition: the chosen delivery format has actually been produced and you have reported where it landed (wiki page name, or the comment URL); on the wiki-page format the `wiki-url` call returned 0 and its URL is the one in the 交付頁 cell, with any non-zero code branched on as sub-step 12.3 says; every link written by this step was cleared by a `link-check.sh` run that exited 0; **and** the user has answered the maintenance question with a chosen registration saved on the wiki.
13. **Stage report — the last thing this stage does, including every early stop** (the work package gate blocked, no work package was selectable, the source branch was missing from the remote, a wiki read or write failed). Run `tools/stage-report.sh implement` with:
- one `--page TYPE:{page}` per wiki page this run wrote — `--page ANALYZE:ANALYZE_{HASH}`, `--page DELIVER:DELIVER_{HASH}`, `--page CONTENTS:DELIVER_CONTENTS`, `--page CONTENTS:MAINTAIN_CONTENTS`. **Every directory page takes the `CONTENTS` type**: the script resolves each page's repo from the TYPE you pass, and a directory page passed under its old type resolves the wrong repo and prints no URL;
- `--worklog` and `--worklog-heading` pointing at the entry step 11.4 wrote — following the Rules section's one-task-one-entry rule, a stage that finished anything already has one. `--pending-file {file} --log-hash {HASH}` is the fallback for a stage that stopped before any task finished: it holds the content for the next `jsc-log:worklog` run, and held content is not a written log;
@@ -98,6 +98,22 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
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 it names the worktree, all three branches and every wiki page this run wrote.
## Every link is checked before it reaches a page
**One syntax.** Every link this stage writes — on `DELIVER_{HASH}`, in the analysis page's PR column, in the `DELIVER_CONTENTS` and `MAINTAIN_CONTENTS` rows, in an issue comment, 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 page, the row or the comment 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 step does |
| --- | --- |
| 0 | every link answers — write the page, upsert the row, post the comment |
| 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 a page rewritten on that reading loses the links that were fine |
Completion condition: every page, row and comment 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
`DELIVER_CONTENTS` and `MAINTAIN_CONTENTS` are shared directories in the CONTENTS wiki repo: every row on them belongs to somebody's work package or repository, and this run reads none of those rows from anywhere else. So every write to them is an upsert of one row — never a whole-page overwrite, and never a row this run does not own. `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.
+18 -2
View File
@@ -7,7 +7,7 @@ description: 'SDLC maintenance stage. Gate on capability tags enforced in code b
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. Any link it carries to a page of another type is the absolute URL from `gitea.sh wiki-url {that type's repo} {page}`, because `[[...]]` resolves only inside one wiki. Branch on that command's exit code every time — this is the same branching `jsc-log:worklog` runs over the same call:
**`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 |
| --- | --- |
@@ -39,10 +39,26 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
3. **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.sh` compares each file after it is written and prints a warning — fix the flagged line at once, then carry on — and `jsc-git:commit` sweeps 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.
4. Commit the changes to a new branch per `jsc-git:commit`, push, then open a PR per `jsc-git:pr` back 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 in `jsc-meta/references/pr-report.md`.
5. **One project's maintenance is one finished task — call `jsc-log:worklog` right 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 same `LOG_{HASH}` page. Content parked earlier by `tools/stage-report.sh --pending-file` is 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 on `LOG_{HASH}` before the next project's sub agent starts.
6. Update the project's last-maintained field (the zh-TW column 「前次維護時間」) in `MAINTAIN_CONTENTS` to today with `jsc-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. Branch on the exit code per "Contents pages are appended, never overwritten" below. Completion condition: the script exited 0, `MAINTAIN_CONTENTS` shows today's date in 「前次維護時間」 for that project, and every other project's row is byte-for-byte unchanged.
6. Update the project's last-maintained field (the zh-TW column 「前次維護時間」) in `MAINTAIN_CONTENTS` to today with `jsc-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 through `jsc-gitea/tools/link-check.sh` before 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 a `link-check.sh` run that exited 0, `MAINTAIN_CONTENTS` shows today's date in 「前次維護時間」 for that project, and every other project's row is byte-for-byte unchanged.
4. 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.
5. **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 maintain` with one `--page TYPE:{page}` per wiki page this run wrote — that is `--page CONTENTS:MAINTAIN_CONTENTS`, under the `CONTENTS` type, because the script resolves each page's repo from the TYPE you pass and `MAINTAIN:` would resolve the wrong repo and print no URL — plus `--worklog` and `--worklog-heading` pointing 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 next `jsc-log:worklog` run, 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.
+19 -3
View File
@@ -10,7 +10,7 @@ This skill is a **logic-only** stage: never output code, and **never modify any
`{HASH}` = the shared wiki hash for `{owner}/{repo}` used to build the `PLAN_{HASH}` page name, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). It prints the **full 40-character uppercase SHA-1** of its input — no truncation to 8 characters, no prefix rewrite. Never shorten it by hand: a shortened name points at a page nobody else writes to.
**The two pages this stage touches live in two different wiki repos.** The content page `PLAN_{HASH}` sits in the repo `jsc-gitea/tools/gitea.sh wiki-repo PLAN` resolves. The directory page `PLAN_CONTENTS` sits in the repo `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_PLAN`. Because the two pages sit in different wikis, the directory row links the plan page by the absolute URL from `gitea.sh wiki-url {PLAN repo} PLAN_{HASH}`: `[[...]]` resolves only inside one wiki and would dead-link from the directory.
**The two pages this stage touches live in two different wiki repos.** The content page `PLAN_{HASH}` sits in the repo `jsc-gitea/tools/gitea.sh wiki-repo PLAN` resolves. The directory page `PLAN_CONTENTS` sits in the repo `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_PLAN`. The directory row links the plan page by the absolute URL from `gitea.sh wiki-url {PLAN repo} PLAN_{HASH}`, written as `[{text}]({url})` — one link syntax, whichever wiki the two pages sit in. The syntax and the check that runs before every write: "Every link is checked before it reaches a page" below.
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 8 still runs after such a stop.
@@ -28,7 +28,7 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
Completion condition: all three items are settled under both conditions of `references/consensus.md` — no remaining unknown that would change the output, **and** the user's explicit confirmation of the summary you read back.
5. Turn the consensus into **user stories** (the zh-TW pattern 「身為⋯⋯我想要⋯⋯以便⋯⋯」), one per line. Completion condition: every consensus item is covered by at least one user story in that pattern, and no user story rests on an unanswered question.
6. Apply `templates/plan-page.md` to create or update the plan page **in the PLAN wiki repo**, and write it back via `jsc-gitea:wiki`. The page content is Traditional Chinese, exactly as the template dictates. Completion condition: the page is saved on the wiki and carries every section the template dictates — goal, scope, feasibility, user stories and the consensus summary — with no placeholder left unfilled.
6. Apply `templates/plan-page.md` to create or update the plan page **in the PLAN wiki repo**, and write it back via `jsc-gitea:wiki`. The page content is Traditional Chinese, exactly as the template dictates. **Every link on that page is written as `[{文字}]({連結})` and passes `jsc-gitea/tools/link-check.sh` before the write** — see "Every link is checked before it reaches a page" below. Completion condition: the page is saved on the wiki and carries every section the template dictates — goal, scope, feasibility, user stories and the consensus summary — with no placeholder left unfilled, and the page holds no link that the check did not clear.
7. Upsert this plan's row in `PLAN_CONTENTS` with `jsc-gitea/tools/wiki-contents.sh`; never hand-edit the directory page. **Take the plan page's absolute link from `gitea.sh wiki-url {PLAN repo} PLAN_{HASH}` first, and branch on that command's exit code before the row is built** — the same branching `jsc-log:worklog` runs over this call:
| Exit | What this step does |
@@ -39,9 +39,25 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
| 7 | the key is invalid or lacks permission (HTTP 401/403) — stop and report the key problem. Never read this as exit 4: the plan page is alive, and treating it as absent records a live page as one that was never written |
| 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 row whose link cell is empty is a directory entry that points nowhere, and the next run overwrites it as if it were correct. Then build one file holding the single row from `templates/plan-contents.md` — the plan name, that absolute link, the code repository, the HASH, the literal 「未分析」 and the creation date; produce that file per Hard limits, with a Bash heredoc or `mktemp`, never with `Write` or `Edit`. Then run `jsc-gitea/tools/wiki-contents.sh upsert PLAN 4 {HASH} {row file} templates/plan-contents.md`. The key is the HASH column, column 4, written exactly as the row file writes it; a key typed by hand appends a second row for the same plan. Branch on the exit code per "Contents pages are appended, never overwritten" below. Completion condition: `wiki-url` returned 0 and its URL is the one in the row, the upsert exited 0, `PLAN_CONTENTS` shows this plan's row with the literal 「未分析」 and that absolute plan-page link, and you have reported both exit codes plus whether the script printed `updated` or `added`.
Never let an empty string stand in for the URL: a row whose link cell is empty is a directory entry that points nowhere, and the next run overwrites it as if it were correct. **Then check that URL with `jsc-gitea/tools/link-check.sh` and build the row only on exit 0** — see "Every link is checked before it reaches a page" below; a link that does not answer never goes into a directory everyone else reads. Then build one file holding the single row from `templates/plan-contents.md` — the plan name, that absolute link written as `[{文字}]({連結})`, the code repository, the HASH, the literal 「未分析」 and the creation date; produce that file per Hard limits, with a Bash heredoc or `mktemp`, never with `Write` or `Edit`. Then run `jsc-gitea/tools/wiki-contents.sh upsert PLAN 4 {HASH} {row file} templates/plan-contents.md`. The key is the HASH column, column 4, written exactly as the row file writes it; a key typed by hand appends a second row for the same plan. Branch on the exit code per "Contents pages are appended, never overwritten" below. Completion condition: `wiki-url` returned 0 and its URL is the one in the row, `link-check.sh` returned 0 over that URL, the upsert exited 0, `PLAN_CONTENTS` shows this plan's row with the literal 「未分析」 and that absolute plan-page link, and you have reported all three exit codes plus whether the script printed `updated` or `added`.
8. **Stage report — the last thing this stage does, including every early stop** (the model gate blocked, no plan was selectable, a wiki read or write failed). Run `tools/stage-report.sh plan` with one `--page TYPE:{page}` per wiki page this run wrote — `--page PLAN:PLAN_{HASH}` for the content page and `--page CONTENTS:PLAN_CONTENTS` for the directory page, because the script resolves each page's repo from the TYPE you pass and the two pages no longer share one — plus `--worklog` and `--worklog-heading` when a work log entry exists. No work log yet: write this stage's log content to a file — with a Bash heredoc or `mktemp` per Hard limits, never with `Write` or `Edit` — and pass `--pending-file {file} --log-hash {HASH}` so it is held for the next `jsc-log:worklog` run. 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 — on `PLAN_{HASH}`, in the `PLAN_CONTENTS` row, in the stage report — is written as `[{文字}]({連結})`. The `{連結}` is the absolute URL `jsc-gitea/tools/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 page 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 the page 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 — write the page |
| 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 a page rewritten on that reading loses the links that were fine |
Completion condition: every page 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
`PLAN_CONTENTS` is a shared directory in the CONTENTS wiki repo: every row on it belongs to somebody's plan, and this run reads none of those rows from anywhere else. So the write is an upsert of one row on top of the content just read — never a whole-page overwrite, and never a row that belongs to another plan. `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.
+2 -1
View File
@@ -2,7 +2,8 @@
> 存放位置:本頁是目錄頁,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫(沒設定就退回 `JSC_WIKI_REPO`,都沒設定就停)。分析頁 `ANALYZE_{HASH}` 住在 `JSC_WIKI_REPO_ANALYZE` 那一邊,兩者不同存取庫。
> 寫入語意:一列代表一份分析頁。一律用 `jsc-gitea/tools/wiki-contents.sh upsert ANALYZE 3 {HASH} {列檔} {本範本}` 單列寫回:該分析頁已經有列就更新那一列,沒有才附加一列,整頁一起送出。禁止整頁覆蓋,也不得改動別人的列。
> 連結寫法:分析頁欄位放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址。`[[...]]` 只在同一個 wiki 內解析,從本頁指過去會是死連結。
> 連結寫法:一律寫成 `[{文字}]({連結})`。分析頁欄位的連結放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
> HASH:由 `jsc-gitea/tools/hash-id` 對 `{owner}/{repo}` 算出的完整 40 碼大寫十六進位,不截短、不加前綴。
| 計畫名稱 | 分析頁 | HASH | 工作包 | 未完成項目 | 狀態 |
+2 -1
View File
@@ -1,7 +1,8 @@
# 分析:{計畫名稱}
> {PLAN 頁絕對網址}、{REPO 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得。
> 跨頁型連結一律用絕對網址:不同頁型可能落在不同存取庫,`[[頁名]]` 只在同一個 wiki 內解析。
> 連結寫法:一律寫成 `[{文字}]({連結})`,連結一律用絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
- 頁名:`ANALYZE_{HASH}`
- HASH:`{HASH}`
+2 -1
View File
@@ -2,7 +2,8 @@
> 存放位置:本頁是目錄頁,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫(沒設定就退回 `JSC_WIKI_REPO`,都沒設定就停)。交付頁 `DELIVER_{HASH}` 住在 `JSC_WIKI_REPO_DELIVER` 那一邊,兩者不同存取庫。
> 寫入語意:一列代表一個工作包的交付。一律用 `jsc-gitea/tools/wiki-contents.sh upsert DELIVER 6 {HASH} {列檔} {本範本}` 單列寫回:該工作包已經有列就更新那一列,沒有才附加一列,整頁一起送出。禁止整頁覆蓋,也不得改動別人的列。
> 連結寫法:交付頁欄位放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址。`[[...]]` 只在同一個 wiki 內解析,從本頁指過去會是死連結。
> 連結寫法:一律寫成 `[{文字}]({連結})`。交付頁欄位的連結放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
> HASH:由 `jsc-gitea/tools/hash-id` 對 `{owner}/{repo}` 加工作包編號(例 `plugins/sdlc#WP-01`)算出的完整 40 碼大寫十六進位,不截短、不加前綴。一個工作包一頁,彼此不覆蓋。
<!-- 交付欄:是 = 交付、交接工作包(WP-01),接手者要據此動工;否 = 實作類工作包。 -->
+3 -1
View File
@@ -1,6 +1,8 @@
# 交付:{計畫名稱} {WP-nn} {工作包名稱}
> {ANALYZE 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得。跨頁型連結一律用絕對網址。
> {ANALYZE 頁絕對網址} 由 `jsc-gitea/tools/gitea.sh wiki-url` 取得。
> 連結寫法:一律寫成 `[{文字}]({連結})`,連結一律用絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
- 頁名:`DELIVER_{HASH}`
- HASH:`{HASH}`
+2 -1
View File
@@ -2,7 +2,8 @@
> 存放位置:本頁是目錄頁,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫(沒設定就退回 `JSC_WIKI_REPO`,都沒設定就停)。`MAINTAIN` 只有目錄頁、沒有內容頁,所以整個型別的紀錄就在這一頁。
> 寫入語意:一列代表一個受維護的存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert MAINTAIN 1 {owner}/{repo} {列檔} {本範本}` 單列寫回:該存取庫已經有列就更新那一列,沒有才附加一列,整頁一起送出。禁止整頁覆蓋,也不得改動別人的列。
> 連結寫法:要指向別的頁型(例如交付頁)時,放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址。`[[...]]` 只在同一個 wiki 內解析,從本頁指過去會是死連結。
> 連結寫法:一律寫成 `[{文字}]({連結})`。要指向別的頁型(例如交付頁)時,連結放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
<!-- 維護截止日 NULL = 永久維護 -->
+2 -1
View File
@@ -2,7 +2,8 @@
> 存放位置:本頁是目錄頁,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫(沒設定就退回 `JSC_WIKI_REPO`,都沒設定就停)。計畫頁 `PLAN_{HASH}` 住在 `JSC_WIKI_REPO_PLAN` 那一邊,兩者不同存取庫。
> 寫入語意:一列代表一份計畫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert PLAN 4 {HASH} {列檔} {本範本}` 單列寫回:該計畫已經有列就更新那一列,沒有才附加一列,整頁一起送出。禁止整頁覆蓋,也不得改動別人的列。
> 連結寫法:計畫頁欄位放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址。`[[...]]` 只在同一個 wiki 內解析,從本頁指過去會是死連結。
> 連結寫法:一律寫成 `[{文字}]({連結})`。計畫頁欄位的連結放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
> HASH:由 `jsc-gitea/tools/hash-id` 對 `{owner}/{repo}` 算出的完整 40 碼大寫十六進位,不截短、不加前綴。
| 計畫名稱 | 計畫頁 | 存取庫 | HASH | 狀態 | 建立時間 |
+3
View File
@@ -1,5 +1,8 @@
# 計畫:{計畫名稱}
> 連結寫法:一律寫成 `[{文字}]({連結})`,連結取自 `jsc-gitea/tools/gitea.sh wiki-url` 的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入;有一筆 DEAD 就不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼。
- 頁名:`PLAN_{HASH}`
- HASH:`{HASH}`
- 存取庫:`{owner}/{repo}`
+2 -1
View File
@@ -2,7 +2,8 @@
> 存放位置:本頁是目錄頁,落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫(沒設定就退回 `JSC_WIKI_REPO`,都沒設定就停)。盤點頁 `REPO_{HASH}` 住在 `JSC_WIKI_REPO_REPO` 那一邊,兩者不同存取庫。
> 寫入語意:一列代表一個存取庫的盤點。一律用 `jsc-gitea/tools/wiki-contents.sh upsert REPO 1 {owner}/{repo} {列檔} {本範本}` 單列寫回:該存取庫已經有列就更新那一列,沒有才附加一列,整頁一起送出。禁止整頁覆蓋,也不得改動別人的列。
> 連結寫法:盤點頁欄位放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址。`[[...]]` 只在同一個 wiki 內解析,從本頁指過去會是死連結。
> 連結寫法:一律寫成 `[{文字}]({連結})`。盤點頁欄位的連結放 `jsc-gitea/tools/gitea.sh wiki-url` 給的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,畫面上看起來像純文字或死連結,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入。有一筆 DEAD 就整列不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404,照狀態碼判會把還活著的頁判成死連結。結束碼 `1`=至少一筆連不到、`2`=一個網址都沒給、`3`=`GITEA_HOST` 未設定、`7`=金鑰失效,停下回報金鑰問題,不得當成連不到。
> HASH:由 `jsc-gitea/tools/hash-id` 對該存取庫自己的 `{owner}/{repo}` 算出的完整 40 碼大寫十六進位,不截短、不加前綴。
| 存取庫 | 盤點頁 | HASH | commit sha | 盤點時間 |
+3
View File
@@ -1,5 +1,8 @@
# 盤點:{owner}/{repo}
> 連結寫法:一律寫成 `[{文字}]({連結})`,連結取自 `jsc-gitea/tools/gitea.sh wiki-url` 的絕對網址,不自行組路徑,也不用 `[[頁名]]` 或 `[[顯示文字|頁名]]`:後者只在同一個 wiki 內解析,寫錯不會報錯,巡不到也修不了。
> 寫入前驗證:要放進本頁的每一個連結先交給 `jsc-gitea/tools/link-check.sh`,結束碼 0 才寫入;有一筆 DEAD 就不寫,把連不到的清單回報給呼叫端。驗證走 API,不看網頁狀態碼。
- 頁名:`REPO_{HASH}`
- HASH:`{HASH}`
- commit sha:`{sha}`
+84 -11
View File
@@ -25,12 +25,22 @@
# --target-branch NAME 目標分支(省略時等同來源分支)
#
# 輸出: 繁體中文 markdown 階段回報,直接貼給使用者。
# 結束碼: 0=回報完整 1=有警告(缺工作日誌,內容已暫存) 2=用法錯誤 3=相依工具找不到
# 結束碼: 0=回報完整 1=有警告(缺工作日誌或有連不到的連結) 2=用法錯誤 3=相依工具找不到
#
# 連結驗證:
# 本檔輸出的是一張給人點的 wiki 連結清單,所以印之前先把這些網址交給
# jsc-gitea/tools/link-check.sh 驗一次,逐列標出「通過、連不到、未驗證」。
# 驗證走 API 不看網頁狀態碼:私有存取庫的網頁網址對沒帶金鑰的請求一律回 404。
# 這裡的驗證只註記、不阻擋——頁面在呼叫本檔之前就寫完了,擋下去只會讓使用者連回報都拿不到。
# 呼叫端該做的事在寫入之前:每個要放進頁面的連結先過 link-check.sh,結束碼 0 才寫入,
# 有 DEAD 就不寫並回報。本檔是最後一道複查,不是那一道關卡的替代品。
#
# 陷阱:
# - 結束碼 1 是警告,不是阻擋:階段的工作已經做完了,擋下去只會讓使用者拿不到回報。
# - 缺工作日誌時一定要給 --pending-file 與 --log-hash,否則內容留在對話裡,換一個工作階段就沒了。
# - 模型閘門那一列只轉述 sdlc-gate.sh report 的結果,不自己判定標籤。
# - 金鑰失效(link-check.sh 結束碼 7)標成「未驗證」,不標成「連不到」:把金鑰問題寫成死連結,
# 下一手就會照著去刪還活著的頁。
set -u
script_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
@@ -83,6 +93,13 @@ sdlc_gate_sh() {
resolve_dep hooks hooks/sdlc-gate.sh
}
link_check_sh() {
if [ -n "${JSC_GITEA_TOOLS:-}" ] && [ -f "$JSC_GITEA_TOOLS/link-check.sh" ]; then
printf '%s\n' "$JSC_GITEA_TOOLS/link-check.sh"; return 0
fi
resolve_dep gitea tools/link-check.sh
}
stage="${1:-}"
case "$stage" in
plan|analyze|implement|maintain) shift ;;
@@ -250,6 +267,56 @@ EOF
)
fi
# ---- 連結驗證 ----
# 先把頁名全部解成網址收在 page_rows,再一次餵給 link-check.sh:一頁一次呼叫是白付的往返成本。
TAB=$(printf '\t')
page_rows=''
url_list=''
if [ -n "$pages" ]; then
page_rows=$(printf '%s' "$pages" | while IFS= read -r item; do
[ -n "$item" ] || continue
type=${item%%:*}
page=${item#*:}
url=$(page_url "$type" "$page" || true)
printf '%s%s%s\n' "$page" "$TAB" "${url:-}"
done)
url_list=$(printf '%s\n' "$page_rows" | awk -F"$TAB" 'NF>1 && $2!=""{print $2}')
fi
[ -n "$worklog_cell" ] && [ -n "${wl_url:-}" ] && url_list=$(printf '%s\n%s' "$url_list" "$wl_url")
LINKCHECK=''
link_note=''
dead_urls=''
skip_urls=''
if [ -n "$url_list" ]; then
LINKCHECK=$(link_check_sh || true)
if [ -z "$LINKCHECK" ]; then
link_note='找不到 jsc-gitea/tools/link-check.sh,這次的連結沒有驗證過。'
else
lc_out=$(printf '%s\n' "$url_list" | "$LINKCHECK" 2>/dev/null)
lc_rc=$?
case "$lc_rc" in
0|1)
dead_urls=$(printf '%s\n' "$lc_out" | awk -F"$TAB" '$1=="DEAD"{print $2}')
# SKIP 是「沒有可查的端點」,不影響 link-check.sh 的結束碼,所以分開標、不進警告。
skip_urls=$(printf '%s\n' "$lc_out" | awk -F"$TAB" '$1=="SKIP"{print $2}')
;;
3) link_note='GITEA_HOST 沒設定,連結沒有驗證過;設好變數再跑一次。' ;;
7) link_note='Gitea 金鑰失效(401、403),連結沒有驗證過。這不代表連結是死的,先處理金鑰。' ;;
*) link_note="連結驗證沒跑完(link-check.sh 結束碼 $lc_rc)。" ;;
esac
fi
fi
[ -n "$dead_urls" ] && warn=1
verify_cell() { # $1=網址 -> 驗證結果欄
[ -n "$1" ] || { printf '未驗證'; return 0; }
if [ -z "$LINKCHECK" ] || [ -n "$link_note" ]; then printf '未驗證'; return 0; fi
if printf '%s\n' "$dead_urls" | grep -Fxq -- "$1"; then printf '**連不到**'
elif printf '%s\n' "$skip_urls" | grep -Fxq -- "$1"; then printf '無可查端點'
else printf '通過'; fi
}
# ---- 輸出 ----
printf '## 階段回報:%s\n\n' "$(stage_zh "$stage")"
printf '| 項目 | 內容 |\n| --- | --- |\n'
@@ -257,23 +324,29 @@ printf '| 模型能力標籤 | %s |\n' "$gate_result"
printf '| 工作日誌 | %s |\n' "$worklog_cell"
[ -n "$branch_rows" ] && printf '%s\n' "$branch_rows"
printf '\n### 寫入的 wiki 頁\n\n'
if [ -n "$pages" ]; then
printf '| 頁面 | 連結 |\n| --- | --- |\n'
printf '%s' "$pages" | while IFS= read -r item; do
[ -n "$item" ] || continue
type=${item%%:*}
page=${item#*:}
url=$(page_url "$type" "$page" || true)
printf '| %s | %s |\n' "$page" "${url:-(取不到網址)}"
if [ -n "$page_rows" ]; then
printf '| 頁面 | 連結 | 連結驗證 |\n| --- | --- | --- |\n'
printf '%s\n' "$page_rows" | while IFS="$TAB" read -r page url; do
[ -n "$page" ] || continue
url=${url:-}
printf '| %s | %s | %s |\n' "$page" "${url:-(取不到網址)}" "$(verify_cell "$url")"
done
else
printf '本階段沒有寫入任何 wiki 頁。\n'
fi
if [ "$warn" -eq 1 ]; then
if [ "$warn" -eq 1 ] || [ -n "$link_note" ]; then
printf '\n### 警告\n\n'
[ -n "$worklog" ] || printf -- '- 這個階段還沒有寫入工作日誌,請自行檢查是不是漏了。%s\n' "$pending_note"
[ "$stage" = implement ] && [ -z "$pr_url" ] && printf -- '- 目標分支還沒有 PR,工作尚未交出去。\n'
exit 1
[ -n "$link_note" ] && printf -- '- %s\n' "$link_note"
if [ -n "$dead_urls" ]; then
printf -- '- 下列連結連不到,請修好再貼給別人:\n'
printf '%s\n' "$dead_urls" | while IFS= read -r dead; do
[ -n "$dead" ] || continue
printf -- ' - %s\n' "$dead"
done
fi
[ "$warn" -eq 1 ] && exit 1
fi
exit 0