feat(狀態回報): 收尾寫一筆 skill-end 事件

現行紀錄只記「被叫用」,沒有成敗也沒有結束碼。跑完整輪的技能與開場就
中止的技能,在紀錄裡長得一模一樣。

start 由技能用量 hook 順手發,不必改技能文件。end 只能由技能自己在收尾
步驟寫——hook 接在技能工具呼叫上,而實際工作發生在之後的模型輪次,它在
原理上看不到成敗。有 start 沒有配對的 end,就是那一輪中止了。

status 五選一,每支技能各自寫明什麼情況選哪一個。找不到回報腳本就安靜
跳過,回報失敗一律不改變技能自己的結論。
This commit is contained in:
2026-09-02 16:01:15 +08:00
parent 277a8f1b88
commit aeb3f467e2
5 changed files with 84 additions and 12 deletions
+20
View File
@@ -87,3 +87,23 @@ Run before a skill run, to apply past lessons.
Done when both pages are read, or reported missing under exit 4, or the run stopped on 5, 7 or 8.
3. Surface every row whose 技能 matches the skill about to run, and summarize each matched 下次做法 for the caller to apply. Done when the matched rows (or 「無相符教訓」) are reported.
## Close: record how the run ended
Both modes end here, as the very last thing this skill does:
`jsc-hooks/tools/report-status.sh skill-end jsc-log:learn {status} {exit} "{detail}"`
Resolve that path the way this file already resolves `jsc-gitea/tools/hash-id` and the other sibling plugin scripts — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A lesson that was recorded stays recorded whether or not the recorder of recorders was installed.
| status | This skill's case |
| --- | --- |
| `ok` | record — both pages carry the new row and every row that was there before is still there. consult — both pages were read, or a page was reported absent under exit 4, and the matched 下次做法 lines reached the caller |
| `blocked` | The run never reached a page: `hash-id` exit 1 (no SHA-1 helper), `wiki-repo LEARN` or `wiki-repo CONTENTS` exit 3 (no wiki repo configured for that page type), or `link-check.sh` exit 3 (`GITEA_HOST` unset) |
| `degraded` | record — the lesson row is on `LEARN_{HASH}` but `wiki-contents.sh upsert` did not land the directory row (exit 1, 3, 7 or 8), so the lesson is on the wiki and nothing points at it. consult — one of the two pages was read and the other stopped the run, so the caller got part of the lesson set and knows it |
| `failed` | An API call answered with something unexpected after the work started: `link-check.sh` exit 1 on a DEAD link, a `wiki-get` that came back 7 or 8, or a page write that failed its retry as well. Nothing reached `LEARN_{HASH}` |
| `aborted` | The user stopped the run, or record mode was called with nothing reusable to record, so the six facts never formed an entry and no write was attempted |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: the mode plus counts and exit codes, never the lesson text, page names, branch names, or personal data.
Done when the command has run, or the script was absent and this step was skipped.
+18
View File
@@ -116,3 +116,21 @@ Done when the page URL is reported, or the skipped write is reported with its re
State the period label, entry count, repositories covered, template source, and the page URL. Name every unfinished work package that carried over — that list is what the next period starts from. State `ELAPSED_MISSING` and `TOKEN_MISSING` whenever either is above 0, so a small total is read as missing data rather than a light week.
Done when those five facts, the carry-over list and the two missing-data counts are stated.
Then record how the run ended, as the very last thing this skill does:
`jsc-hooks/tools/report-status.sh skill-end jsc-log:report {status} {exit} "{detail}"`
Resolve that path the way this file already resolves `jsc-gitea/tools/link-check.sh` and the other sibling plugin scripts — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A report that was produced stays produced whether or not the recorder was installed.
| status | This skill's case |
| --- | --- |
| `ok` | The period's section is on `REPORT_{HASH}` and the `REPORT_CONTENTS` row carries this period. `log-aggregate.sh` exit 3 stays `ok`: an empty range is an answer, and section 2 requires the report to be produced anyway — put `ENTRIES=0` in `{detail}` so the zero is read as a counted zero, not a run that quit |
| `blocked` | Nothing could be summarised and nothing was: `report-range.sh` exit 4 (this machine's `date` does no date arithmetic), `report-template.sh resolve` exit 3 (neither template exists), `wiki-repo CONTENTS` or `wiki-repo LOG` exit 3 (no directory or log pages to read), `hash-id` exit 1, or `link-check.sh` exit 3 or 7 |
| `degraded` | The report body is finished and handed to the caller but did not fully land: `wiki-repo REPORT` exit 3 skipped the wiki write entirely, or the section landed and `wiki-contents.sh upsert` did not, or `wiki-repo LEARN` exit 3 left the yearly 全年教訓 section filled with 無. Say which part is missing in `{detail}` |
| `failed` | The collection or the write broke part-way: `link-check.sh` exit 1 on a DEAD link, a `jsc-gitea:wiki` read or a `wiki-url` call that came back 7 or 8, `log-aggregate.sh` exit 4 on an unreadable page file, or a write that failed its retry as well |
| `aborted` | The user stopped the run, most often at the period question in section 1, so no range was ever fixed and no page was read |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: the period plus counts and exit codes, never report text, page names, branch names, or personal data.
Done when the command has run, or the script was absent and this step was skipped.
+17
View File
@@ -25,3 +25,20 @@ Data is recorded continuously by `jsc-hooks/hooks/skill-usage.sh` under `$JSC_HO
| 2 | The subcommand or the `--cli` argument was rejected — the subcommand is neither `skills` nor `chains`, or `--cli` came with no value. Fix the argument and rerun. Never rerun the same command unchanged, and never report the counts as zero: nothing was read |
Done when the exit code was read and the branch it names was taken.
2. Record how the run ended, as the very last thing this skill does:
`jsc-hooks/tools/report-status.sh skill-end jsc-log:stats {status} {exit} "{detail}"`
Resolve that path the way this file already names `jsc-hooks/hooks/skill-usage.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. This skill only reads counts; it must never fail because a count of its own could not be written.
| status | This skill's case |
| --- | --- |
| `ok` | The tool exited 0 and every printed line reached the report. A run that printed no line is `ok` as well — the count really is zero, so say `count=0` in `{detail}` rather than dressing an empty data file up as a problem |
| `blocked` | `tools/usage-stats.sh` is not on this machine, which is a partial plugin install rather than a zero count. No number was ever read, so none is reported |
| `degraded` | The caller asked for both counts and only one sub-command answered, so the report covers half of what was asked. Name the half that is missing in `{detail}` |
| `failed` | Exit 2 — the sub-command or the `--cli` value was rejected, so nothing was read. This is the one case that must never be reported as a count of zero, and recording it as `failed` is what keeps the two apart in the event stream too |
| `aborted` | The user stopped the run, or the request turned out to be about elapsed time or token usage, which belong to `jsc-log:worklog`; this skill then stops before it counts anything |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: the sub-command plus counts and exit codes, never branch names or personal data.
Done when the command has run, or the script was absent and this step was skipped.
+17
View File
@@ -115,3 +115,20 @@ The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id`
| 2 | The claim path is not the one `merge` produced, or it points outside this `{HASH}`'s pending directory. Rerun from step 4 with the `CLAIM` value that `merge` printed; never pass a hand-written path |
Done when one of the two ran and its exit code was reported.
8. Record how this run ended, as the very last thing this skill does:
`jsc-hooks/tools/report-status.sh skill-end jsc-log:worklog {status} {exit} "{detail}"`
Resolve that path the way row 5 already resolves `jsc-hooks/hooks/session-timer.sh` — the sibling plugin directory, no separate lookup rule for this one call. **A missing script is not a failure here: skip this step in silence and let the run end as it stands.** The script swallows its own write errors and exits 0 even then, so nothing branches on its code either. A run whose result could not be recorded still had that result, and a work log that fails because the recorder is absent is worse than no recording.
| status | This skill's case |
| --- | --- |
| `ok` | Steps 5 and 6 both wrote and step 7 cleared the pending area. `orphans` exit 4 stays `ok`: an orphan directory belongs to some other repository and changes nothing about this entry — carry its line count in `{detail}` so the count is on record even though the run passed |
| `blocked` | Nothing could be written and nothing was: `hash-id` exit 1 (no SHA-1 helper on this machine), `worklog-target.sh friday` exit 4 (no date arithmetic), `wiki-repo LOG` exit 3 (no LOG wiki repo configured), or `link-check.sh` exit 3 (`GITEA_HOST` unset) or exit 7 (token rejected). The gate stopped the run before a page was touched |
| `degraded` | The entry is on `LOG_{HASH}` but the close-out is short: `wiki-contents.sh upsert` returned non-zero so `LOG_CONTENTS` still carries the old row, or `worklog-pending.sh commit` exited 1 so the pending files survive a successful write and the next run merges them again |
| `failed` | Nothing reached the wiki after the run started working: `link-check.sh` exit 1 stopped the write on a DEAD link, the `PAGE` read came back 7 or 8, or `worklog-pending.sh merge` exited 1 |
| `aborted` | The user stopped the run, or the trigger turned out not to hold — no task ended here, so there is no entry to write and none was attempted |
`{exit}` is the exit code of whatever decided the status, `0` for `ok`. `{detail}` is one short line well under 200 characters: counts and exit codes only, never entry text, page names, branch names, or personal data.
Done when the command has run, or the script was absent and this step was skipped.