diff --git a/skills/analyze/SKILL.md b/skills/analyze/SKILL.md index 2f69553..66c602b 100644 --- a/skills/analyze/SKILL.md +++ b/skills/analyze/SKILL.md @@ -1,6 +1,6 @@ --- name: analyze -description: SDLC analysis stage. Gate on capability tags enforced in code by sdlc-gate (analyze requires reasoning-max), confirm the source branch, then pick a plan from PLAN_CONTENTS and question until consensus per references/consensus.md. Analyze its user stories against the current state (working directory plus REPO_{HASH} inventory for reuse), then run WBS with a standalone delivery/handover WP-01, CPM estimates and TDD todos. Write wiki page ANALYZE_{HASH} with real sample data, 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 after planning and before implementation; not for writing code (that is implement), and not before a plan exists in PLAN_CONTENTS. +description: SDLC analysis stage. Gate on capability tags enforced in code by sdlc-gate (analyze requires reasoning-max), pick a plan from PLAN_CONTENTS first, then confirm that plan's source branch and question until consensus per references/consensus.md. Analyze its user stories against the current state (working directory plus REPO_{HASH} inventory for reuse), then run WBS with a standalone delivery/handover WP-01, CPM estimates and TDD todos. Write wiki page ANALYZE_{HASH} with real sample data, 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 after planning and before implementation; not for writing code (that is implement), and not before a plan exists in PLAN_CONTENTS. --- # analyze @@ -9,33 +9,54 @@ Goal: create or extend the wiki analysis page `ANALYZE_{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 `ANALYZE_{HASH}` page name, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). -All wiki reads and writes go through `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 11 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 analyze`. 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. **Confirm the source branch** — the branch whose code counts as the current state: +2. **Find out whether there is anything to analyze, before anything else costs the user a round.** Read `PLAN_CONTENTS` for plans whose status is the literal 「未分析」 (name and HASH), and read `ANALYZE_CONTENTS` for existing analyses. **The two pages are independent — read them concurrently, and do not wait for the source branch: which branch the analysis reads from depends on the plan, so it is confirmed in step 4, after the target is known.** Completion condition: you have listed every 未分析 plan with its name and HASH plus every existing analysis, or reported that both lists are empty and stopped. +3. Let the user choose per `jsc-ask:ask` rules: **extend an existing analysis** or **analyze a new plan**. State the impact scope on every option. Completion condition: the user has picked one option explicitly, and you have named the target — the existing `ANALYZE_{HASH}` page, or the plan the new analysis covers. +4. **Confirm the source branch for that target** — the branch whose code counts as the current state, confirmed before any code is read and before the analysis page is written: 1. Run `git fetch --prune origin` first — without it, every `origin/...` reference is stale cache. Then report the working directory's current branch, the **remote** branches available (`git branch -r`; never `git branch`) and whether the working tree is clean. 2. Ask per `jsc-ask:ask` rules which branch the analysis reads from; state the impact scope on every option (analysing the wrong branch produces work packages for code that does not exist). 3. **The current state is always the remote branch `origin/{source-branch}`, never the local one.** The working directory's HEAD must point at the same commit as `origin/{source-branch}`; behind, ahead or diverged all mean you would be analysing code that is not what the remote holds. Report the gap (`git rev-list --left-right --count origin/{source-branch}...HEAD`) and stop — this stage never switches branches, never stashes, never pulls and never touches the working tree. Rules in `references/branch.md`. - 4. Record the confirmed branch and the head sha **of `origin/{source-branch}`** on the analysis page. Completion condition: the user has confirmed the branch explicitly; never infer it from the current checkout alone. -3. Read `PLAN_CONTENTS` via `jsc-gitea:wiki` for plans whose status is the literal 「未分析」 (name and HASH), and read `ANALYZE_CONTENTS` for existing analyses. Completion condition: you have listed every 未分析 plan with its name and HASH plus every existing analysis, or reported that a list is empty. -4. Let the user choose per `jsc-ask:ask` rules: **extend an existing analysis** or **analyze a new plan**. State the impact scope on every option. Completion condition: the user has picked one option explicitly, and you have named the target — the existing `ANALYZE_{HASH}` page, or the plan the new analysis covers. + 4. Record the confirmed branch and the head sha **of `origin/{source-branch}`** on the analysis page. Completion condition: the user has confirmed the branch explicitly, and the `origin` consistency check above has passed; never infer the branch from the current checkout alone. 5. Analyze the plan page's user stories one by one against the **current state**, **questioning until consensus** per `references/consensus.md` (the single authority for both planning and analysis): every answer produces the next question, and consensus needs both no output-changing unknown **and** the user's explicit confirmation. Never assume a missing detail, and never start the WBS while any item is still open. Current state means: - 1. Every file in the working directory, at the commit `origin/{source-branch}` points to (verified in step 2). + 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. 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`, and update `REPO_CONTENTS` per `templates/repo-contents.md`. + - 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`, and upsert this repository's row in `REPO_CONTENTS` per `templates/repo-contents.md` — add the row if missing, otherwise refresh its commit sha and 盤點時間. `REPO_CONTENTS` is a contents page: read it back, change only this repository's row, and write the whole page. Never overwrite it wholesale, and never touch a row belonging to another repository. + - The `REPO_CONTENTS` read branches by exit code, and only exit 4 opens the create path — 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. Completion condition: every user story has reached consensus under both conditions of `references/consensus.md`, and every reuse decision — reused, or rejected with its reason — is recorded in 複用決策. 6. Run a **Work Breakdown Structure (WBS)**: split the user stories into work packages, number them sequentially (`WP-01`, `WP-02`, ...) and mark dependencies. Completion condition: every user story on the plan page maps to at least one numbered work package, and every dependency edge is recorded in the WBS table's 相依 column. 7. **`WP-01` is always the delivery/handover work package** — see "Delivery package is WP-01" below. It stands alone, never merged into an implementation package, and every implementation package that consumes its spec depends on it. Completion condition: `WP-01` is marked 交付 `是` and holds only spec-shaped items, and every implementation package that consumes its spec names `WP-01` in its 相依 column — or the no-handover case below is confirmed with the user and its reason is written on the page. 8. Estimate every work package's effort in hours and days with the **Critical Path Method (CPM)**, and mark the critical path. Draw the critical path as a mermaid gantt chart per `references/cpm-chart.md` — its date/duration rules are mandatory, not a suggestion; skipping them is how the `Invalid date` rendering failure happens. Completion condition: every work package carries an hours figure and a days figure, the critical path plus its total days are written on the page, and the gantt chart follows `references/cpm-chart.md` exactly (integer-hour durations, `after` chaining, no `dateFormat X`). -9. 在 TDD 待辦前先產生 **使用者故事驗收計畫**,再把每個工作包拆進 **測試計畫(TDD)** 區塊,格式用 `[ ]`(未完成)與 `[x]`(完成)。每個使用者故事都要有驗收情境,並標明驗收方式是 `真實資料` 或 `邏輯推論`。簡單故事至少 1 個情境;中等故事至少 2 個情境,含 1 個主要流程與 1 個邊界或錯誤流程;複雜故事至少 3 個情境,含主要流程、邊界流程與失敗流程。每個情境都要寫出輸入、預期結果、資料來源與對應工作包。每個 TDD 待辦都是一個完整循環:一個接縫、一個先失敗的測試、一個最小實作、一次綠燈驗證,以及必要時的綠燈後重構。不要把紅燈測試、最小實作、綠燈驗證拆成不同待辦。接縫與反模式見 `references/tdd.md`。完成條件:每個使用者故事都在 `## 使用者故事驗收計畫` 有情境;每個情境都有明確驗收方式與資料來源;情境數量符合複雜度;每個工作包都在 `## 測試計畫(TDD)` 下有自己的小節;每個實作工作包至少有一個測試先行的 `[ ]` 待辦;每個待辦都寫出接縫、驗收情境、測試斷言行為、最小實作範圍與綠燈驗證方式。純交付工作包可以寫文件驗證或範例資料驗證待辦,但仍要寫出能證明規格可用的測試證據或審查證據。 -10. Apply `templates/analyze-page.md` to create or update the analysis 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, including the source branch, the head sha and the 未決項 section (「無」 when there is none). -11. If the analysis page is new: add it to `ANALYZE_CONTENTS` per `templates/analyze-contents.md`, and flip the plan's status in `PLAN_CONTENTS` to the literal 「已分析」. Completion condition: `ANALYZE_CONTENTS` shows the new row and `PLAN_CONTENTS` shows the literal 「已分析」, both saved on the wiki. -12. **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). Run `tools/stage-report.sh analyze` with one `--page TYPE:{page}` per wiki page this run wrote — `ANALYZE_{HASH}`, `ANALYZE_CONTENTS`, `PLAN_CONTENTS`, and `REPO_{HASH}` plus `REPO_CONTENTS` when a re-inventory happened — 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. +9. **Write the user story acceptance plan first, then the TDD todos.** The acceptance plan goes under the section `## 使用者故事驗收計畫`; every work package is then broken down under `## 測試計畫(TDD)`, with `[ ]` for an open item and `[x]` for a finished one. + 1. Every user story gets acceptance scenarios, and every scenario states its acceptance method as either `真實資料` or `邏輯推論`. Scenario count follows the story's complexity: a simple story takes at least 1; a medium story at least 2, covering one main flow plus one boundary or error flow; a complex story at least 3, covering the main flow, the boundary flow and the failure flow. + 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 and write it back via `jsc-gitea:wiki`; the page content is Traditional Chinese, exactly as the template dictates. `ANALYZE_CONTENTS` and `PLAN_CONTENTS` are then upserted per "Contents pages are appended, never overwritten" below: read each page back, add this analysis's row to `ANALYZE_CONTENTS` if it is missing and otherwise refresh it, flip only this plan's status in `PLAN_CONTENTS` to the literal 「已分析」, and write each whole page back. 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 — and, for a new page, `ANALYZE_CONTENTS` shows its new row and `PLAN_CONTENTS` shows the literal 「已分析」, both saved on the wiki. +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 — `ANALYZE_{HASH}`, `ANALYZE_CONTENTS`, `PLAN_CONTENTS`, and `REPO_{HASH}` plus `REPO_CONTENTS` when a re-inventory happened — 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 + +`ANALYZE_CONTENTS`, `PLAN_CONTENTS` and `REPO_CONTENTS` are shared directories: every row on them belongs to somebody's plan, analysis or repository, and this run reads none of those rows from anywhere else. So every write to them 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, and a row this run does not own stays untouched. + +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 run'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 this step write a fresh template over a live directory, and every other row is gone — the write carries no merge and no backup. Content pages (`ANALYZE_{HASH}`, `PLAN_{HASH}`, `REPO_{HASH}`) are the opposite case: each belongs to one subject, so rewriting one whole is correct. The distinction is the page, not the write. + +Completion condition: every contents-page write this stage made names the `wiki-get` exit code it branched on, and no page was created on any code other than 4. ## Delivery package is WP-01 @@ -54,5 +75,5 @@ Every sample value on the analysis page — request and response payloads, field ## Hard limits - Never output a code snippet (file paths and method names are allowed). -- Never modify any file in the working directory. +- **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 analyze` 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 switch, create or clean branches in this stage; ask the user to do it. diff --git a/skills/implement/SKILL.md b/skills/implement/SKILL.md index c44926a..9a3e91d 100644 --- a/skills/implement/SKILL.md +++ b/skills/implement/SKILL.md @@ -1,89 +1,113 @@ --- name: implement -description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), then confirm the analysis page's source branch - it is both the worktree base and the PR target. Every already-open work package's PR comments get triaged via tools/wp-gate.sh check and fixed by sub agents only after jsc-ask consensus, never blocking an unrelated package; picking a candidate is gated per-candidate in code by tools/wp-gate.sh check-deps, and claiming it records the package number via tools/wp-gate.sh claim so tools/wp-gate.sh owns keeps every session on its own package's PR. Claim a ready work package from ANALYZE_CONTENTS with a work ticket, confirm a delivery package's content type, then complete its TDD todos one at a time inside a worktree built from origin/{source-branch}, updating the wiki after every item, closing with the two side-by-side audits jsc-review code-review and jsc-review api-doc (the latter gated by swagger-detect.sh and explicitly skipped where the project has no Swagger support), one PR back to the source branch, a jsc-gitea pr-watch.sh poll that holds until that PR merges, a jsc-log:worklog entry per finished task, the chosen delivery document, an optional MAINTAIN_CONTENTS entry, and a tools/stage-report.sh report covering the model tag verdict, the worklog link, every wiki link written, the worktree and the three branches. Use when analysis is done and code must be written; not for planning or analysis. +description: SDLC implementation stage. Gate on capability tags enforced in code by sdlc-gate (implement requires coding), settle every open work package's PR comments first, then claim a ready package before confirming the analysis page's source branch - it is both the worktree base and the PR target. Every already-open work package's PR comments get triaged via tools/wp-gate.sh check and fixed by sub agents only after jsc-ask consensus, never blocking an unrelated package; every candidate is gated in code by tools/wp-gate.sh check-deps before it reaches the options, and claiming records the package number via tools/wp-gate.sh claim so tools/wp-gate.sh owns keeps every session on its own package's PR. Claim a ready work package from ANALYZE_CONTENTS with a work ticket, confirm a delivery package's content type, then complete its TDD todos one at a time inside a worktree built from origin/{source-branch}, updating the wiki after every item, closing with the two side-by-side audits jsc-review code-review and jsc-review api-doc (the latter gated by swagger-detect.sh and explicitly skipped where the project has no Swagger support), one PR back to the source branch, a jsc-gitea pr-watch.sh poll that holds until that PR merges, a jsc-log:worklog entry per finished task, the chosen delivery document, an optional MAINTAIN_CONTENTS entry, and a tools/stage-report.sh report covering the model tag verdict, the worklog link, every wiki link written, the worktree and the three branches. Use when analysis is done and code must be written; not for planning or analysis. --- # implement Goal: complete the analysis page's todos one by one; **update the wiki status immediately after every completed item**. -All wiki reads and writes go through `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 13 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 implement`. This stage requires the `coding` 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. **Confirm the source branch — it is also this stage's PR target**: - 1. Run `git fetch --prune origin`, then read the source branch from the analysis page and report it, along with the current branch and whether the working tree is clean. - 2. Ask per `jsc-ask:ask` rules to confirm that `origin/{source-branch}` is both the worktree's base and the PR target for every work package in this analysis. State the impact scope: each finished work package merges back into the source branch, and that branch as a whole reaches `develop` later as its own separate PR. - 3. **A source branch missing from the remote is a stop-and-report condition, never a silent fallback.** That rule (section 「來源分支在遠端找不到」), the remote-only basis and the uncommitted-changes rules: `references/branch.md`. - 4. Completion condition: the user has confirmed the source branch explicitly, and it is recorded on the analysis page next to the work ticket. -3. **Generate a work ticket**: format `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}`. `{HASH}` = the shared wiki hash for `{owner}/{repo}`, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). Rename the current session to the ticket name; skip the rename only when the CLI exposes no rename command. Completion condition: the ticket string exists, and you have reported it together with which branch applied — renamed, or skipped because this CLI has no rename command. -4. **Settle every already-open work-package PR's comments — this keeps existing PRs moving, but does not by itself decide which new package may start (that's step 6)**: - 1. Read the analysis page's PR column. For every work package holding a PR that is not marked merged, run `jsc-sdlc/tools/wp-gate.sh check {owner}/{repo} {index} --since {the comment timestamp recorded in that PR column}`. Drop `--since` when that package has no recorded timestamp yet. - 2. Exit 0 (`status=merged`) clears that package: remove its worktree (`references/branch.md`) and mark the package done on the analysis page. - 3. Exit 1 (`status=open` or `status=closed-unmerged`) means that package's own PR is not settled yet. **This does not block picking a different, unrelated work package** — SDLC implementation can run several independent packages in parallel; an unmerged PR only holds back packages that depend on it (step 6 checks that specifically), never the whole analysis page. Sub-steps 4.4 to 4.9 are the one comment round this skill owns; step 11.5 runs the same sub-steps for the PR it just opened. - 4. **The PR has to be yours before you touch it.** Run `jsc-sdlc/tools/wp-gate.sh owns {owner}/{repo} {index} --wp {the work package number that PR column belongs to}`. `status=owned` → carry on. `status=foreign` (exit 1) → leave that PR alone, name the work package the script says holds it, and let that package's own session handle it. `status=unowned` (exit 0) → carry on, and report that ownership could not be determined. Completion condition: the script has run for that PR and its verdict is reported. - 5. **Agree on the fix before making it.** Run the `jsc-ask:ask` decision tree over the comments the script printed — issue comments, review verdicts and inline comments alike — one option set per comment, each option stating its impact scope (which files it touches, whether it changes the package's TDD todos). **Never fix a comment automatically**: a reviewer's wording often allows two different fixes, and picking one silently costs another review round. Completion condition: the user has said how each comment is handled before any file is edited. - 6. **Each round of fixes MUST run as a sub agent** inside that package's own worktree, pushing to the same work branch, so the PR updates itself. Hand the sub agent the agreed decisions and let the comment text stay inside it — the main agent keeps only the decisions and the bookkeeping. Never open a second PR for the same package. - 7. Give every printed comment an outcome — fixed, no fix needed, or cannot fix. Reply to every handled comment with `jsc-gitea/tools/gitea.sh comment-reply {owner}/{repo} {index} {issue|review|inline} {comment id} {reply file}`. The reply states the outcome and the commit, file, or reason. Pure discussion and praise can be ignored only when you list the reason. Rules and the completion condition: `jsc-meta/references/pr-report.md`. - 8. Write the script's `latest=` value into that work package's PR column, appended after the existing PR link as `#{index} 已處理留言 {ISO time}`, and save the page back to the wiki. Next run passes it as `--since`, so handled comments stay handled. Reuse the existing PR column and add no new column; the analysis page's columns belong to `analyze`. - 9. **One round of comment fixes is one finished task — call `jsc-log:worklog` now**, under the Rules section's one-task-one-entry rule. Completion condition: the entry for this round is saved on `LOG_{HASH}` before the next round starts. - 10. Exit 3 means the gate could not decide (a missing dependency, or the PR could not be found). Report it and stop — an undecidable gate never counts as merged. - 11. Completion condition: every work package holding an unmerged PR has had this run's comments triaged (fixed, no fix needed, or cannot fix), logged and reported; a package left unmerged after this does not block step 5/6 for packages that do not depend on it. -5. Read `ANALYZE_CONTENTS` via `jsc-gitea:wiki` and list what is unfinished: plan name, HASH, work package number, count of open items. A selectable work package must satisfy all three: **unfinished, not holding a work ticket, and — verified by step 6's `wp-gate.sh check-deps`, never by eyeballing the 相依 column yourself — dependency-free or all dependencies merged**. Completion condition: you have listed every selectable work package, or reported that none is selectable and stopped. -6. **Before picking, the candidate's own dependencies decide — the gate lives in code, not in this text, and it is scoped to that one candidate, never to every open PR on the page**: - 1. For the candidate work package the user is about to choose, run `jsc-sdlc/tools/wp-gate.sh check-deps {owner}/{repo} {wp-number} --analyze ANALYZE_{HASH}`. It re-reads the caller-specified analysis page and queries Gitea itself — it does not trust anything you already read or concluded, and it never guesses the page from the repository hash. - 2. Exit 0 (`status=ready`) → eligible; proceed to claim it. Exit 1 (`status=blocked`) → not eligible; report which dependency work package's PR is not merged yet, and offer the user the other eligible candidates instead of this one. Exit 3 (`status=missing-dep`) is an undecidable gate — report it and stop; never treat an undecidable result as either ready or blocked. +2. **Read the analysis pages, then settle every already-open work-package PR's comments — this keeps existing PRs moving and can clear a dependency for step 4, but it does not by itself decide which new package may start (step 4 does)**: + 1. Read `ANALYZE_CONTENTS`, then read every analysis page it lists as unfinished. **Keep this read: steps 3, 5 and 8 reuse it and never read the same pages again.** Completion condition: for every unfinished analysis page you hold its WBS table, its PR column, its source branch and the repositories it names. + 2. **Prefetch every open PR's state in one batch.** For every work package holding a PR that is not marked merged, run `jsc-sdlc/tools/wp-gate.sh check {owner}/{repo} {index} --since {the comment timestamp recorded in that PR column}`. Drop `--since` when that package has no recorded timestamp yet. **These calls do not depend on each other — run them concurrently and collect every result before you ask the user anything.** The consensus rounds and the fixes that follow stay one comment at a time. Completion condition: every open PR has a recorded exit code and `status=` line. + 3. Exit 0 (`status=merged`) clears that package: remove its worktree (`references/branch.md`) and mark the package done on the analysis page. + 4. Exit 1 (`status=open` or `status=closed-unmerged`) means that package's own PR is not settled yet. **This does not block picking a different, unrelated work package** — SDLC implementation can run several independent packages in parallel; an unmerged PR only holds back packages that depend on it (step 4 checks that specifically), never the whole analysis page. Sub-steps 2.5 to 2.10 are the one comment round this skill owns; step 11.5 runs the same sub-steps for the PR it just opened. + 5. **The PR has to be yours before you touch it.** Run `jsc-sdlc/tools/wp-gate.sh owns {owner}/{repo} {index} --wp {the work package number that PR column belongs to}`. `status=owned` (exit 0) → carry on. `status=foreign` (exit 1) → leave that PR alone, name the work package the script says holds it, and let that package's own session handle it. `status=unowned` (exit 0) → carry on, and report that ownership could not be determined. `status=usage` (exit 2) → bad arguments; fix them and run it again, and change nothing on that PR until the script returns a verdict. Completion condition: the script has run for that PR and its verdict is reported. + 6. **Agree on the fix before making it.** Run the `jsc-ask:ask` decision tree over the comments the script printed — issue comments, review verdicts and inline comments alike — one option set per comment, each option stating its impact scope (which files it touches, whether it changes the package's TDD todos). **Never fix a comment automatically**: a reviewer's wording often allows two different fixes, and picking one silently costs another review round. Completion condition: the user has said how each comment is handled before any file is edited. + 7. **Each round of fixes MUST run as a sub agent** inside that package's own worktree, pushing to the same work branch, so the PR updates itself. Hand the sub agent the agreed decisions and let the comment text stay inside it — the main agent keeps only the decisions and the bookkeeping. Never open a second PR for the same package. + 8. Give every printed comment an outcome — fixed, no fix needed, or cannot fix. Reply to every handled comment with `jsc-gitea/tools/gitea.sh comment-reply {owner}/{repo} {index} {issue|review|inline} {comment id} {reply file}`. The reply states the outcome and the commit, file, or reason. Pure discussion and praise can be ignored only when you list the reason. Rules and the completion condition: `jsc-meta/references/pr-report.md`. + 9. Write the script's `latest=` value into that work package's PR column, appended after the existing PR link as `#{index} 已處理留言 {ISO time}`, and save the page back to the wiki. Next run passes it as `--since`, so handled comments stay handled. Reuse the existing PR column and add no new column; the analysis page's columns belong to `analyze`. + 10. **One round of comment fixes is one finished task — call `jsc-log:worklog` now**, under the Rules section's one-task-one-entry rule. **Resolve that package's PLAN and ANALYZE wiki repositories and page URLs once and hand the resolved values to every call**: the same package's later rounds reuse them, and only a change of repository forces a fresh resolution. Completion condition: the entry for this round is saved on `LOG_{HASH}` before the next round starts. + 11. Exit 2 (`status=usage`) means bad arguments — a malformed `{owner}/{repo}`, a missing index, or a `--since` value that is not the previous round's `latest=`. Fix the arguments and run it again. Completion condition: the rerun returned 0, 1 or 3; a usage error never counts as merged, settled or blocked. + 12. Exit 3 (`status=missing-dep`) means the gate could not decide (a missing dependency script, or the PR could not be found). Report it and stop — an undecidable gate never counts as merged. + 13. Completion condition: every work package holding an unmerged PR has had this run's comments triaged (fixed, no fix needed, or cannot fix), logged and reported; a package left unmerged after this does not block steps 3 and 4 for packages that do not depend on it. +3. From the pages read in step 2.1, list what is unfinished: plan name, HASH, work package number, count of open items. A selectable work package satisfies both: **unfinished, and not holding a work ticket**. Dependencies are not judged here and never by eyeballing the 相依 column — step 4's `wp-gate.sh check-deps` is the only judge. Completion condition: you have listed every selectable work package, or reported that none is selectable and stopped. +4. **Every candidate passes the dependency gate before the user sees it — the gate lives in code, not in this text, and it judges one candidate at a time, never every open PR on the page**: + 1. Run `jsc-sdlc/tools/wp-gate.sh check-deps {owner}/{repo} {wp-number} --analyze ANALYZE_{HASH}` **once per candidate from step 3, before asking the user anything**. Candidates do not depend on each other's verdict, so run these concurrently. The script re-reads the caller-specified analysis page and queries Gitea itself — it does not trust anything you already read or concluded, and it never guesses the page from the repository hash. + 2. Branch on each candidate's exit code. Exit 0 (`status=ready`) → put it in the option list. Exit 1 (`status=blocked`) → keep it out of the option list, and report which dependency work package's PR is not merged yet. Exit 3 (`status=missing-dep`) is an undecidable gate — report it and stop; never treat an undecidable result as either ready or blocked. Exit 2 (`status=usage`) → bad arguments or a missing `--analyze`; fix them and run that candidate again, and leave it out of the options until the rerun returns a verdict. Completion condition: every candidate from step 3 carries one of these verdicts, and only the `ready` ones remain. 3. A dependency phrased as text pointing outside this analysis page (another plan, another analysis) cannot be checked by the script; it comes back listed separately as needing manual confirmation. Confirm it with the user before proceeding — an unchecked cross-page dependency is not the same as a cleared one. - 4. Let the user pick per `jsc-ask:ask` rules from the eligible candidates only (options state open-item count and estimated effort). **List delivery packages (交付 `是`) first** — the analysis page makes `WP-01` the standalone delivery package, so keep that order in the options. Write the ticket into that work package's ticket column (the zh-TW field 「工作證」) on the analysis page and save it back to the wiki. - 5. **Record the claim in code, so later steps can tell this package's PR from any other package's**: run `jsc-sdlc/tools/wp-gate.sh claim {owner}/{repo} {wp-number} --analyze ANALYZE_{HASH}`, with the number read off the analysis page — the work package number is the only thing ownership is judged by (`references/branch.md`). One repository holds one claim at a time; `wp-gate.sh check` hands it back once the PR merges. Completion condition: `check-deps` returned `status=ready` for the picked candidate (any cross-page dependency confirmed with the user), the ticket is saved on the wiki, and `claim` returned `status=claimed`; only then may you proceed. + 4. Let the user pick per `jsc-ask:ask` rules from the `ready` candidates only (options state open-item count and estimated effort). **List delivery packages (交付 `是`) first** — the analysis page makes `WP-01` the standalone delivery package, so keep that order in the options. Completion condition: the user has named one `ready` work package, and any cross-page dependency of it is confirmed. + 5. **Record the claim in code, so later steps can tell this package's PR from any other package's**: run `jsc-sdlc/tools/wp-gate.sh claim {owner}/{repo} {wp-number} --analyze ANALYZE_{HASH}`, with the number read off the analysis page — the work package number is the only thing ownership is judged by (`references/branch.md`). One repository holds one claim at a time; `wp-gate.sh check` hands it back once the PR merges. Branch on the exit code: exit 0 (`status=claimed`) → proceed. Exit 2 (`status=usage`) → fix the arguments and run it again. Exit 3 (`status=missing-dep`) → the claim could not be recorded; report it and stop. **No recorded claim, no work**: without it the repository has no owner on record, `owns` answers `unowned` for every PR from then on, and every later session is waved through onto this package's PR. Completion condition: `claim` returned `status=claimed`; only then may you proceed. +5. **Confirm the source branch — it is also this stage's PR target**: + 1. Run `git fetch --prune origin`, then take the source branch from the analysis page already read in step 2.1 and report it, along with the current branch and whether the working tree is clean. + 2. **Show the recorded value and take a single-key confirmation.** The analysis page already carries the answer, so this is a confirmation, not a fresh question: print `origin/{source-branch}` as both the worktree base and the PR target for this work package, and accept one key to confirm it. Two cases have no shortcut and go through the full `jsc-ask:ask` decision tree, each option stating its impact scope: **the analysis page records no source branch**, and **`origin/{source-branch}` does not exist on the remote**. + 3. **A source branch missing from the remote is a stop-and-report condition, never a silent fallback.** That rule (section 「來源分支在遠端找不到」), the remote-only basis and the uncommitted-changes rules: `references/branch.md`. + 4. Completion condition: the user has confirmed the source branch — by the single key, or through the decision tree in either exception case — and it is recorded in the analysis page's 「來源分支」 column. +6. **Generate a work ticket and claim the package on the page**: format `TICKET_{yyyyMMdd}_{HHmmss}_{HASH}`. `{HASH}` = the shared wiki hash for `{owner}/{repo}`, computed by `jsc-gitea/tools/hash-id` (see `jsc-gitea:wiki`). Rename the current session to the ticket name; skip the rename only when the CLI exposes no rename command. Write the ticket into the picked work package's ticket column (the zh-TW field 「工作證」) on the analysis page and save it back to the wiki. Completion condition: the ticket string exists, it is saved in that package's 「工作證」 column on the wiki, and you have reported it together with which branch applied — renamed, or skipped because this CLI has no rename command. 7. **A delivery/handover package confirms its content before its first todo**: 1. Ask per `jsc-ask:ask` rules what this delivery must contain. The options are fixed: **1. API 文件** and **2. 由使用者輸入**. State the impact scope on each. **Never assume the type, and never skip this — the answer decides what the whole package produces.** 2. Required fields, sample-data order and the new-versus-existing parameter marking: `references/deliver-formats.md`. 3. Completion condition: the confirmed type is written into the analysis page's 交付型別 column and saved back to the wiki before the first todo starts. -8. **Create the worktree — before touching any code.** Run `git fetch --prune origin`, read the source branch and the repositories involved from the analysis page, then ask per `jsc-ask:ask` rules how to handle the branch. Path layout, the two `git worktree add` options, quoting, `.git/info/exclude` and the reporting duty: `references/branch.md`. Completion condition: every repository the analysis page names has a worktree built from `origin/{source-branch}`, and you have reported each worktree's path, checked-out branch, source branch and starting commit sha. +8. **Create the worktree — before touching any code.** Run `git fetch --prune origin` (this fetch is deliberate: the worktree must start from the newest remote state), then reuse the source branch confirmed in step 5 and the repository list read in step 2.1 — never read them off the page a second time. Ask per `jsc-ask:ask` rules how to handle the branch. **A multi-repository analysis builds its worktrees concurrently**; the repositories do not depend on each other. Path layout, the two `git worktree add` options, quoting, `.git/info/exclude` and the reporting duty: `references/branch.md`. Completion condition: every repository the analysis page names has a worktree built from `origin/{source-branch}`, and you have reported each worktree's path, checked-out branch, source branch and starting commit sha. 9. **Complete the work package's open items one at a time. Every item MUST run as a sub agent**, working inside the worktree: 1. One sub agent takes one item and follows the TDD loop: red before green, one vertical slice. Rules and anti-patterns: `references/tdd.md` (refactoring belongs to the review stage). 2. The main agent keeps only the wiki bookkeeping: when the sub agent reports the item done, flip its `[ ]` to `[x]` on the analysis page and save to the wiki. - 3. **A code comment states why the code is written this way; it never states where the work is documented.** The tracking numbers this stage always holds — the work package number, the analysis page number, the TDD todo number, the source branch name and the PR number — stay out of every code comment; write the reason itself into the comment instead. Full list and the allowed exceptions: `jsc-review/references/comment-scope.md`. `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 with the same item. Completion condition: the item's own diff holds no comment line carrying any of those numbers, and every warning the hook printed for this item is fixed. + 3. **A code comment states why the code is written this way; it never states where the work is documented.** The tracking numbers this stage always holds — the work package number, the analysis page number, the TDD todo number, the source branch name and the PR number — stay out of every code comment; write the reason itself into the comment instead. Full list and the allowed exceptions: `jsc-review/references/comment-scope.md`. `jsc-hooks/hooks/comment-scope.sh` is wired on all five CLIs, but **it fires at a different moment on each, so never treat it as one uniform warning**: only claude scans the file the instant it is written, through `PostToolUse`; codex sweeps the whole worktree at the end of every turn, kiro at the next prompt submit, and copilot and antigravity only once when the session ends. Fix whatever it flags at once, then carry on with the same item. On the four CLIs that are not claude, none of that lands while the item is still being written, so **the one pass that still arrives in time is the `comment-scope.sh sweep` that `jsc-git:commit` runs before it commits at step 11.1** — that sweep is what keeps a flagged comment out of the commit, and a line it flags is fixed there, never waived. Completion condition: the item's own diff holds no comment line carrying any of those numbers, every warning the hook printed for this item is fixed, and on a CLI other than claude you have reported that the step 11.1 sweep is the pass being relied on. 4. Completion condition: every item of the package shows `[x]` on the saved analysis page, and each save happened before the next item's sub agent started. -10. **Two closing audits, side by side — a work package is finished only when both of them clear. Neither replaces the other**: - 1. **Code review** — when all items are done, sweep the work package's whole diff for the comment rule of step 9.3, then call `jsc-review:code-review` and wait for the verdict. Each round of fixes **MUST run as a sub agent** inside the same worktree. Completion condition: the review passes, and the diff handed to it holds no comment line carrying the work package number, the analysis page number, a TDD todo number, the source branch name or a PR number (full list: `jsc-review/references/comment-scope.md`). +10. **Two closing audits, side by side — a work package is finished only when both of them clear. Neither replaces the other, and neither waits for the other**: + 1. **Code review** — when all items are done, call `jsc-review:code-review` over the work package's diff and wait for the verdict. The comment rule of step 9.3 is that review's own group, and `jsc-git:commit` sweeps the whole tree again at step 11.1, so this step runs no separate comment sweep of its own. Each round of fixes **MUST run as a sub agent** inside the same worktree. Completion condition: the review returned a passing verdict. 2. **API document audit — on a project that supports Swagger, the work package stays unfinished until its controller files are fully documented.** Whether the project supports Swagger is decided in code by `jsc-review/tools/swagger-detect.sh {worktree path}`; run it and branch on its exit code instead of judging the project yourself: - `0` — the project supports Swagger. Call `jsc-review:api-doc` over the work package's changed controller files and wait for its verdict. What that audit checks belongs to `jsc-review:api-doc`; read the items there and keep no copy of them here. - `1` — the project does not support Swagger. **Skip this audit explicitly and report the skip.** A reported skip is a pass, never a failure. - `2` — bad arguments or a bad path. Fix them and run the script again; an undecidable detection is neither a pass nor a skip. A failing verdict is fixed, never waived: each round of fixes **MUST run as a sub agent** inside the same worktree, and `jsc-review:api-doc` runs again over the fixed files until it passes. Completion condition: `swagger-detect.sh` has run for this worktree and its exit code is reported, and either `jsc-review:api-doc` returned a passing verdict, or the skip is reported together with the exit code that caused it. + 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 4). - 3. Run `jsc-sdlc/tools/wp-gate.sh lock {owner}/{repo} {index} --wp {wp-number}`. The lock is what makes step 4'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 6.5, so `owns` can keep other sessions off it. Leave `--wp` out and every session is free to touch this PR. 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. Completion condition: the entry is saved on `LOG_{HASH}` before the wait starts. + 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). + 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: - - `0` — the PR merged or was closed. Run `jsc-sdlc/tools/wp-gate.sh check {owner}/{repo} {index}` to tell merged from closed-unmerged, release the lock and hand the claim back. `status=merged` → remove the worktree (`references/branch.md`) and mark the package done on the analysis page; `status=closed-unmerged` → report it and stop, closed without merging is not finished. Comments printed in that final round still get an outcome per step 4.5 to 4.9. - - `10` — new comments are waiting. Run step 4.4 to 4.9 over them (ownership, consensus, sub agent fixes, outcomes, timestamp, work log), then run `pr-watch.sh` again. Repeat for as many rounds as the reviewer needs. + - `0` — the PR merged or was closed. Run `jsc-sdlc/tools/wp-gate.sh check {owner}/{repo} {index}` to tell merged from closed-unmerged, release the lock and hand the claim back. `status=merged` → remove the worktree (`references/branch.md`) and mark the package done on the analysis page; `status=closed-unmerged` → report it and stop, closed without merging is not finished. Comments printed in that final round still get an outcome per step 2.6 to 2.10. + - `10` — new comments are waiting. Run step 2.5 to 2.10 over them (ownership, consensus, sub agent fixes, outcomes, timestamp, work log), then run `pr-watch.sh` again. Repeat for as many rounds as the reviewer needs. - `3` — the PR could not be found. Report and stop; a PR nobody can find never counts as merged. - `2` — bad arguments, or Gitea was unreachable on the very first poll. Fix the arguments or the environment and run it again; never fall back to eyeballing the PR page and calling it merged. - 6. **Do not start another work package in this session.** An unrelated package can still proceed, but in its own session with its own worktree; packages that depend on this one stay blocked until step 4 or step 11.5 sees it merged. + 6. **Do not start another work package in this session.** An unrelated package can still proceed, but in its own session with its own worktree; packages that depend on this one stay blocked until step 2 or step 11.5 sees it merged. 7. Completion condition: the PR exists, its URL is saved on the analysis page and reported to the user with the table format in `jsc-meta/references/pr-report.md`, the lock existed (`status=locked`), the work log entry is saved, and `pr-watch.sh` has returned `0` with `wp-gate.sh check` confirming `status=merged` — or the run stopped on a reported `2`, `3` or `status=closed-unmerged`. -12. **Delivery document** — a finished work package is a delivery, so **always ask before producing it; never pick a format silently and never skip this step**: - 1. Ask per `jsc-ask:ask` rules which 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). +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}` 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. A new page is added to `DELIVER_CONTENTS` per `templates/deliver-contents.md`. + 3. Wiki page: write `DELIVER_{HASH}` 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. Then upsert this work package's row in `DELIVER_CONTENTS` per `templates/deliver-contents.md` — add the row if missing, otherwise refresh it — following "Contents pages are appended, never overwritten" below. 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. Completion condition: the chosen format has actually been produced, and you have reported where it landed (wiki page name, or the comment URL). -13. Ask per `jsc-ask:ask` rules whether to register this project for maintenance: append to `MAINTAIN_CONTENTS` with `templates/maintain-contents.md`. Required: repository `{owner}/{repo}`, maintenance method, start date. Optional: end date (NULL = maintain forever), last-maintained time. Completion condition: the user has answered, and a chosen registration is saved on the wiki. -14. **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). Run `tools/stage-report.sh implement` with: + 5. With the delivery produced, ask per `jsc-ask:ask` rules whether to register this project for maintenance: upsert this repository's row in `MAINTAIN_CONTENTS` with `templates/maintain-contents.md` — add the row if missing, otherwise refresh it — following "Contents pages are appended, never overwritten" below. Required: repository `{owner}/{repo}`, maintenance method, start date. Optional: end date (NULL = maintain forever), last-maintained time. + 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), **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 — `ANALYZE_{HASH}`, `DELIVER_{HASH}` and `DELIVER_CONTENTS`, `MAINTAIN_CONTENTS`; - `--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; - `--worktree {path} --source-branch {name} --work-branch {name} --pr {url}` — the script reads the commit count, the push state and whether the source branch exists on the remote by itself, so pass the names, not your own count. 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. +## Contents pages are appended, never overwritten + +`DELIVER_CONTENTS` and `MAINTAIN_CONTENTS` are shared directories: 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 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, and a row this run does not own stays untouched. + +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 run'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 the step write a fresh template over a live directory, and every other work package's row is gone — the write carries no merge and no backup. `DELIVER_{HASH}` is the opposite case: it is a content page belonging to one work package, so writing it whole is correct. The distinction is the page, not the write. + +Completion condition: every contents-page write this stage made names the `wiki-get` exit code it branched on, and no page was created on any code other than 4. + ## Rules -- **One finished task, one work log entry.** A task is one of three things: one work package, one round of PR-comment fixes, or one standalone fix commit. Call `jsc-log:worklog` the moment one of them finishes — never let a stage end and then write a single catch-up entry, because by then the elapsed time, the token counts and the difficulties are gone. Every entry appends to the same `LOG_{HASH}` page, so one package that took five comment rounds leaves five entries. 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. -- The work ticket is a mutex: always skip work packages that already hold a ticket; never take one over. +- **One finished task, one work log entry.** A task is one of three things: one work package, one round of PR-comment fixes, or one standalone fix commit. Call `jsc-log:worklog` the moment one of them finishes — never let a stage end and then write a single catch-up entry, because by then the elapsed time, the token counts and the difficulties are gone. Every entry appends to the same `LOG_{HASH}` page, so one package that took five comment rounds leaves five entries. The PLAN and ANALYZE wiki repositories and page URLs are resolved once per work package and reused by every entry of that package; only a change of repository forces a fresh resolution. 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. +- The work ticket is a mutex: always skip work packages that already hold a ticket; never take one over. Between the claim (step 4.5) and the ticket write (step 6), `wp-gate.sh claim` is what holds the package — the claim is recorded in code, so a second session sees it even before the ticket reaches the page. - Never batch wiki updates across items; one item, one update. - Sample data written into code or fixtures follows the analysis page's 資料來源 column, under the rules in `references/deliver-formats.md`. - Everything the skill writes out (wiki content, commit messages, PR descriptions) stays Traditional Chinese per the STE100 rule. + +## What the single-key source-branch confirmation gives up + +Step 5.2 used to run a full decision tree that made the user say out loud that the source branch is **also the PR target**, every run. That explicit two-way confirmation is gone: a user who taps the confirm key now agrees to the analysis page's recorded branch as the PR target without being asked about the target separately. A stale or wrong PR target on the analysis page therefore reaches the PR unchallenged. The two exception cases in step 5.2 — no recorded branch, and a branch missing from the remote — are what remains of that guard, and they keep the full decision tree.