feat(狀態回報): 收尾寫一筆 skill-end 事件
現行紀錄只記「被叫用」,沒有成敗也沒有結束碼。跑完整輪的技能與開場就 中止的技能,在紀錄裡長得一模一樣。 start 由技能用量 hook 順手發,不必改技能文件。end 只能由技能自己在收尾 步驟寫——hook 接在技能工具呼叫上,而實際工作發生在之後的模型輪次,它在 原理上看不到成敗。有 start 沒有配對的 end,就是那一輪中止了。 status 五選一,每支技能各自寫明什麼情況選哪一個。找不到回報腳本就安靜 跳過,回報失敗一律不改變技能自己的結論。
This commit is contained in:
+16
-1
@@ -12,7 +12,7 @@ This skill is a **logic-only** stage: never output code, and **never modify any
|
||||
|
||||
**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.
|
||||
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. Steps 8 and 9 still run after such a stop.
|
||||
|
||||
## Steps
|
||||
|
||||
@@ -41,6 +41,21 @@ All wiki reads and writes go through `jsc-gitea:wiki`. **A failed wiki read or w
|
||||
|
||||
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.
|
||||
9. **Write this run's `skill-end` status event — the very last thing this stage does, right after step 8, on every path including every early stop.** Run `jsc-hooks/tools/report-status.sh skill-end jsc-sdlc:plan {status} {exit} [detail]`, naming the script the way this stage already names `jsc-hooks/hooks/sdlc-gate.sh` in step 1. The matching `skill-start` event is written by jsc-hooks on its own, so this step owes only the `end`: a hook fires on the skill tool call and this stage's work happens in the model turns after it, so **no hook can see how this run ended**. A `start` with no `end` is what an aborted run looks like in the record, and this step is the only thing that keeps a finished run from looking like one.
|
||||
|
||||
`{status}` is one of five words, never a sixth:
|
||||
|
||||
| Status | When `plan` reports it |
|
||||
| --- | --- |
|
||||
| `ok` | Every step's completion condition is met: the gate passed, all three consensus items were confirmed by the user, `PLAN_{HASH}` is saved with no placeholder left, the `PLAN_CONTENTS` row carries the literal 「未分析」 and a checked absolute link, and `tools/stage-report.sh` exited 0 |
|
||||
| `blocked` | Step 1's model gate stopped the run: `sdlc-gate.sh lock plan` exited non-zero because the model running this stage carries no `reasoning-max` tag. Nothing was planned, so this is **never `failed`** — the gate stopping an underpowered model is the gate working, and recording it as a failure sends the next reader hunting for a defect that is not there |
|
||||
| `failed` | The run got past the gate and then a write did not land: the `PLAN_{HASH}` write failed, `wiki-url` returned 5, 7 or 8, `link-check.sh` returned 1 so the page was never written, or `wiki-contents.sh` returned 1, 7 or 8 while the plan page is also unsaved |
|
||||
| `degraded` | The plan page is saved but the directory did not follow it: `wiki-contents.sh` returned 1, 3, 7 or 8 over `PLAN_CONTENTS`, or `tools/stage-report.sh` exited 1 (no work log, or a link in its list does not answer). The plan exists; what is missing is the directory row that lets anyone find it |
|
||||
| `aborted` | The user stopped the run, or the run stopped itself because its premise did not hold — no plan was selectable and the user wanted no new one, or the consensus rounds ended with no agreement, so no user story was written |
|
||||
|
||||
`{exit}` is the exit code of the script whose verdict decided the status — the gate's code for `blocked`, the failing script's code for `failed` and `degraded` — and `0` when nothing exited non-zero, `ok` and `aborted` included. `[detail]` is optional and Traditional Chinese per the STE100 rule: one line, no line break, naming what decided the status (for example 「模型能力標籤不符」 or 「目錄頁未更新」). The script truncates it at 200 characters, so put the short reason there and nothing else.
|
||||
|
||||
**A failure in this step never changes this stage's verdict.** The script is not found (jsc-hooks is not installed on this machine, or this CLI's layout puts it somewhere else) → skip the event quietly and carry on; nothing is reported to the user and no step is re-run. The three recording sub-commands are built to exit 0 even when the write fails, so a non-zero code here means only that the call itself was malformed (exit 2, a usage error) — fix the arguments once and, either way, never turn a finished stage into a failed one because the record of it failed. Completion condition: one `skill-end` event has been written for this run, or the script could not be found and that skip is the reason no event exists.
|
||||
|
||||
## Every link is checked before it reaches a page
|
||||
|
||||
|
||||
Reference in New Issue
Block a user