Files
sdlc/skills/plan/SKILL.md
T
jiantw83 39a7e17b85 feat(contents): 目錄頁改為先讀回再附加
目錄頁的每一列都屬於別人的計畫、工作包或存取庫。過去照範本整頁覆寫,別人的列會直接消失,而且寫入不做合併,也沒有備份。

- 五份目錄範本都寫明寫入語意:先讀回整頁,已有的列就更新,沒有才附加。
- 規劃與維護技能加上讀取結束碼分支表。只有「頁面真的不存在」才准照範本建頁;金鑰失效或 API 失敗一律停下來回報,不得當成沒有頁面。
- wiki 讀或寫失敗就停止該階段,並指出是哪一頁、哪一個動作失敗,避免把沒存成功的頁面報成已存。
- 補上寫入閘門只有 claude 擋得住的事實,其餘四支 CLI 只能靠內文約束。
- 記下工作包閘門對規劃階段降為提醒後放棄的在製品上限。
- 維護階段先整批對齊各專案再逐一交給 sub agent,專案之間互不相依。
2026-08-31 11:10:30 +08:00

7.8 KiB

name, description
name description
plan SDLC planning stage. Gate on capability tags enforced in code by sdlc-gate (plan requires reasoning-max, verified against the transcript's actual model id), pick or create a plan from PLAN_CONTENTS, then run a decision tree until goal, scope, and feasibility reach consensus. Produce user stories into wiki page PLAN_{HASH}, then close with tools/stage-report.sh - model tag verdict, worklog link, every wiki link written. Logic only - never write code or modify files. Use when the user wants to start or refine a plan; not for analysis or implementation.

plan

Goal: create or extend the wiki plan page PLAN_{HASH}. This skill is a logic-only stage: never output code, and never modify any file.

{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). 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.

Steps

  1. Model gate and stage lock — run jsc-cli/tools/model-tags.sh sync, then jsc-hooks/hooks/sdlc-gate.sh lock plan. This stage requires the reasoning-max capability tag. 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.

  2. Read PLAN_CONTENTS via jsc-gitea:wiki and list the plans whose status is the literal 「未分析」 (not analyzed), with names and HASH. Completion condition: you have listed every 未分析 plan with its name and HASH, or reported that none exists.

  3. Let the user choose per jsc-ask:ask rules: extend an existing plan (list the not-analyzed plans as options) or create a new plan. State the impact scope on every option. Completion condition: the user has picked one option explicitly, and you have named the PLAN_{HASH} page this run writes to.

  4. Keep questioning until consensus — rules in references/consensus.md, which is the single authority for both planning and analysis. Cover all three items:

    • Goal: the problem to solve and the criteria for success.
    • Scope: what is included, what is excluded, which repositories are involved.
    • Feasibility: whether the system architecture and data sources support the goal.

    One round is never enough: every answer must produce the next question, derived from what that answer just exposed. Never fill a gap with your own assumption, and never move on to user stories while any item is still open.

    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, 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.

  7. Upsert this plan's row in PLAN_CONTENTS using the entry format of templates/plan-contents.md, with status set to the literal 「未分析」 — add the row if missing, otherwise refresh it. Read the page back first and write the whole page, per "Contents pages are appended, never overwritten" below; never overwrite it wholesale, and never touch a row belonging to another plan. Completion condition: PLAN_CONTENTS shows this plan's row with the literal 「未分析」, every other row is byte-for-byte unchanged, and the wiki-get exit code the write branched on is named.

  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 PLAN:{page} per wiki page this run wrote (PLAN_{HASH} and PLAN_CONTENTS both count), plus --worklog and --worklog-heading when a work log entry exists. No work log yet: write this stage's log content to a file 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

PLAN_CONTENTS is a shared directory: 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 — add the row if missing, otherwise refresh it, then wiki-put the whole page. Whole-page overwrite is forbidden.

That rests entirely on reading the old page back, so branch the wiki-get on its exit code:

Exit What this step does
0 the page is there — upsert this plan's row into the content that came back, then write the whole page
4 the page really does not exist yet — this is the only code that permits building it from the template
7 the key is invalid or lacks permission — stop, report the code and its cause, create no page and write nothing
8 any other API failure — same as 7: stop and report, 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" makes step 7 write a fresh template over a live directory, and every other plan's row is gone — the write carries no merge and no backup. PLAN_{HASH} is the opposite case: it is a content page belonging to this one plan, so step 6 rewriting it whole is correct. The distinction is the page, not the write.

Completion condition: the PLAN_CONTENTS write names the wiki-get exit code it branched on, and no page was created on any code other than 4.

Hard limits

  • Never output a code snippet.
  • Never modify any file in the working directory. This limit is enforced in code where the CLI allows it: jsc-hooks/hooks/write-guard.sh in stage mode runs as a PreToolUse hook and blocks Write, Edit and MultiEdit while this stage's lock exists — the same lock state jsc-hooks/hooks/sdlc-gate.sh lock plan writes in step 1. Only claude has PreToolUse. Codex, copilot, antigravity and kiro never reach that hook, so on those four CLIs this line is the only thing holding the limit.
  • Never skip the decision tree and assume requirements.
  • Never stop questioning after one round; consensus is reached only under references/consensus.md, and the user says so.

What the work package gate no longer stops here

jsc-hooks/hooks/sdlc-gate.sh wp-check used to block this skill outright while the repository still held an unsettled work package PR. For plan it is now a reminder that prints and lets the run through. The protection given up is the work-in-progress cap: nothing stops a new plan from starting while packages from the last one are still open, so plans can pile up faster than they are implemented. The reminder still names the unsettled package, and the same gate still blocks analyze and maintain, so the cap holds one stage later. implement was always waved through — that is the path that settles the open PR, and a gate that blocked it would lock itself.