From fbfe424c39e65f3f6fd019ecdca183160ceea9a2 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:22:33 +0800 Subject: [PATCH 1/5] =?UTF-8?q?feat(worklog):=20=E6=97=A5=E8=AA=8C?= =?UTF-8?q?=E7=9B=AE=E9=8C=84=E9=A0=81=E6=94=B9=E6=88=90=E4=B8=80=E5=80=8B?= =?UTF-8?q?=E6=97=A5=E8=AA=8C=E9=A0=81=E4=B8=80=E5=80=8B=E5=A4=A7=E6=A8=99?= =?UTF-8?q?=E9=A1=8C=E5=8D=80=E5=A1=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 日誌目錄頁的版面從 markdown 表格換成「大標題加條列」:一個日誌頁一個大標題 區塊,標題就是那個日誌頁的實際頁名,週五日期、條目數這些欄位改成標題底下的 一層條列。範本與 worklog 技能的寫入敘述、完成條件、退出碼說明一起跟上。 表格的欄位組合是整頁共用的,一頁上卻有很多存取庫各自的紀錄,每一輪只重寫 自己那一列。欄位一增減,舊列的格數與表頭就對不上,而目錄頁不能整頁覆蓋—— 覆蓋等於刪掉別人的紀錄。條列一筆一個區塊,欄位各自獨立,加一條只動到自己 那一個區塊。 比對鍵從裸雜湊那一格改成大標題本身,標題寫成日誌頁的實際頁名。頁名只由 存取庫的擁有者與名稱決定,換主機位址、把日誌頁移到別的存取庫,或頁名編碼 有差,都動不到它;含網址的那一條連結照樣留著給人點,但不當鍵——拿它當鍵, 上面任一件事一變,這一輪的文字就跟上一輪不一樣,比不到既有那一筆就走附加, 同一個日誌頁多出第二個區塊,舊區塊從此再也更新不到。欄號那個參數只在舊 表格頁轉檔時用得到,頁面已經是條列就完全忽略它。 範圍是工作日誌的目錄頁與 worklog 技能。 --- skills/worklog/SKILL.md | 28 ++++++++++++++-------------- templates/log-contents.md | 21 +++++++++++++-------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/skills/worklog/SKILL.md b/skills/worklog/SKILL.md index 204ecbf..89dc1c4 100644 --- a/skills/worklog/SKILL.md +++ b/skills/worklog/SKILL.md @@ -1,6 +1,6 @@ --- name: worklog -description: Append one work-log entry to wiki LOG_{HASH} plus LOG_CONTENTS as soon as a task ends, where a task is one work package, one round of PR-comment fixes, or one standalone fix commit — one task, one entry, appended to the same page. Every entry carries the ten facts (repo, branch, plan link, work package link, elapsed time from session-timer, token usage per CLI, status, details, difficulties, PR target); HASH is the full 40-character uppercase SHA-1 of {owner}/{repo} and the work-week Friday drives the page content. LOG_{HASH} sits in the LOG wiki repo while LOG_CONTENTS sits in the separate CONTENTS repo, so the directory row goes through jsc-gitea/tools/wiki-contents.sh upsert and links the log page by its absolute wiki-url. Merge whatever tools/worklog-pending.sh holds for that HASH into the same write, then clear the pending area once that write succeeded. Trigger at the end of every such task in implement or maintain; not for planning notes. +description: Append one work-log entry to wiki LOG_{HASH} plus LOG_CONTENTS as soon as a task ends, where a task is one work package, one round of PR-comment fixes, or one standalone fix commit — one task, one entry, appended to the same page. Every entry carries the ten facts (repo, branch, plan link, work package link, elapsed time from session-timer, token usage per CLI, status, details, difficulties, PR target); HASH is the full 40-character uppercase SHA-1 of {owner}/{repo} and the work-week Friday drives the page content. LOG_{HASH} sits in the LOG wiki repo while LOG_CONTENTS sits in the separate CONTENTS repo, so the directory entry goes through jsc-gitea/tools/wiki-contents.sh upsert as one H2 block keyed by the page name LOG_{HASH}, with a bullet per field and the log page linked by its absolute wiki-url. Merge whatever tools/worklog-pending.sh holds for that HASH into the same write, then clear the pending area once that write succeeded. Trigger at the end of every such task in implement or maintain; not for planning notes. --- # worklog — work log @@ -47,7 +47,7 @@ The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id` - Hash: `jsc-gitea/tools/hash-id "{owner}/{repo}"` on the code repo from row 1. Exit 1 means this machine has no SHA-1 helper: stop and report that `sha1sum` or `shasum` has to be installed, and never hand-compute the hash. Exit 2 means the input was empty, which happens when row 1 failed to parse `{owner}/{repo}`: fix row 1 and rerun, because the empty string has a valid SHA-1 and would file this entry on a page nobody reads. Done when the hosting `{owner}/{repo}` and the full 40-character uppercase `{HASH}` are both known. -2. Run `tools/worklog-target.sh "{HASH}" all` and `tools/worklog-target.sh friday` in parallel. `all` prints `PAGE=LOG_{HASH}` and `CONTENTS=LOG_CONTENTS`; `friday` prints the Friday of the current work week as `yyyy-MM-dd`, which drives the page content and the row dates. Exit 2 means the arguments were rejected, so fix them and rerun. Exit 4 from `friday` means this machine's `date` does no date arithmetic: stop and report it, because a hand-picked Friday is exactly what goes wrong across a month or year boundary. Done when both page names and that Friday date are known. +2. Run `tools/worklog-target.sh "{HASH}" all` and `tools/worklog-target.sh friday` in parallel. `all` prints `PAGE=LOG_{HASH}` and `CONTENTS=LOG_CONTENTS`; `friday` prints the Friday of the current work week as `yyyy-MM-dd`, which drives the page content and the directory block's dates. Exit 2 means the arguments were rejected, so fix them and rerun. Exit 4 from `friday` means this machine's `date` does no date arithmetic: stop and report it, because a hand-picked Friday is exactly what goes wrong across a month or year boundary. Done when both page names and that Friday date are known. 3. Fill `templates/log-entry.md` with the ten facts of this one task and save it to a file. Done when that file holds exactly one entry. 4. Run `tools/worklog-pending.sh merge {HASH} {entry file}`. It prints `MERGED=` (pending content in time order, then this task's entry), `CLAIM=` (the pending files it took) and `PENDING=` (how many). Pending content was written by an earlier stage that ended without a work log, so it belongs in **this** write. `merge` also picks up the legacy `H` + first 7 characters directory of the same `{HASH}`, so pending content stored under the previous hash rule still reaches the wiki. @@ -83,29 +83,29 @@ The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id` | 7 | The Gitea token was rejected (HTTP 401/403). Stop and report the token problem. A rejected token makes live pages look missing, and one batch judged on that answer wipes out links that still work | A failed write stops the run and goes to step 7 as a failure — never report the page as written when it was not. Done when every entry in `MERGED` is on `PAGE`, every entry that was already there is still there, and any 4 / 7 / 8 branch was followed as stated. -6. Update `CONTENTS` in the same pass, and let `jsc-gitea/tools/wiki-contents.sh` do the row work — never hand-edit the directory page. +6. Update `CONTENTS` in the same pass, and let `jsc-gitea/tools/wiki-contents.sh` do the block work — never hand-edit the directory page. - `LOG_CONTENTS` lives in the CONTENTS wiki repo that `gitea.sh wiki-repo CONTENTS` resolves (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`), which is **not** the LOG repo of step 1 and never falls back to it. Because the two pages sit in different wikis, the row links the log page as `[LOG_{HASH}]()`, with `` from `gitea.sh wiki-url LOG_{HASH}` — never the same-wiki `[[...]]` form, which resolves inside one wiki only and dead-links from here without reporting an error. `wiki-url` exit 4 means the step 5 write has not landed yet, so stop and rerun step 5 before this one; exit 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand; exit 7 or 8 means the token or the API failed, so stop and report that status. + `LOG_CONTENTS` lives in the CONTENTS wiki repo that `gitea.sh wiki-repo CONTENTS` resolves (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`), which is **not** the LOG repo of step 1 and never falls back to it. Because the two pages sit in different wikis, the 日誌頁 bullet links the log page as `[LOG_{HASH}]()`, with `` from `gitea.sh wiki-url LOG_{HASH}` — never the same-wiki `[[...]]` form, which resolves inside one wiki only and dead-links from here without reporting an error. `wiki-url` exit 4 means the step 5 write has not landed yet, so stop and rerun step 5 before this one; exit 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand; exit 7 or 8 means the token or the API failed, so stop and report that status. - Put that URL through the step 5 `link-check.sh` gate before the upsert, with the same exit branches: only exit 0 writes the row, and a DEAD line stops the write and goes to step 7 as a failure. + Put that URL through the step 5 `link-check.sh` gate before the upsert, with the same exit branches: only exit 0 writes the block, and a DEAD line stops the write and goes to step 7 as a failure. - Build one file holding the single row from `templates/log-contents.md` — the absolute link, the bare `{HASH}` of step 1, this week's Friday date from step 2, the entry count and the update time — then run: + Build one file holding the single H2 block from `templates/log-contents.md` — the `## LOG_{HASH}` heading, a blank line, then one `- {欄位名}:{值}` bullet per field in the template's order: the absolute link, the bare `{HASH}` of step 1, this week's Friday date from step 2, the entry count and the update time. Then run: - `jsc-gitea/tools/wiki-contents.sh upsert LOG 2 "{HASH}" {row file} templates/log-contents.md` + `jsc-gitea/tools/wiki-contents.sh upsert LOG 1 "LOG_{HASH}" {block file} templates/log-contents.md` - The key is column 2, the bare 40-character `{HASH}` with no link markup around it. Column 1 carries the same page as a link for a human to click, and that link is exactly what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST`, one move of `JSC_WIKI_REPO_LOG`, or one difference in how Gitea encodes the page name makes this run's cell differ from the last run's, the match fails, the row is appended, and the same log page now owns two rows of which the older is never updated again. The bare hash depends only on `{owner}/{repo}`. The script reads the whole page, replaces the matching row and appends when none matches, so every row that belongs to another log page stays as it was. + The key is the H2 heading itself, the content page name `LOG_{HASH}`, and the 日誌頁 bullet carries that same page as a link for a human to click. That link is exactly what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST`, one move of `JSC_WIKI_REPO_LOG`, or one difference in how Gitea encodes the page name makes this run's text differ from the last run's, the match fails, the block is appended, and the same log page now owns two blocks of which the older is never updated again. The page name depends only on `{owner}/{repo}`, so none of those three touches it. The `1` in the command is ``, which the script uses only while the directory page is still an old markdown table — it names the column whose cell text becomes the H2 heading, and a page already in list shape ignores it. The script reads the whole page, replaces the matching block and appends when none matches, so every block that belongs to another log page stays as it was. | Exit | Do | | --- | --- | - | 0 | The row is in place. It prints `updated` or `added` plus the page it wrote — carry that word into the close-out | - | 1 | The write failed, or the directory page holds no markdown table. Stop and report it as a failed write, and go to step 7 as a failure | - | 2 | An argument was rejected (unknown type, key column, missing row file). Fix the argument and rerun this step; nothing was written | + | 0 | The block is in place. It prints `updated` or `added` plus the page it wrote — carry that word into the close-out | + | 1 | The page content could not be assembled, or the write failed. Stop and report it as a failed write, and go to step 7 as a failure. A page with no matching block is not this code: the block is appended instead | + | 2 | An argument was rejected (unknown type, key column, missing block file). Fix the argument and rerun this step; nothing was written | | 3 | No CONTENTS wiki repo is configured. Stop and report `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set, and go to step 7 as a failure. The log entry itself is on `PAGE` and stays there | | 4 | The directory page is absent and the script received no template. The call above always passes one, so this code means `templates/log-contents.md` is not at that path — a partial plugin install, not a missing argument. Stop and report the path; rerunning the same command changes nothing. Reinstall the plugin, confirm the file is there, then rerun. A mistyped template path exits 2, not 4 | - | 7 | The key is invalid or lacks permission, so the other rows are unknown. Stop and report the key problem; the script wrote nothing, which is what keeps the other pages' rows alive | + | 7 | The key is invalid or lacks permission, so the other blocks are unknown. Stop and report the key problem; the script wrote nothing, which is what keeps the other pages' blocks alive | | 8 | Some other API failure. Stop and report that status and retry only after the API is back | - Done when the run exited 0 and the row for `PAGE` carries this week's Friday date from step 2, or a non-zero code was reported and step 7 ran as a failure. + Done when the run exited 0 and the block for `PAGE` carries this week's Friday date from step 2, or a non-zero code was reported and step 7 ran as a failure. 7. Close the pending area on the result of steps 5 and 6: `tools/worklog-pending.sh commit {HASH} {CLAIM}` after both succeeded, or `tools/worklog-pending.sh abort {HASH} {CLAIM}` after either failed. `abort` keeps every pending file for the retry, so keep the entry file too and rerun from step 4. | Exit | Do | @@ -125,7 +125,7 @@ The `{HASH}` in every page name above is computed with `jsc-gitea/tools/hash-id` | --- | --- | | `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 | + | `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 block, 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 | diff --git a/templates/log-contents.md b/templates/log-contents.md index e714faa..92642bf 100644 --- a/templates/log-contents.md +++ b/templates/log-contents.md @@ -1,13 +1,18 @@ # 日誌目錄 -> 由 `jsc-log:worklog` 維護。每個日誌頁一列;`{HASH}` 是 `{owner}/{repo}` 的完整 40 碼大寫十六進位 SHA-1,頁內仍依該週五日期整理。 +> 由 `jsc-log:worklog` 維護。每個日誌頁一個區塊;`{HASH}` 是 `{owner}/{repo}` 的完整 40 碼大寫十六進位 SHA-1,頁內仍依該週五日期整理。 > 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,日誌頁 `LOG_{HASH}` 落在 `JSC_WIKI_REPO_LOG` 的存取庫,兩者分屬不同 wiki。 -> 連結寫法:日誌頁那一欄寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 +> 連結寫法:日誌頁那一條寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 > 連結驗證:每個要寫進本頁的連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。 -> 寫入語意:一列代表一個日誌頁。一律用 `jsc-gitea/tools/wiki-contents.sh upsert LOG 2 {HASH} {列檔} {本範本}` 單列整頁寫回——它讀整頁、找得到該頁既有的那一列就換掉那一列,找不到才附加。 -> 鍵取第 2 欄的裸 HASH,不取第 1 欄的連結。第 1 欄的連結帶著主機名與存取庫名:`GITEA_HOST` 一換、`JSC_WIKI_REPO_LOG` 改指別的存取庫,或 Gitea 對頁名的編碼有差,連結就跟上一輪寫的不一樣,鍵比不到就走附加,同一個日誌頁多出第二列,舊列從此不再更新。裸 HASH 只跟 `{owner}/{repo}` 有關,這三件事都動不到它。 -> 禁止整頁覆蓋,也不得改動別人的列。 +> 寫入語意:一個區塊代表一個日誌頁。一律用 `jsc-gitea/tools/wiki-contents.sh upsert LOG 1 LOG_{HASH} {區塊檔} {本範本}` 單一區塊整頁寫回——它讀整頁、找得到該頁既有的那個區塊就換掉,找不到才附加到頁尾。 +> 參數語意:`` 的 `1` 只在舊頁還是表格時用得到,代表轉檔時取第 1 欄格子的文字當 H2 標題;頁面已經是條列格式就完全忽略它。`` 是 H2 標題文字,也就是內容頁頁名 `LOG_{HASH}`。第四個參數是區塊檔,內容是 `## {key}` 那一行加空行加各條欄位,不是一列表格。 +> 鍵是 H2 標題的頁名,不是連結。頁名只由 `{owner}/{repo}` 決定:`GITEA_HOST` 一換、`JSC_WIKI_REPO_LOG` 改指別的存取庫,或 Gitea 對頁名的編碼有差,都動不到它。連結帶著主機名與存取庫名,這三件事任一變動就跟上一輪寫的不一樣;拿連結當鍵就比不到既有那一筆,走附加,同一個日誌頁多出第二個區塊,舊區塊從此不再更新。 +> 禁止整頁覆蓋,也不得改動別人的區塊。 -| 日誌頁 | HASH | 週五日期 | 條目數 | 最後更新 | -| --- | --- | --- | --- | --- | -| [LOG_{HASH}]({日誌頁絕對網址}) | {HASH} | {yyyy-MM-dd} | {n} | {yyyy-MM-dd HH:mm} | +## LOG_{HASH} + +- 日誌頁:[LOG_{HASH}]({日誌頁絕對網址}) +- HASH:{HASH} +- 週五日期:{yyyy-MM-dd} +- 條目數:{n} +- 最後更新:{yyyy-MM-dd HH:mm} From cce75e2f45c978018083ce85bb14d43da1a030c1 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:22:33 +0800 Subject: [PATCH 2/5] =?UTF-8?q?feat(learn):=20=E6=95=99=E8=A8=93=E7=9B=AE?= =?UTF-8?q?=E9=8C=84=E9=A0=81=E6=94=B9=E6=88=90=E4=B8=80=E5=80=8B=E5=AD=98?= =?UTF-8?q?=E5=8F=96=E5=BA=AB=E4=B8=80=E5=80=8B=E5=A4=A7=E6=A8=99=E9=A1=8C?= =?UTF-8?q?=E5=8D=80=E5=A1=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 教訓目錄頁的版面從 markdown 表格換成「大標題加條列」:一個存取庫一個大標題 區塊,標題就是那個存取庫教訓頁的實際頁名,存取庫名稱、教訓紀錄連結、最後 更新時間改成標題底下的一層條列。範本與 learn 技能的寫入敘述一起跟上。 表格的欄位組合是整頁共用的,一頁上卻有很多存取庫各自的紀錄,每一輪只重寫 自己那一列。欄位一增減,舊列的格數與表頭就對不上,而目錄頁不能整頁覆蓋—— 覆蓋等於刪掉別人的紀錄。條列一筆一個區塊,欄位各自獨立,加一條只動到自己 那一個區塊。 比對鍵從存取庫名稱那一格改成大標題本身,標題寫成教訓頁的實際頁名。頁名只由 存取庫的擁有者與名稱決定,換主機位址、改存取庫或頁名編碼有差都動不到它。 欄號那個參數配合線上實際欄位改成教訓紀錄那一欄:轉檔時取那一格的文字當 標題,格子寫成連結就只取顯示文字。欄號填錯的話,轉出來的標題跟鍵對不上, 既有那一筆會被當成新的附加上去,同一個存取庫變兩個區塊。 範圍是教訓紀錄的目錄頁與 learn 技能。 --- skills/learn/SKILL.md | 31 ++++++++++++++++--------------- templates/learn-contents.md | 19 +++++++++++-------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/skills/learn/SKILL.md b/skills/learn/SKILL.md index d7d4afb..00d426c 100644 --- a/skills/learn/SKILL.md +++ b/skills/learn/SKILL.md @@ -1,6 +1,6 @@ --- name: learn -description: Record a lesson learned after a skill run to wiki LEARN_{HASH} plus LEARN_CONTENTS, or consult past lessons before a skill run. Each entry is one table row with date, skill, CLI, situation, lesson, and next-time approach. HASH is the full 40-character uppercase SHA-1 of {owner}/{repo}; LEARN_{HASH} sits in the LEARN wiki repo while LEARN_CONTENTS sits in the separate CONTENTS repo, so the directory row goes through jsc-gitea/tools/wiki-contents.sh upsert and links the lesson page by its absolute wiki-url. Use when a skill run produced a reusable lesson, or before running a skill to consult past lessons; not for work-time logs (see worklog). +description: Record a lesson learned after a skill run to wiki LEARN_{HASH} plus LEARN_CONTENTS, or consult past lessons before a skill run. Each entry is one table row with date, skill, CLI, situation, lesson, and next-time approach. HASH is the full 40-character uppercase SHA-1 of {owner}/{repo}; LEARN_{HASH} sits in the LEARN wiki repo while LEARN_CONTENTS sits in the separate CONTENTS repo, so the directory entry goes through jsc-gitea/tools/wiki-contents.sh upsert as one H2 block keyed by the page name LEARN_{HASH}, with a bullet per field and the lesson page linked by its absolute wiki-url. Use when a skill run produced a reusable lesson, or before running a skill to consult past lessons; not for work-time logs (see worklog). --- # learn — lessons learned @@ -11,10 +11,11 @@ Close the loop on skill runs: record what a run taught you, consult it before th - Directory page: `LEARN_CONTENTS`. Content page: `LEARN_{HASH}`, one page per repository. - The two pages live in different wikis. `LEARN_{HASH}` goes to `jsc-gitea/tools/gitea.sh wiki-repo LEARN`; `LEARN_CONTENTS` goes to `gitea.sh wiki-repo CONTENTS` (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3), which never falls back to the LEARN repo. Exit 3 on either hands the question to `jsc-gitea:wiki`, which owns the resolution order and the wording; exit 2 means the type argument was misspelled, so fix it and rerun. -- Every link this skill writes takes the shape `[{text}]({absolute URL})`. The directory row links the lesson page as `[LEARN_{HASH}]()`, with `` from `gitea.sh wiki-url LEARN_{HASH}` — never assembled by hand, and never the same-wiki `[[...]]` form, which resolves inside one wiki only and dead-links from the directory without reporting an error. `wiki-url` exit 4 means the lesson page is not written yet, so write it first; exit 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand; exit 7 or 8 means the token or the API failed, so stop and report that status. +- Every link this skill writes takes the shape `[{text}]({absolute URL})`. The directory block's 教訓紀錄 bullet links the lesson page as `[LEARN_{HASH}]()`, with `` from `gitea.sh wiki-url LEARN_{HASH}` — never assembled by hand, and never the same-wiki `[[...]]` form, which resolves inside one wiki only and dead-links from the directory without reporting an error. `wiki-url` exit 4 means the lesson page is not written yet, so write it first; exit 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand; exit 7 or 8 means the token or the API failed, so stop and report that status. - Check every link before it reaches a page: `jsc-gitea/tools/link-check.sh {url}...`. Only exit 0 permits the write. - Compute `{HASH}` from `{owner}/{repo}` with `jsc-gitea/tools/hash-id`. It prints the full 40-character uppercase SHA-1 — no truncation and no prefix rewrite, so never shorten it. Exit 1 means no SHA-1 helper on this machine: stop and report that `sha1sum` or `shasum` has to be installed, and never hand-compute the hash. Exit 2 means the input was empty, so fix the `{owner}/{repo}` parse and rerun. -- All wiki reads and writes go through `jsc-gitea:wiki`, except the `LEARN_CONTENTS` row, which goes through `jsc-gitea/tools/wiki-contents.sh`. +- All wiki reads and writes go through `jsc-gitea:wiki`, except the `LEARN_CONTENTS` block, which goes through `jsc-gitea/tools/wiki-contents.sh`. +- The two pages carry different shapes. `LEARN_{HASH}` stays a markdown table, one row per lesson. `LEARN_CONTENTS` is a list page: `# 教訓目錄`, a `>` preamble, then one H2 block per repository whose heading is that repository's lesson page name. ## Mode: record @@ -46,37 +47,37 @@ Run after a skill run that produced a reusable lesson. | Exit | Do | | --- | --- | - | 0 | Every link answered. Write the row | + | 0 | Every link answered. Write the row and the directory block | | 1 | At least one link is DEAD. Write nothing and report the DEAD lines to the caller | | 2 | No URL reached the script. Pass the URLs and rerun | | 3 | The list holds a Gitea URL but `GITEA_HOST` is unset. Set it and rerun; never skip the check | | 7 | The Gitea token was rejected (HTTP 401/403). Stop and report the token problem. A rejected token makes live pages look missing, and one batch judged on that answer wipes out links that still work | - - Update `LEARN_CONTENTS` in the same pass, and let `jsc-gitea/tools/wiki-contents.sh` do the row work — never hand-edit the directory page. Build one file holding the single row from `templates/learn-contents.md` (the repository name, the absolute link from `gitea.sh wiki-url LEARN_{HASH}`, and the update time), then run: + - Update `LEARN_CONTENTS` in the same pass, and let `jsc-gitea/tools/wiki-contents.sh` do the block work — never hand-edit the directory page. Build one file holding the single H2 block from `templates/learn-contents.md` — the `## LEARN_{HASH}` heading, a blank line, then one `- {欄位名}:{值}` bullet per field in the template's order: the repository name, the absolute link from `gitea.sh wiki-url LEARN_{HASH}`, and the update time. Then run: - `jsc-gitea/tools/wiki-contents.sh upsert LEARN 1 "{owner}/{repo}" {row file} templates/learn-contents.md` + `jsc-gitea/tools/wiki-contents.sh upsert LEARN 2 "LEARN_{HASH}" {block file} templates/learn-contents.md` - Column 1 is the repository name, so the key stays the same string across every run and one repository keeps exactly one row. The script reads the whole page, replaces the matching row and appends when none matches, so every row that belongs to another repository stays as it was. + The key is the H2 heading itself, the content page name `LEARN_{HASH}`, so it stays the same string across every run and one repository keeps exactly one block. The 教訓紀錄 bullet carries that same page as a link, and that link is what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST`, one move of `JSC_WIKI_REPO_LEARN`, or one difference in how Gitea encodes the page name makes the match fail and appends a second block for the same repository. The page name depends only on `{owner}/{repo}`. The `2` is ``, which the script uses only while the directory page is still an old markdown table — it names the column whose cell text (the link text alone) becomes the H2 heading, and a page already in list shape ignores it. The script reads the whole page, replaces the matching block and appends when none matches, so every block that belongs to another repository stays as it was. | Exit | Do | | --- | --- | - | 0 | The row is in place. It prints `updated` or `added` plus the page it wrote | - | 1 | The write failed, or the directory page holds no markdown table. Report `LEARN_CONTENTS` as not written, together with the row content | + | 0 | The block is in place. It prints `updated` or `added` plus the page it wrote | + | 1 | The page content could not be assembled, or the write failed. Report `LEARN_CONTENTS` as not written, together with the block content. A page with no matching block is not this code: the block is appended instead | | 2 | An argument was rejected. Fix the argument and rerun this bullet; nothing was written | | 3 | No CONTENTS wiki repo is configured. Report `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set. The lesson itself is on `LEARN_{HASH}` and stays there | | 4 | The directory page is absent and the script received no template. The call above always passes one, so this code means `templates/learn-contents.md` is not at that path — a partial plugin install, not a missing argument. Stop and report the path; rerunning the same command changes nothing. Reinstall the plugin, confirm the file is there, then rerun. A mistyped template path exits 2, not 4 | - | 7 | The token is invalid or lacks permission, so the other repositories' rows are unknown. Stop and report the token problem; the script wrote nothing, which is what keeps those rows alive | + | 7 | The token is invalid or lacks permission, so the other repositories' blocks are unknown. Stop and report the token problem; the script wrote nothing, which is what keeps those blocks alive | | 8 | Some other API failure. Stop and report that status and retry only after the API is back | - - A failed write on either page: retry once. Still failing, stop and report which page was not written (`LEARN_{HASH}` or `LEARN_CONTENTS`) together with the row content that was meant to go in, so the lesson is not lost. Never report a page as written when it was not. - - Done when the sub agent reports both pages written, the main agent has confirmed the row exists on `LEARN_{HASH}`, and the rows that were there before are still there. + - A failed write on either page: retry once. Still failing, stop and report which page was not written (`LEARN_{HASH}` or `LEARN_CONTENTS`) together with the row or block content that was meant to go in, so the lesson is not lost. Never report a page as written when it was not. + - Done when the sub agent reports both pages written, the main agent has confirmed the row exists on `LEARN_{HASH}` and the block on `LEARN_CONTENTS`, and every row and block that was there before is still there. ## Mode: consult Run before a skill run, to apply past lessons. 1. Resolve `{owner}/{repo}` and compute `{HASH}` as in record mode. Done when `LEARN_{HASH}` is known. -2. Read `LEARN_CONTENTS` from the CONTENTS repo and the repo's `LEARN_{HASH}` from the LEARN repo via `jsc-gitea:wiki` — two `wiki-repo` calls, two different wikis — and branch on the exit code `jsc-gitea/tools/gitea.sh` returned. Only 4 means the page is absent; every other failure code means the read never happened, so an empty page must never be inferred from it. The rows on `LEARN_CONTENTS` point at lesson pages by absolute URL, and rows for other repositories point outside the LEARN repo resolved here, so follow each link as given rather than treating the page name as local. +2. Read `LEARN_CONTENTS` from the CONTENTS repo and the repo's `LEARN_{HASH}` from the LEARN repo via `jsc-gitea:wiki` — two `wiki-repo` calls, two different wikis — and branch on the exit code `jsc-gitea/tools/gitea.sh` returned. Only 4 means the page is absent; every other failure code means the read never happened, so an empty page must never be inferred from it. The blocks on `LEARN_CONTENTS` point at lesson pages by absolute URL, and blocks for other repositories point outside the LEARN repo resolved here, so follow each link as given rather than treating the H2 heading as a page in this wiki. | Exit | Do | | --- | --- | @@ -98,9 +99,9 @@ Resolve that path the way this file already resolves `jsc-gitea/tools/hash-id` a | 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 | +| `ok` | record — `LEARN_{HASH}` carries the new row and `LEARN_CONTENTS` this repository's block, and everything 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 | +| `degraded` | record — the lesson row is on `LEARN_{HASH}` but `wiki-contents.sh upsert` did not land the directory block (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 | diff --git a/templates/learn-contents.md b/templates/learn-contents.md index cfc5026..1848df5 100644 --- a/templates/learn-contents.md +++ b/templates/learn-contents.md @@ -1,13 +1,16 @@ # 教訓目錄 -> 由 `jsc-log:learn` 維護。這是教訓目錄頁 `LEARN_CONTENTS`。每個存取庫一列;`LEARN_{HASH}` 的 `{HASH}` 依共用 wiki hash 規則產生:取 `{owner}/{repo}` 的完整 SHA-1 四十碼,a-f 轉大寫,不截短、不加前綴。 +> 由 `jsc-log:learn` 維護。這是教訓目錄頁 `LEARN_CONTENTS`。每個存取庫一個區塊;`LEARN_{HASH}` 的 `{HASH}` 依共用 wiki hash 規則產生:取 `{owner}/{repo}` 的完整 SHA-1 四十碼,a-f 轉大寫,不截短、不加前綴。 > 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,教訓頁 `LEARN_{HASH}` 落在 `JSC_WIKI_REPO_LEARN` 的存取庫,兩者分屬不同 wiki。 -> 連結寫法:教訓紀錄那一欄寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 +> 連結寫法:教訓紀錄那一條寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 > 連結驗證:每個要寫進本頁的連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。 -> 寫入語意:一列代表一個存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert LEARN 1 {owner}/{repo} {列檔} {本範本}` 單列整頁寫回——它讀整頁、找得到該存取庫既有的那一列就換掉那一列,找不到才附加。 -> 鍵取第 1 欄的存取庫名稱,不取第 2 欄的連結。存取庫名稱每一輪都一樣,連結會隨主機名與頁名編碼變動。 -> 禁止整頁覆蓋,也不得改動別人的列。 +> 寫入語意:一個區塊代表一個存取庫。一律用 `jsc-gitea/tools/wiki-contents.sh upsert LEARN 2 LEARN_{HASH} {區塊檔} {本範本}` 單一區塊整頁寫回——它讀整頁、找得到該存取庫既有的那個區塊就換掉,找不到才附加到頁尾。 +> 參數語意:`` 的 `2` 只在舊頁還是表格時用得到,代表轉檔時取第 2 欄格子的文字當 H2 標題,格子是 `[文字](網址)` 就只取文字;頁面已經是條列格式就完全忽略它。`` 是 H2 標題文字,也就是內容頁頁名 `LEARN_{HASH}`。第四個參數是區塊檔,內容是 `## {key}` 那一行加空行加各條欄位,不是一列表格。 +> 鍵是 H2 標題的頁名,不是連結。頁名只由 `{owner}/{repo}` 決定,換主機名、改存取庫、頁名編碼有差都動不到它;連結帶著主機名與存取庫名,一變就比不到鍵,同一個存取庫會多出第二個區塊。 +> 禁止整頁覆蓋,也不得改動別人的區塊。 -| 存取庫名稱 | 教訓紀錄 | 最後更新時間 | -| --- | --- | --- | -| {owner}/{repo} | [LEARN_{HASH}]({教訓頁絕對網址}) | {yyyy-MM-dd HH:mm} | +## LEARN_{HASH} + +- 存取庫名稱:{owner}/{repo} +- 教訓紀錄:[LEARN_{HASH}]({教訓頁絕對網址}) +- 最後更新時間:{yyyy-MM-dd HH:mm} From 369b327a4f7b1490241bc80f87774e4045518c7c Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:22:33 +0800 Subject: [PATCH 3/5] =?UTF-8?q?feat(report):=20=E5=A0=B1=E8=A1=A8=E7=9B=AE?= =?UTF-8?q?=E9=8C=84=E9=A0=81=E6=94=B9=E6=88=90=E4=B8=80=E5=80=8B=E5=A0=B1?= =?UTF-8?q?=E8=A1=A8=E9=A0=81=E4=B8=80=E5=80=8B=E5=A4=A7=E6=A8=99=E9=A1=8C?= =?UTF-8?q?=E5=8D=80=E5=A1=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 報表目錄頁的版面從 markdown 表格換成「大標題加條列」:一個報表頁一個大標題 區塊,也就是一個存取庫的一種期間一個區塊,標題就是那個報表頁的實際頁名, 期間、最新一期、期數這些欄位改成標題底下的一層條列。範本與 report 技能的 寫入敘述與退出碼說明一起跟上。 表格的欄位組合是整頁共用的,一頁上卻有很多報表頁各自的紀錄,每一輪只重寫 自己那一列。欄位一增減,舊列的格數與表頭就對不上,而目錄頁不能整頁覆蓋—— 覆蓋等於刪掉別人的紀錄。條列一筆一個區塊,欄位各自獨立,加一條只動到自己 那一個區塊。 比對鍵從裸雜湊那一格改成大標題本身,標題寫成報表頁的實際頁名。頁名只由 存取庫的擁有者、名稱與期間決定,換主機位址或頁名編碼有差都動不到它;含 網址的那一條連結照樣留著給人點,但不當鍵。把報表存取庫改指別的庫是另一回 事:雜湊本身就取自那個庫,換庫等於換頁,四種期間會各多一個區塊,那是換庫 的本意,不是鍵失準,舊區塊請人工清掉。 範圍是工作報表的目錄頁與 report 技能。 --- skills/report/SKILL.md | 28 +++++++++++++++------------- templates/report-contents.md | 24 +++++++++++++++--------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/skills/report/SKILL.md b/skills/report/SKILL.md index 06f7bce..d3f2d1c 100644 --- a/skills/report/SKILL.md +++ b/skills/report/SKILL.md @@ -1,6 +1,6 @@ --- name: report -description: Summarise work logs into a yearly, monthly, weekly or daily report. Resolve the period with tools/report-range.sh, resolve the template with tools/report-template.sh - a project's .jsc/templates/report-{period}.md wins over the skill's own copy - then read every log page listed in LOG_CONTENTS and keep the entries dated inside the range. Fill the template with real aggregates (entry count, repositories, elapsed time, token usage, blockers, carry-overs) and write it to wiki REPORT_{HASH}, whose full 40-character uppercase hash comes from the REPORT wiki repo's own {owner}/{repo} plus the period rather than from a code repo, appending the period as a new section. Directory pages LOG_CONTENTS, LEARN_CONTENTS and REPORT_CONTENTS all sit in the shared CONTENTS wiki repo while every content page stays in its own type's repo, so the REPORT_CONTENTS row goes through jsc-gitea/tools/wiki-contents.sh upsert and links the report page by its absolute wiki-url. Use when someone asks for a work summary over a period; not for recording a single work package, which is jsc-log:worklog. +description: Summarise work logs into a yearly, monthly, weekly or daily report. Resolve the period with tools/report-range.sh, resolve the template with tools/report-template.sh - a project's .jsc/templates/report-{period}.md wins over the skill's own copy - then read every log page listed in LOG_CONTENTS and keep the entries dated inside the range. Fill the template with real aggregates (entry count, repositories, elapsed time, token usage, blockers, carry-overs) and write it to wiki REPORT_{HASH}, whose full 40-character uppercase hash comes from the REPORT wiki repo's own {owner}/{repo} plus the period rather than from a code repo, appending the period as a new section. Directory pages LOG_CONTENTS, LEARN_CONTENTS and REPORT_CONTENTS all sit in the shared CONTENTS wiki repo while every content page stays in its own type's repo, so the REPORT_CONTENTS entry goes through jsc-gitea/tools/wiki-contents.sh upsert as one H2 block keyed by the page name REPORT_{HASH}, with a bullet per field and the report page linked by its absolute wiki-url. Use when someone asks for a work summary over a period; not for recording a single work package, which is jsc-log:worklog. --- # report — summarise work logs by period @@ -25,11 +25,13 @@ Done when start, end and label are known. Directory pages and content pages no longer share a wiki. Every `*_CONTENTS` page — `LOG_CONTENTS`, `LEARN_CONTENTS`, `REPORT_CONTENTS` — lives in the one repo that `jsc-gitea/tools/gitea.sh wiki-repo CONTENTS` resolves (`JSC_WIKI_REPO_CONTENTS`, then `JSC_WIKI_REPO`, then exit 3; it never falls back to a page type's own variable). Each content page still lives in its own type's repo: log pages in `wiki-repo LOG`, lesson pages in `wiki-repo LEARN`, the report page in `wiki-repo REPORT`. Keep the two apart — one shared directory repo, one repo per content type — and resolve every one of them on its own. +Every directory page is a list page, not a table: an H1, a `>` preamble, then one H2 block per entry whose heading is that entry's content page name, with `- {欄位名}:{值}` bullets under it. Read the links out of the bullets. + Run these four lines of work in parallel — none of them consumes another's output, and the log pages are the slow one: 1. **Template.** `tools/report-template.sh resolve {period}` from the working directory prints `{path}{project|skill}`. -2. **Log pages.** `gitea.sh wiki-repo CONTENTS`, then read `LOG_CONTENTS` through `jsc-gitea:wiki`, then read **every** log page it lists, one sub agent per page. The rows link their pages by absolute URL, so follow each link as given; `gitea.sh wiki-repo LOG` names the repo the log pages of this working directory sit in, and a row pointing elsewhere is another repo's log page, not a broken link. -3. **Lessons (yearly only).** Read `LEARN_CONTENTS` from the same CONTENTS repo, then read the lesson pages it lists for the 全年教訓 section. Resolve the lesson pages' own repo with `gitea.sh wiki-repo LEARN`, never with the LOG repo of line 2: the two directory pages now share a repo, but LOG and LEARN **content** pages routinely live in different ones, and reusing the LOG repo reads the wrong wiki. Other periods skip this line. +2. **Log pages.** `gitea.sh wiki-repo CONTENTS`, then read `LOG_CONTENTS` through `jsc-gitea:wiki`, then read **every** log page it lists, one sub agent per page. The page holds one H2 block per log page, each with a 日誌頁 bullet carrying an absolute URL, so follow each link as given rather than the H2 heading; `gitea.sh wiki-repo LOG` names the repo the log pages of this working directory sit in, and a block pointing elsewhere is another repo's log page, not a broken link. +3. **Lessons (yearly only).** Read `LEARN_CONTENTS` from the same CONTENTS repo, then read the lesson pages its blocks link for the 全年教訓 section. Resolve the lesson pages' own repo with `gitea.sh wiki-repo LEARN`, never with the LOG repo of line 2: the two directory pages now share a repo, but LOG and LEARN **content** pages routinely live in different ones, and reusing the LOG repo reads the wrong wiki. Other periods skip this line. 4. **Report repo.** `gitea.sh wiki-repo REPORT`, so step 3 has its target ready. Exit branches for the external calls above: @@ -74,22 +76,22 @@ Write through `jsc-gitea:wiki`: - Repo: the REPORT repo from step 2, line 4. It hosts the content page only; `REPORT_CONTENTS` goes to the CONTENTS repo instead. - Page: `REPORT_` plus `gitea.sh hash-id "{owner}/{repo}/{period}"`. Here `{owner}/{repo}` is **the REPORT wiki repo itself** — the value `gitea.sh wiki-repo REPORT` printed — and not the code repo the logs came from. Every other page in this skill set hashes the code repo; this one page does not, because a report spans every code repo whose logs landed in the range, so no single code repo names it. Feed `hash-id` the exact string `{REPORT wiki owner}/{REPORT wiki repo}/{period}`, with `{period}` being the literal `daily`, `weekly`, `monthly` or `yearly` — so year, month, week and day each get their own page. `hash-id` prints the full 40-character uppercase SHA-1: use it whole, never shortened and never prefixed. - Read the page first and branch on the exit code the underlying `gitea.sh wiki-get` returned. **Only exit 4 means the page is not there yet** and may be built from scratch. On exit 0 the existing sections are in hand, so append into them. On exit 7 the token is invalid or lacks permission, and on exit 8 the API failed some other way: both leave the earlier periods unknown, so stop, report the status and write nothing — a page rebuilt on top of an unread read loses every period already on it. -- Check every link before it goes on a page — the ones inside the new section and the row's link alike: `jsc-gitea/tools/link-check.sh {url}...`. It prints one `{OK|DEAD|SKIP}{url}{note}` line per URL and resolves Gitea URLs through the API, because a private repo answers a logged-out web request with 404 and would fail a page that is there. Only exit 0 permits the write. +- Check every link before it goes on a page — the ones inside the new section and the directory block's link alike: `jsc-gitea/tools/link-check.sh {url}...`. It prints one `{OK|DEAD|SKIP}{url}{note}` line per URL and resolves Gitea URLs through the API, because a private repo answers a logged-out web request with 404 and would fail a page that is there. Only exit 0 permits the write. | Exit | Do | | --- | --- | - | 0 | Every link answered. Write the section, or the row | + | 0 | Every link answered. Write the section, or the directory block | | 1 | At least one link is DEAD. Write nothing and report the DEAD lines to the caller | | 2 | No URL reached the script. Pass the URLs and rerun | | 3 | The list holds a Gitea URL but `GITEA_HOST` is unset. Set it and rerun; never skip the check | | 7 | The Gitea token was rejected (HTTP 401/403). Stop and report the token problem. A rejected token makes live pages look missing, and one batch judged on that answer wipes out links that still work | - Append this period as a new section, newest first. Rerunning the same period replaces that period's section only, leaving the other periods untouched. -- Refresh the page's row in `REPORT_CONTENTS` with `jsc-gitea/tools/wiki-contents.sh` — never hand-edit the directory page. It sits in the CONTENTS repo, not the REPORT repo, so the row links the report page as `[REPORT_{HASH}]()`, with `` from `gitea.sh wiki-url REPORT_{HASH}`. Every link on the report body and on this row takes that same `[{text}]({absolute URL})` shape; the same-wiki `[[...]]` form resolves inside one wiki only and dead-links from here without reporting an error. Build one file holding the single row from `templates/report-contents.md` (the absolute link, the bare `{HASH}`, the period, the newest label, the section count and the update time), then run: +- Refresh the page's block in `REPORT_CONTENTS` with `jsc-gitea/tools/wiki-contents.sh` — never hand-edit the directory page. It sits in the CONTENTS repo, not the REPORT repo, so the 報表頁 bullet links the report page as `[REPORT_{HASH}]()`, with `` from `gitea.sh wiki-url REPORT_{HASH}`. Every link on the report body and in this block takes that same `[{text}]({absolute URL})` shape; the same-wiki `[[...]]` form resolves inside one wiki only and dead-links from here without reporting an error. Build one file holding the single H2 block from `templates/report-contents.md` — the `## REPORT_{HASH}` heading, a blank line, then one `- {欄位名}:{值}` bullet per field in the template's order: the absolute link, the bare `{HASH}`, the period, the newest label, the section count and the update time. Then run: - `jsc-gitea/tools/wiki-contents.sh upsert REPORT 2 "{HASH}" {row file} templates/report-contents.md` + `jsc-gitea/tools/wiki-contents.sh upsert REPORT 1 "REPORT_{HASH}" {block file} templates/report-contents.md` - The key is column 2, the bare 40-character `{HASH}` this step already computed, with no link markup around it. Column 1 carries the same page as a link for a human to click, and that link is exactly what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST` or one difference in how Gitea encodes the page name makes this run's cell differ from the last run's, the match fails, the row is appended, and the same report page now owns two rows of which the older is never updated again. The script replaces the matching row and appends when none matches, so every row that belongs to another report page stays as it was. + The key is the H2 heading itself, the content page name `REPORT_{HASH}` this step already computed, and the 報表頁 bullet carries that same page as a link for a human to click. That link is exactly what must not be the key: it embeds the host and the encoded page name, so one change of `GITEA_HOST` or one difference in how Gitea encodes the page name makes this run's text differ from the last run's, the match fails, the block is appended, and the same report page now owns two blocks of which the older is never updated again. The page name depends only on the hashed `{owner}/{repo}/{period}`, so neither of those two touches it. The `1` is ``, which the script uses only while the directory page is still an old markdown table — it names the column whose cell text (the link text alone) becomes the H2 heading, and a page already in list shape ignores it. The script replaces the matching block and appends when none matches, so every block that belongs to another report page stays as it was. | Call | Exit | Do | | --- | --- | --- | @@ -99,15 +101,15 @@ Write through `jsc-gitea:wiki`: | `gitea.sh wiki-url` | 4 / 5 | 4 means the report page write has not landed, so write it first; 5 means the page carries no `html_url`, so stop and report it and never assemble the URL by hand | | `gitea.sh wiki-url` | 7 / 8 | 7 means the token is invalid or lacks permission (HTTP 401/403), 8 means some other API failure. Both leave it unknown whether the page is there, so stop and report the token or API status. Never fold either into 4: reading an invalid key as a missing page is the same misread this table separates 7 from 4 to prevent, and here it would send the run back to rewrite a report page that is already on the server | | `jsc-gitea:wiki` write | failure | Retry once. Still failing, stop and report the page name that was not written, and print the report body so the work is not lost. Never report a page as written when it was not | -| `wiki-contents.sh upsert` | 0 | The row is in place. It prints `updated` or `added` plus the page it wrote | -| `wiki-contents.sh upsert` | 1 | The write failed, or the directory page holds no markdown table. Report `REPORT_CONTENTS` as not written, together with the row content | +| `wiki-contents.sh upsert` | 0 | The block is in place. It prints `updated` or `added` plus the page it wrote | +| `wiki-contents.sh upsert` | 1 | The page content could not be assembled, or the write failed. Report `REPORT_CONTENTS` as not written, together with the block content. A page with no matching block is not this code: the block is appended instead | | `wiki-contents.sh upsert` | 2 | An argument was rejected. Fix the argument and rerun this bullet; nothing was written | | `wiki-contents.sh upsert` | 3 | No CONTENTS wiki repo is configured. Report `JSC_WIKI_REPO_CONTENTS` and `JSC_WIKI_REPO` as the two variables to set. The report itself is on `REPORT_{HASH}` and stays there | | `wiki-contents.sh upsert` | 4 | The directory page is absent and the script received no template. The call above always passes one, so this code means `templates/report-contents.md` is not at that path — a partial plugin install, not a missing argument. Stop and report the path; rerunning the same command changes nothing. Reinstall the plugin, confirm the file is there, then rerun. A mistyped template path exits 2, not 4 | -| `wiki-contents.sh upsert` | 7 | The token is invalid or lacks permission, so the other rows are unknown. Stop and report the token problem; the script wrote nothing, which is what keeps those rows alive | +| `wiki-contents.sh upsert` | 7 | The token is invalid or lacks permission, so the other blocks are unknown. Stop and report the token problem; the script wrote nothing, which is what keeps those blocks alive | | `wiki-contents.sh upsert` | 8 | Some other API failure. Stop and report that status and retry only after the API is back | -Write the content page before its row in `REPORT_CONTENTS`, never the two at once: a directory row pointing at a page whose write failed is worse than a missing row, and `wiki-url` cannot name a page that is not there yet. +Write the content page before its block in `REPORT_CONTENTS`, never the two at once: a directory block pointing at a page whose write failed is worse than a missing block, and `wiki-url` cannot name a page that is not there yet. Done when the page URL is reported, or the skipped write is reported with its reason, or the run stopped on a read that returned 7 or 8 and that status was reported. @@ -125,7 +127,7 @@ Resolve that path the way this file already resolves `jsc-gitea/tools/link-check | 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 | +| `ok` | The period's section is on `REPORT_{HASH}` and the `REPORT_CONTENTS` block 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 | diff --git a/templates/report-contents.md b/templates/report-contents.md index aeb2018..01e96f3 100644 --- a/templates/report-contents.md +++ b/templates/report-contents.md @@ -1,14 +1,20 @@ # 報表目錄 -> 由 `jsc-log:report` 維護。年、月、週、日各一頁;`HASH` 取 `{owner}/{repo}/{期間}` 的完整 40 碼大寫十六進位 SHA-1,算法與其他頁面共用,但這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫。 +> 由 `jsc-log:report` 維護。年、月、週、日各一個區塊;`HASH` 取 `{owner}/{repo}/{期間}` 的完整 40 碼大寫十六進位 SHA-1,算法與其他頁面共用,但這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫。 > 本頁落在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,報表頁 `REPORT_{HASH}` 落在 `JSC_WIKI_REPO_REPORT` 的存取庫,兩者分屬不同 wiki。 -> 連結寫法:報表頁那一欄寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 +> 連結寫法:報表頁那一條寫成 `[{頁名}]({絕對網址})`,網址取自 `jsc-gitea/tools/gitea.sh wiki-url`,不用 `[[...]]`。兩頁分屬不同存取庫,`[[...]]` 連不過去,畫面上還看不出壞掉。 > 連結驗證:每個要寫進本頁的連結先交給 `jsc-gitea/tools/link-check.sh`,退出碼 0 才寫入。出現 DEAD 就不寫,把連不到的那幾筆回報給呼叫端。 -> 寫入語意:一列代表一個報表頁,也就是一個存取庫的一種期間。一律用 `jsc-gitea/tools/wiki-contents.sh upsert REPORT 2 {HASH} {列檔} {本範本}` 單列整頁寫回——它讀整頁、找得到該報表頁既有的那一列就換掉那一列,找不到才附加。 -> 鍵取第 2 欄的裸 HASH,不取第 1 欄的連結。第 1 欄的連結帶著主機名與頁名編碼,`GITEA_HOST` 一換或 Gitea 對頁名的編碼有差,連結就跟上一輪寫的不一樣,鍵比不到就走附加,同一個報表頁多出第二列,舊列從此不再更新。裸 HASH 不受這兩件事影響。 -> 但 `JSC_WIKI_REPO_REPORT` 改指別的存取庫是另一回事:HASH 本身就取自 REPORT wiki 存取庫,換庫等於換頁,四個期間會各多一列。那是換庫的本意,不是鍵失準,舊列請人工清掉。 -> 禁止整頁覆蓋,也不得改動別人的列。 +> 寫入語意:一個區塊代表一個報表頁,也就是一個存取庫的一種期間。一律用 `jsc-gitea/tools/wiki-contents.sh upsert REPORT 1 REPORT_{HASH} {區塊檔} {本範本}` 單一區塊整頁寫回——它讀整頁、找得到該報表頁既有的那個區塊就換掉,找不到才附加到頁尾。 +> 參數語意:`` 的 `1` 只在舊頁還是表格時用得到,代表轉檔時取第 1 欄格子的文字當 H2 標題,格子是 `[文字](網址)` 就只取文字;頁面已經是條列格式就完全忽略它。`` 是 H2 標題文字,也就是內容頁頁名 `REPORT_{HASH}`。第四個參數是區塊檔,內容是 `## {key}` 那一行加空行加各條欄位,不是一列表格。 +> 鍵是 H2 標題的頁名,不是連結。頁名只由 `{owner}/{repo}/{期間}` 決定,`GITEA_HOST` 一換或 Gitea 對頁名的編碼有差都動不到它;連結帶著主機名與頁名編碼,一變就跟上一輪寫的不一樣,拿它當鍵就比不到既有那一筆,走附加,同一個報表頁多出第二個區塊,舊區塊從此不再更新。 +> 但 `JSC_WIKI_REPO_REPORT` 改指別的存取庫是另一回事:`HASH` 本身就取自 REPORT wiki 存取庫,換庫等於換頁,四個期間會各多一個區塊。那是換庫的本意,不是鍵失準,舊區塊請人工清掉。 +> 禁止整頁覆蓋,也不得改動別人的區塊。 -| 報表頁 | HASH | 期間 | 最新一期 | 期數 | 最後更新 | -| --- | --- | --- | --- | --- | --- | -| [REPORT_{HASH}]({報表頁絕對網址}) | {HASH} | {daily、weekly、monthly、yearly 四選一} | {最新一期的標籤} | {n} | {yyyy-MM-dd HH:mm} | +## REPORT_{HASH} + +- 報表頁:[REPORT_{HASH}]({報表頁絕對網址}) +- HASH:{HASH} +- 期間:{daily、weekly、monthly、yearly 四選一} +- 最新一期:{最新一期的標籤} +- 期數:{n} +- 最後更新:{yyyy-MM-dd HH:mm} From 18d12bbbe23302ff905da03bb94ab031d5c084cf Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:22:33 +0800 Subject: [PATCH 4/5] =?UTF-8?q?docs(behaviors):=20=E8=AA=AA=E6=98=8E?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E8=88=87=E8=A1=8C=E7=82=BA=E6=B8=85=E5=96=AE?= =?UTF-8?q?=E8=B7=9F=E4=B8=8A=E7=9B=AE=E9=8C=84=E9=A0=81=E7=9A=84=E6=A2=9D?= =?UTF-8?q?=E5=88=97=E7=89=88=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 存取庫說明與技能行為清單裡講到目錄頁的段落一律改口:一筆一個大標題區塊、 標題就是內容頁頁名也就是鍵、欄位是標題底下的一層條列,頁上不留表格。 三個目錄頁的範本與三支技能的敘述都換了版面,說明文件卻還寫著「一列」與 「表格」。文件與範本各說一套,照文件手工補紀錄的人會補出一列表格,那一列 在條列頁上讀不成一筆紀錄,而且下一輪工具重寫時也不會被當成既有那一筆換掉。 只改敘述,行為與呼叫參數都不動;欄位語意與寫入語意的正本仍在各自的範本 引言裡,這裡只指向它,不重述細節。 範圍是說明文件與技能行為清單裡的目錄頁敘述。 --- README.md | 8 ++++---- references/behaviors.md | 24 ++++++++++++------------ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 3454573..1c9d7d1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # jsc-log — 工作日誌與統計 -jsc 技能組的 log domain:工作完成後把十項資訊寫入 wiki 日誌頁(`LOG_{HASH}`,`HASH` 依共享規則計算,取 `{owner}/{repo}` 的完整 SHA-1 四十碼、a-f 轉大寫,不截短也不加前綴;頁內仍按工作週週五整理),統計技能使用次數與呼叫鏈次數,並把技能執行的教訓記到 `LEARN_{HASH}`,供下次執行前查閱。目錄頁(`LOG_CONTENTS`、`LEARN_CONTENTS`、`REPORT_CONTENTS`)另住一個專用存取庫,與內容頁分開。 +jsc 技能組的 log domain:工作完成後把十項資訊寫入 wiki 日誌頁(`LOG_{HASH}`,`HASH` 依共享規則計算,取 `{owner}/{repo}` 的完整 SHA-1 四十碼、a-f 轉大寫,不截短也不加前綴;頁內仍按工作週週五整理),統計技能使用次數與呼叫鏈次數,並把技能執行的教訓記到 `LEARN_{HASH}`,供下次執行前查閱。目錄頁(`LOG_CONTENTS`、`LEARN_CONTENTS`、`REPORT_CONTENTS`)另住一個專用存取庫,與內容頁分開。目錄頁的版面是「H1 加 `>` 引言,再一筆一個 H2 區塊」:H2 標題就是那一筆的內容頁頁名,也就是鍵,欄位寫成標題底下的 `- {欄位名}:{值}` 條列,頁上不留 markdown 表格。 ## 安裝、更新、移除 @@ -40,7 +40,7 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 每完成一個任務就寫一筆日誌。任務有三種:一個工作包、一輪 PR 留言修正、一個獨立的修正提交。下一個任務開始前先把這一筆寫完,同一個工作包跑五輪留言修正就是五筆,各自帶自己的花費時間與 token 用量,附加到同一頁 `LOG_{HASH}`——連「試了卻沒改到檔案」的那一輪也留下來,那段時間才看得見。 -每筆蒐集十項資訊(存取庫、分支、計畫連結、工作包連結、花費時間、token 用量、任務狀態、執行細節、困難與解決、PR 目標分支)。計畫連結、工作包連結、花費時間、token 用量四項來源互不相依,併行取得;存取庫解析與 `HASH` 計算也併行。用 `tools/worklog-target.sh` 產生目標頁,工作週的週五由同一支的 `friday` 子命令算出,套範本後附加到 `LOG_{HASH}`。目錄頁 `LOG_CONTENTS` 住在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,與 `LOG_{HASH}` 不同 wiki,改由 `jsc-gitea/tools/wiki-contents.sh upsert LOG 2 {HASH}` 單列寫回:鍵取第 2 欄的裸 HASH,第 1 欄的絕對網址(`gitea.sh wiki-url` 給的)只給人點。網址帶主機名,換主機就比不到鍵,同一頁會多一列。寫入前跑 `tools/worklog-pending.sh merge {HASH} {本次條目檔}`:之前有階段跑完沒寫日誌,內容暫存在那裡,這次一併寫進去;wiki 寫入成功才 `commit` 清掉暫存,失敗就 `abort` 保留。 +每筆蒐集十項資訊(存取庫、分支、計畫連結、工作包連結、花費時間、token 用量、任務狀態、執行細節、困難與解決、PR 目標分支)。計畫連結、工作包連結、花費時間、token 用量四項來源互不相依,併行取得;存取庫解析與 `HASH` 計算也併行。用 `tools/worklog-target.sh` 產生目標頁,工作週的週五由同一支的 `friday` 子命令算出,套範本後附加到 `LOG_{HASH}`。目錄頁 `LOG_CONTENTS` 住在 `JSC_WIKI_REPO_CONTENTS` 解出的專用存取庫,與 `LOG_{HASH}` 不同 wiki,改由 `jsc-gitea/tools/wiki-contents.sh upsert LOG 1 LOG_{HASH}` 單一區塊寫回:鍵是 H2 標題的頁名 `LOG_{HASH}`,「日誌頁」那一條的絕對網址(`gitea.sh wiki-url` 給的)只給人點。網址帶主機名,拿它當鍵換主機就比不到,同一頁會多一個區塊;頁名只由 `{owner}/{repo}` 決定,不受影響。命令裡的 `1` 是 ``,只在舊頁還是表格時用來認出哪一欄的文字當 H2 標題。寫入前跑 `tools/worklog-pending.sh merge {HASH} {本次條目檔}`:之前有階段跑完沒寫日誌,內容暫存在那裡,這次一併寫進去;wiki 寫入成功才 `commit` 清掉暫存,失敗就 `abort` 保留。 ### `stats` @@ -48,11 +48,11 @@ Marketplace 統一為 `jsc`(https://gitea.jsc.idv.tw/plugins/meta.git),安 ### `learn` -技能執行後把教訓(日期、技能、CLI、情境、教訓、下次做法)附加到 `LEARN_{HASH}`,並用 `jsc-gitea/tools/wiki-contents.sh upsert` 更新 `LEARN_CONTENTS`;技能執行前查閱同兩頁,套用相符的「下次做法」。兩頁分屬不同存取庫:`LEARN_{HASH}` 取 `JSC_WIKI_REPO_LEARN`,`LEARN_CONTENTS` 取 `JSC_WIKI_REPO_CONTENTS`,列裡的連結用絕對網址。 +技能執行後把教訓(日期、技能、CLI、情境、教訓、下次做法)附加到 `LEARN_{HASH}`,並用 `jsc-gitea/tools/wiki-contents.sh upsert LEARN 2 LEARN_{HASH}` 更新 `LEARN_CONTENTS` 上該存取庫那個區塊;技能執行前查閱同兩頁,套用相符的「下次做法」。兩頁分屬不同存取庫:`LEARN_{HASH}` 取 `JSC_WIKI_REPO_LEARN`,`LEARN_CONTENTS` 取 `JSC_WIKI_REPO_CONTENTS`,區塊裡的連結用絕對網址。 ### `report` -把工作日誌總結成年報、月報、週報或日報。期間由 `tools/report-range.sh` 算出(週次採 ISO-8601),範本由 `tools/report-template.sh` 解析——工作目錄的 `.jsc/templates/report-{period}.md` 優先,沒有才用技能自帶的那份。範本解析、日誌頁讀取、教訓頁讀取三線併行,各頁也一頁一個 sub agent 同時讀。目錄頁 `LOG_CONTENTS` 取 `JSC_WIKI_REPO_CONTENTS` 的專用存取庫,它列出的日誌頁用絕對網址逐頁讀回;讀完交給 `tools/log-aggregate.sh` 算出條目數、涵蓋存取庫、花費時間、各 CLI token 用量與狀態計數,填進範本後寫入 wiki `REPORT_{HASH}`(`HASH` 取 `{owner}/{repo}/{期間}` 的完整 40 碼,這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫——本頁其他 `HASH` 取的是程式碼存取庫,只有這一處不同),同一期間重跑只換掉那一節。`REPORT_CONTENTS` 那一列改由 `jsc-gitea/tools/wiki-contents.sh upsert REPORT 2 {HASH}` 寫回,鍵同樣取裸 HASH 那一欄。年報的教訓「內容頁」另解 `JSC_WIKI_REPO_LEARN`,不沿用日誌頁的存取庫;目錄頁同住 CONTENTS 存取庫是另一回事,兩者別混。單筆工作紀錄請用 `worklog`。 +把工作日誌總結成年報、月報、週報或日報。期間由 `tools/report-range.sh` 算出(週次採 ISO-8601),範本由 `tools/report-template.sh` 解析——工作目錄的 `.jsc/templates/report-{period}.md` 優先,沒有才用技能自帶的那份。範本解析、日誌頁讀取、教訓頁讀取三線併行,各頁也一頁一個 sub agent 同時讀。目錄頁 `LOG_CONTENTS` 取 `JSC_WIKI_REPO_CONTENTS` 的專用存取庫,它列出的日誌頁用絕對網址逐頁讀回;讀完交給 `tools/log-aggregate.sh` 算出條目數、涵蓋存取庫、花費時間、各 CLI token 用量與狀態計數,填進範本後寫入 wiki `REPORT_{HASH}`(`HASH` 取 `{owner}/{repo}/{期間}` 的完整 40 碼,這裡的 `{owner}/{repo}` 取 REPORT wiki 存取庫,不是程式碼存取庫——本頁其他 `HASH` 取的是程式碼存取庫,只有這一處不同),同一期間重跑只換掉那一節。`REPORT_CONTENTS` 那個區塊改由 `jsc-gitea/tools/wiki-contents.sh upsert REPORT 1 REPORT_{HASH}` 寫回,鍵同樣是 H2 標題的頁名。年報的教訓「內容頁」另解 `JSC_WIKI_REPO_LEARN`,不沿用日誌頁的存取庫;目錄頁同住 CONTENTS 存取庫是另一回事,兩者別混。單筆工作紀錄請用 `worklog`。 diff --git a/references/behaviors.md b/references/behaviors.md index cbcc323..e2d20cd 100644 --- a/references/behaviors.md +++ b/references/behaviors.md @@ -7,20 +7,20 @@ | 項目 | 內容 | | --- | --- | | 觸發時機 | 一次技能執行留下可重用的教訓時,用 record 模式記錄。要跑某支技能之前,用 consult 模式查過去的教訓。工時與 Token 紀錄不走這支,走 worklog | -| 關鍵步驟 | record 模式收齊日期、技能、CLI、情境、教訓、下次做法這六欄、從 `git remote get-url origin` 解出 `{owner}/{repo}`、用 `hash-id` 算出完整 40 碼大寫的 `{HASH}`、用 `wiki-repo LEARN` 解出教訓頁存取庫、開 sub agent 讀 `LEARN_{HASH}`、依 `wiki-get` 的退出碼分支(0 在表尾追加一列、4 才用 `templates/learn-page.md` 建頁、7 與 8 停止並回報)、取 `wiki-url` 的絕對網址並寫成 `[{頁名}]({絕對網址})`、把該網址交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、同一輪跑 `wiki-contents.sh upsert LEARN 1 {owner}/{repo}` 更新落在 CONTENTS 存取庫的 `LEARN_CONTENTS`、依它的退出碼分流(0 已寫、1 寫入失敗、2 參數錯、3 未設 `JSC_WIKI_REPO_CONTENTS`、4 缺範本、7 金鑰失效、8 其他 API 失敗)、寫入失敗重試一次;consult 模式算出 `{HASH}`、從 CONTENTS 存取庫讀 `LEARN_CONTENTS`、從 LEARN 存取庫讀 `LEARN_{HASH}`、依列上的絕對網址讀頁、挑出「技能」欄相符的列、整理每一列的「下次做法」交給呼叫端;兩種模式都以 `jsc-hooks/tools/report-status.sh skill-end jsc-log:learn {status} {結束碼} {detail}` 收尾,腳本不在這台機器上就安靜跳過 | -| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh wiki-repo LEARN` 與 `wiki-repo CONTENTS` 與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那一列)、`jsc-gitea:wiki`(其餘 wiki 讀寫)、`git remote get-url origin`、`templates/learn-page.md`、`templates/learn-contents.md` | -| 完成條件 | record 模式要 sub agent 回報兩頁都寫入成功,主代理確認新列在 `LEARN_{HASH}` 上,原有的列一列不少,寫入前 `link-check.sh` 回 0,`wiki-contents.sh` 回 0 並印出 `updated` 或 `added`。`link-check.sh` 回 1 就不寫目錄頁,改回報 DEAD 清單。consult 模式要兩頁都讀到,或以退出碼 4 回報頁面不存在,或在 5、7、8 停止並回報狀態。收尾一定要寫一筆 `skill-end` 狀態事件:兩頁都成功是 `ok`,內容頁寫成功但目錄頁沒更新是 `degraded`,連結驗證回 1 或讀寫回 7、8 是 `failed`,雜湊工具缺席或 wiki 存取庫沒設定是 `blocked`,使用者中止或根本沒有可記的教訓是 `aborted` | -| 可驗證跡象 | LEARN 存取庫的 `LEARN_{HASH}` 表尾多一列教訓;CONTENTS 存取庫的 `LEARN_CONTENTS` 上該 repo 那一列的「最後更新時間」換新,且「教訓紀錄」欄是 `[{頁名}]({絕對網址})`,頁面上沒有 `[[...]]` 這種同 wiki 寫法。頁面原本不存在時,會新建 `LEARN_{HASH}` 或 `LEARN_CONTENTS`。連結驗證不過就兩頁都沒有新內容,只有 DEAD 清單的回報。consult 模式在 wiki 上無寫入跡象,只有回報內容。兩種模式跑完,`$JSC_HOME/usage/events.jsonl` 都會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:learn`,`status` 欄是那五個值之一;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 | +| 關鍵步驟 | record 模式收齊日期、技能、CLI、情境、教訓、下次做法這六欄、從 `git remote get-url origin` 解出 `{owner}/{repo}`、用 `hash-id` 算出完整 40 碼大寫的 `{HASH}`、用 `wiki-repo LEARN` 解出教訓頁存取庫、開 sub agent 讀 `LEARN_{HASH}`、依 `wiki-get` 的退出碼分支(0 在表尾追加一列、4 才用 `templates/learn-page.md` 建頁、7 與 8 停止並回報)、取 `wiki-url` 的絕對網址並寫成 `[{頁名}]({絕對網址})`、把該網址交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、照 `templates/learn-contents.md` 組出一個 H2 區塊檔(`## LEARN_{HASH}` 加空行加各條 `- {欄位名}:{值}`)、同一輪跑 `wiki-contents.sh upsert LEARN 2 LEARN_{HASH}` 更新落在 CONTENTS 存取庫的 `LEARN_CONTENTS`(鍵是 H2 標題的頁名,不是那條帶主機名的連結;命令裡的 `2` 是 ``,只在舊頁還是表格時用來認出哪一欄的文字當標題)、依它的退出碼分流(0 已寫、1 組不出內容或寫入失敗、2 參數錯、3 未設 `JSC_WIKI_REPO_CONTENTS`、4 缺範本、7 金鑰失效、8 其他 API 失敗)、寫入失敗重試一次;consult 模式算出 `{HASH}`、從 CONTENTS 存取庫讀 `LEARN_CONTENTS`、從 LEARN 存取庫讀 `LEARN_{HASH}`、依區塊裡「教訓紀錄」那一條的絕對網址讀頁、挑出「技能」欄相符的列、整理每一列的「下次做法」交給呼叫端;兩種模式都以 `jsc-hooks/tools/report-status.sh skill-end jsc-log:learn {status} {結束碼} {detail}` 收尾,腳本不在這台機器上就安靜跳過 | +| 外部呼叫 | `jsc-gitea/tools/hash-id`、`jsc-gitea/tools/gitea.sh wiki-repo LEARN` 與 `wiki-repo CONTENTS` 與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那個區塊)、`jsc-gitea:wiki`(其餘 wiki 讀寫)、`git remote get-url origin`、`templates/learn-page.md`、`templates/learn-contents.md` | +| 完成條件 | record 模式要 sub agent 回報兩頁都寫入成功,主代理確認新列在 `LEARN_{HASH}` 上、該存取庫那個區塊在 `LEARN_CONTENTS` 上,原有的列與區塊一個不少,寫入前 `link-check.sh` 回 0,`wiki-contents.sh` 回 0 並印出 `updated` 或 `added`。`link-check.sh` 回 1 就不寫目錄頁,改回報 DEAD 清單。consult 模式要兩頁都讀到,或以退出碼 4 回報頁面不存在,或在 5、7、8 停止並回報狀態。收尾一定要寫一筆 `skill-end` 狀態事件:兩頁都成功是 `ok`,內容頁寫成功但目錄頁沒更新是 `degraded`,連結驗證回 1 或讀寫回 7、8 是 `failed`,雜湊工具缺席或 wiki 存取庫沒設定是 `blocked`,使用者中止或根本沒有可記的教訓是 `aborted` | +| 可驗證跡象 | LEARN 存取庫的 `LEARN_{HASH}` 表尾多一列教訓;CONTENTS 存取庫的 `LEARN_CONTENTS` 上標題為 `LEARN_{HASH}` 的那個區塊,「最後更新時間」那一條換新,「教訓紀錄」那一條是 `[{頁名}]({絕對網址})`,頁面上沒有 markdown 表格,也沒有 `[[...]]` 這種同 wiki 寫法。頁面原本不存在時,會新建 `LEARN_{HASH}` 或 `LEARN_CONTENTS`;`LEARN_CONTENTS` 建出來只有 H1 與 `>` 引言,範本的示範區塊不會留在上面。舊的表格式目錄頁會在同一輪整頁轉成區塊,別的存取庫那幾筆原樣轉過去。連結驗證不過就兩頁都沒有新內容,只有 DEAD 清單的回報。consult 模式在 wiki 上無寫入跡象,只有回報內容。兩種模式跑完,`$JSC_HOME/usage/events.jsonl` 都會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:learn`,`status` 欄是那五個值之一;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 | ## report | 項目 | 內容 | | --- | --- | | 觸發時機 | 有人要一段期間的工作總結時用,期間分年、月、週、日四種。單一工作包的紀錄不走這支,走 worklog | -| 關鍵步驟 | 呼叫端沒指定期間就依 `jsc-ask:ask` 問、跑 `tools/report-range.sh` 取得起訖日與標籤、平行做四件事(`tools/report-template.sh resolve` 解出範本、從 CONTENTS 存取庫讀 `LOG_CONTENTS` 並照列上的絕對網址逐頁讀回工作日誌、年報另外讀 `LEARN_CONTENTS` 並用 `wiki-repo LEARN` 解出教訓內容頁的存取庫、解出 REPORT 的 wiki repo)、把頁面內容存成檔案後跑 `tools/log-aggregate.sh` 算出條目數、涵蓋 repo、花費時間、Token 用量、任務狀態、從存活條目挑出阻塞與未完成工作包、照範本的標題與表格填出報告、用 `hash-id "{REPORT wiki 存取庫}/{period}"` 算出完整 40 碼頁名、讀 `REPORT_{HASH}` 後把本期當成新章節追加在最前、取 `wiki-url` 的絕對網址並依它的退出碼分流(4 是頁還沒寫、5 是頁上沒有 `html_url`、7 與 8 一律停下並回報金鑰或 API 狀態,不得當成 4)、章節內與目錄列的連結一律寫成 `[{文字}]({絕對網址})` 並在寫入前全數交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、最後跑 `wiki-contents.sh upsert REPORT 2 {HASH}` 更新落在 CONTENTS 存取庫的 `REPORT_CONTENTS`(鍵取第 2 欄的裸 HASH,不取第 1 欄那個帶主機名的連結)、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:report {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 | -| 外部呼叫 | `tools/report-range.sh`、`tools/report-template.sh`、`tools/log-aggregate.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(CONTENTS、LOG、LEARN、REPORT)與 `hash-id` 與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那一列)、`jsc-gitea:wiki`、`jsc-ask:ask`、`templates/report-{period}.md`、`templates/report-contents.md` | -| 完成條件 | 回報頁面 URL,或回報跳過寫入與它的原因,或在讀取回 7、8 時停下並回報狀態。每次寫入前 `link-check.sh` 要回 0,回 1 就不寫該頁並回報 DEAD 清單。`wiki-contents.sh upsert` 要回 0,回 1、2、3、4、7、8 就照該碼回報且不得謊報已寫入。收尾要講出期間標籤、條目數、涵蓋的 repo、範本來源、頁面 URL、帶到下一期的未完成工作包清單,以及 `ELAPSED_MISSING` 與 `TOKEN_MISSING`。收尾一定要寫一筆 `skill-end` 狀態事件:章節與目錄列都到位是 `ok`(範圍內沒有條目、`log-aggregate.sh` 回 3 也算 `ok`,`detail` 帶 `ENTRIES=0`),報告本文交出去但 wiki 沒寫全是 `degraded`,連結驗證回 1 或讀寫回 7、8 是 `failed`,期間算不出來或範本與存取庫沒設定是 `blocked`,使用者中止是 `aborted` | -| 可驗證跡象 | REPORT 存取庫的 `REPORT_{HASH}`(雜湊取自 REPORT wiki 存取庫的 `{owner}/{repo}` 加期間,不是程式碼存取庫)多一個本期章節;CONTENTS 存取庫的 `REPORT_CONTENTS` 該列的「最新一期」、「期數」、「最後更新」換新,且「報表頁」欄是 `[{頁名}]({絕對網址})`、「HASH」欄是不帶連結的裸 HASH,兩頁都找不到 `[[...]]` 這種同 wiki 寫法。連結驗證不過就沒有新章節,也沒有新的目錄列,只有 DEAD 清單的回報。重跑同一期間只換掉那一列,不會多出第二列。本機留下工作日誌頁面內容的暫存檔,供 `log-aggregate.sh` 讀取。沒設定 REPORT wiki repo 時不寫 wiki,只印出報告本文;沒設定 `JSC_WIKI_REPO_CONTENTS` 時內容頁照寫,只有目錄頁那一列沒動。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:report`,`status` 與 `exit` 兩欄對得上上一列講的判準;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 | +| 關鍵步驟 | 呼叫端沒指定期間就依 `jsc-ask:ask` 問、跑 `tools/report-range.sh` 取得起訖日與標籤、平行做四件事(`tools/report-template.sh resolve` 解出範本、從 CONTENTS 存取庫讀 `LOG_CONTENTS` 並照每個區塊裡「日誌頁」那一條的絕對網址逐頁讀回工作日誌、年報另外讀 `LEARN_CONTENTS` 並用 `wiki-repo LEARN` 解出教訓內容頁的存取庫、解出 REPORT 的 wiki repo)、把頁面內容存成檔案後跑 `tools/log-aggregate.sh` 算出條目數、涵蓋 repo、花費時間、Token 用量、任務狀態、從存活條目挑出阻塞與未完成工作包、照範本的標題與表格填出報告、用 `hash-id "{REPORT wiki 存取庫}/{period}"` 算出完整 40 碼頁名、讀 `REPORT_{HASH}` 後把本期當成新章節追加在最前、取 `wiki-url` 的絕對網址並依它的退出碼分流(4 是頁還沒寫、5 是頁上沒有 `html_url`、7 與 8 一律停下並回報金鑰或 API 狀態,不得當成 4)、章節內與目錄區塊的連結一律寫成 `[{文字}]({絕對網址})` 並在寫入前全數交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、照 `templates/report-contents.md` 組出一個 H2 區塊檔(`## REPORT_{HASH}` 加空行加各條 `- {欄位名}:{值}`)、跑 `wiki-contents.sh upsert REPORT 1 REPORT_{HASH}` 更新落在 CONTENTS 存取庫的 `REPORT_CONTENTS`(鍵是 H2 標題的頁名,不是「報表頁」那條帶主機名的連結;命令裡的 `1` 是 ``,只在舊頁還是表格時用來認出哪一欄的文字當標題)並依它的退出碼分流(0 已寫、1 組不出內容或寫入失敗、2 參數錯、3 未設 `JSC_WIKI_REPO_CONTENTS`、4 缺範本、7 金鑰失效、8 其他 API 失敗)、最後跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:report {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 | +| 外部呼叫 | `tools/report-range.sh`、`tools/report-template.sh`、`tools/log-aggregate.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(CONTENTS、LOG、LEARN、REPORT)與 `hash-id` 與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那個區塊)、`jsc-gitea:wiki`、`jsc-ask:ask`、`templates/report-{period}.md`、`templates/report-contents.md` | +| 完成條件 | 回報頁面 URL,或回報跳過寫入與它的原因,或在讀取回 7、8 時停下並回報狀態。每次寫入前 `link-check.sh` 要回 0,回 1 就不寫該頁並回報 DEAD 清單。`wiki-contents.sh upsert` 要回 0 並印出 `updated` 或 `added`,回 1、2、3、4、7、8 就照該碼回報且不得謊報已寫入。收尾要講出期間標籤、條目數、涵蓋的 repo、範本來源、頁面 URL、帶到下一期的未完成工作包清單,以及 `ELAPSED_MISSING` 與 `TOKEN_MISSING`。收尾一定要寫一筆 `skill-end` 狀態事件:章節與目錄區塊都到位是 `ok`(範圍內沒有條目、`log-aggregate.sh` 回 3 也算 `ok`,`detail` 帶 `ENTRIES=0`),報告本文交出去但 wiki 沒寫全是 `degraded`,連結驗證回 1 或讀寫回 7、8 是 `failed`,期間算不出來或範本與存取庫沒設定是 `blocked`,使用者中止是 `aborted` | +| 可驗證跡象 | REPORT 存取庫的 `REPORT_{HASH}`(雜湊取自 REPORT wiki 存取庫的 `{owner}/{repo}` 加期間,不是程式碼存取庫)多一個本期章節;CONTENTS 存取庫的 `REPORT_CONTENTS` 上標題為 `REPORT_{HASH}` 的那個區塊,「最新一期」、「期數」、「最後更新」三條換新,「報表頁」那一條是 `[{頁名}]({絕對網址})`、「HASH」那一條是不帶連結的裸 HASH,目錄頁上沒有 markdown 表格,兩頁也都找不到 `[[...]]` 這種同 wiki 寫法。連結驗證不過就沒有新章節,也沒有新的目錄區塊,只有 DEAD 清單的回報。重跑同一期間只換掉那個區塊,不會多出第二個;四種期間各自一個區塊,因為 `HASH` 帶著期間。舊的表格式目錄頁會在同一輪整頁轉成區塊,別人那幾筆原樣轉過去。本機留下工作日誌頁面內容的暫存檔,供 `log-aggregate.sh` 讀取。沒設定 REPORT wiki repo 時不寫 wiki,只印出報告本文;沒設定 `JSC_WIKI_REPO_CONTENTS` 時內容頁照寫,只有目錄頁那個區塊沒動。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:report`,`status` 與 `exit` 兩欄對得上上一列講的判準;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 | ## stats @@ -37,7 +37,7 @@ | 項目 | 內容 | | --- | --- | | 觸發時機 | implement 或 maintain 階段每結束一項任務就寫一筆。一項任務是一個工作包、一輪 PR 意見修正,或一次獨立的修正提交。下一項任務開始前就要寫完。規劃階段的筆記不走這支 | -| 關鍵步驟 | 全程由 sub agent 收集與寫入、平行取得十項事實(`git remote get-url origin` 的 repo、`git branch --show-current` 的分支、PLAN 頁絕對連結、ANALYZE 頁工作包絕對連結、`session-timer.sh report` 的花費時間、`token-usage.sh` 的各 CLI Token、任務狀態、細節與產出、困難與解法、PR 目標分支)、平行解出 LOG wiki repo(只供內容頁)與完整 40 碼大寫的 `{HASH}`、跑 `tools/worklog-target.sh` 取得頁名與本週五日期、用 `templates/log-entry.md` 填出單筆條目檔、跑 `tools/worklog-pending.sh merge` 併入待寫內容(同時收編同一個 `{HASH}` 的「`H` 加前 7 碼」舊目錄)、跑 `tools/worklog-pending.sh orphans` 掃出現行規則定址不到的暫存目錄(0 就安靜帶過,4 就把每一列回報給使用者並繼續本輪)、讀 `PAGE` 後依退出碼分支(0 追加在頁尾、4 才建頁、7 與 8 停止且不建頁)、寫入前把 `MERGED` 裡的每個連結交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、同一輪用 `wiki-repo CONTENTS` 解出目錄頁存取庫、取 `wiki-url` 的絕對網址並寫成 `[{頁名}]({絕對網址})`、同樣先過 `link-check.sh` 才跑 `wiki-contents.sh upsert LOG 2 {HASH}` 更新 `LOG_CONTENTS`(鍵取第 2 欄的裸 HASH,不取第 1 欄那個帶主機名的連結)並依 0、1、2、3、4、7、8 各自分流、最後依成敗跑 `worklog-pending.sh commit` 或 `abort`、再跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:worklog {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 | -| 外部呼叫 | `jsc-hooks/hooks/session-timer.sh report`、`tools/token-usage.sh`、`tools/worklog-target.sh`、`tools/worklog-pending.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(LOG 與 CONTENTS)與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那一列)、`jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`、`jsc-ask:ask`、`git remote get-url origin`、`git branch --show-current`、`templates/log-entry.md`、`templates/log-contents.md` | -| 完成條件 | 兩次寫入前 `link-check.sh` 都回 0,`MERGED` 的每一筆條目都在 `PAGE` 上,原有條目逐字不動,`wiki-contents.sh upsert` 回 0 且 `CONTENTS` 該列帶著本週五日期、其他列不動,`worklog-pending.sh` 的 `commit` 或 `abort` 其中一個跑過並回報退出碼,`orphans` 也跑過且回 0 或已把孤兒清單回報出去。`link-check.sh` 回 1 或 `upsert` 回非 0 就照該碼回報,並把步驟七當成失敗處理,讓待寫內容留著。收尾一定要寫一筆 `skill-end` 狀態事件:兩頁都到位是 `ok`(`orphans` 掃到孤兒仍算 `ok`,但 `detail` 要帶筆數,因為孤兒屬於別的存取庫,不影響本輪結論),內容頁寫成功而 `LOG_CONTENTS` 沒更新是 `degraded`,連結驗證回 1 不寫是 `failed`,雜湊工具或日期運算缺席、wiki 存取庫沒設定是 `blocked`,使用者中止或這一輪根本沒有任務結束是 `aborted` | -| 可驗證跡象 | LOG 存取庫的 `LOG_{HASH}` 頁尾多一筆條目,條目裡的計畫名稱、工作包編號、PR 目標分支三欄都是 `[{文字}]({絕對網址})`;CONTENTS 存取庫的 `LOG_CONTENTS` 該列的「條目數」與「最後更新」換新,且「日誌頁」欄是 `[{頁名}]({絕對網址})`,頁面上沒有 `[[...]]` 這種同 wiki 寫法,「HASH」欄是不帶連結的裸 HASH。連結驗證不過就兩頁都沒有新內容,待寫檔原樣保留。重跑同一頁只換掉那一列,不會多出第二列。`$JSC_HOME/worklog-pending/{HASH}` 底下的待寫檔在 `commit` 後清空,`abort` 後原樣保留;該目錄名是 40 碼大寫十六進位,或尚未遷移的舊暫存那種 8 碼大寫十六進位、`H` 加 7 碼大寫十六進位;推得出對映的舊目錄會連同新目錄一起被清掉。暫存區留下非 40 碼的目錄時,該輪的回報上看得到 `orphans` 印出的那幾列。本機留下填好的條目檔。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:worklog`,`status` 與 `exit` 兩欄對得上上一列講的判準,孤兒筆數落在 `detail` 欄;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 | +| 關鍵步驟 | 全程由 sub agent 收集與寫入、平行取得十項事實(`git remote get-url origin` 的 repo、`git branch --show-current` 的分支、PLAN 頁絕對連結、ANALYZE 頁工作包絕對連結、`session-timer.sh report` 的花費時間、`token-usage.sh` 的各 CLI Token、任務狀態、細節與產出、困難與解法、PR 目標分支)、平行解出 LOG wiki repo(只供內容頁)與完整 40 碼大寫的 `{HASH}`、跑 `tools/worklog-target.sh` 取得頁名與本週五日期、用 `templates/log-entry.md` 填出單筆條目檔、跑 `tools/worklog-pending.sh merge` 併入待寫內容(同時收編同一個 `{HASH}` 的「`H` 加前 7 碼」舊目錄)、跑 `tools/worklog-pending.sh orphans` 掃出現行規則定址不到的暫存目錄(0 就安靜帶過,4 就把每一列回報給使用者並繼續本輪)、讀 `PAGE` 後依退出碼分支(0 追加在頁尾、4 才建頁、7 與 8 停止且不建頁)、寫入前把 `MERGED` 裡的每個連結交給 `link-check.sh` 驗證(0 才寫入、1 有 DEAD 就不寫並回報那幾筆、2 沒給網址、3 未設 `GITEA_HOST`、7 金鑰被拒就停下)、同一輪用 `wiki-repo CONTENTS` 解出目錄頁存取庫、取 `wiki-url` 的絕對網址並寫成 `[{頁名}]({絕對網址})`、同樣先過 `link-check.sh`、照 `templates/log-contents.md` 組出一個 H2 區塊檔(`## LOG_{HASH}` 加空行加各條 `- {欄位名}:{值}`)才跑 `wiki-contents.sh upsert LOG 1 LOG_{HASH}` 更新 `LOG_CONTENTS`(鍵是 H2 標題的頁名,不是「日誌頁」那條帶主機名的連結;命令裡的 `1` 是 ``,只在舊頁還是表格時用來認出哪一欄的文字當標題)並依 0、1、2、3、4、7、8 各自分流(1 是組不出內容或寫入失敗,找不到區塊只是走附加,不算錯)、最後依成敗跑 `worklog-pending.sh commit` 或 `abort`、再跑 `jsc-hooks/tools/report-status.sh skill-end jsc-log:worklog {status} {結束碼} {detail}` 記下本輪結果,腳本不在這台機器上就安靜跳過 | +| 外部呼叫 | `jsc-hooks/hooks/session-timer.sh report`、`tools/token-usage.sh`、`tools/worklog-target.sh`、`tools/worklog-pending.sh`、`jsc-gitea/tools/gitea.sh wiki-repo`(LOG 與 CONTENTS)與 `wiki-url`、`jsc-gitea/tools/link-check.sh`(寫入前驗證連結)、`jsc-gitea/tools/wiki-contents.sh upsert`(目錄頁那個區塊)、`jsc-gitea/tools/hash-id`、`jsc-gitea:wiki`、`jsc-ask:ask`、`git remote get-url origin`、`git branch --show-current`、`templates/log-entry.md`、`templates/log-contents.md` | +| 完成條件 | 兩次寫入前 `link-check.sh` 都回 0,`MERGED` 的每一筆條目都在 `PAGE` 上,原有條目逐字不動,`wiki-contents.sh upsert` 回 0 且 `CONTENTS` 上標題為 `LOG_{HASH}` 的那個區塊帶著本週五日期、其他區塊不動,`worklog-pending.sh` 的 `commit` 或 `abort` 其中一個跑過並回報退出碼,`orphans` 也跑過且回 0 或已把孤兒清單回報出去。`link-check.sh` 回 1 或 `upsert` 回非 0 就照該碼回報,並把步驟七當成失敗處理,讓待寫內容留著。收尾一定要寫一筆 `skill-end` 狀態事件:兩頁都到位是 `ok`(`orphans` 掃到孤兒仍算 `ok`,但 `detail` 要帶筆數,因為孤兒屬於別的存取庫,不影響本輪結論),內容頁寫成功而 `LOG_CONTENTS` 沒更新是 `degraded`,連結驗證回 1 不寫是 `failed`,雜湊工具或日期運算缺席、wiki 存取庫沒設定是 `blocked`,使用者中止或這一輪根本沒有任務結束是 `aborted` | +| 可驗證跡象 | LOG 存取庫的 `LOG_{HASH}` 頁尾多一筆條目,條目裡的計畫名稱、工作包編號、PR 目標分支三欄都是 `[{文字}]({絕對網址})`;CONTENTS 存取庫的 `LOG_CONTENTS` 上標題為 `LOG_{HASH}` 的那個區塊,「條目數」與「最後更新」兩條換新,「日誌頁」那一條是 `[{頁名}]({絕對網址})`、「HASH」那一條是不帶連結的裸 HASH,目錄頁上沒有 markdown 表格,也沒有 `[[...]]` 這種同 wiki 寫法。連結驗證不過就兩頁都沒有新內容,待寫檔原樣保留。重跑同一頁只換掉那個區塊,不會多出第二個。舊的表格式目錄頁會在同一輪整頁轉成區塊,別的日誌頁那幾筆原樣轉過去。`$JSC_HOME/worklog-pending/{HASH}` 底下的待寫檔在 `commit` 後清空,`abort` 後原樣保留;該目錄名是 40 碼大寫十六進位,或尚未遷移的舊暫存那種 8 碼大寫十六進位、`H` 加 7 碼大寫十六進位;推得出對映的舊目錄會連同新目錄一起被清掉。暫存區留下非 40 碼的目錄時,該輪的回報上看得到 `orphans` 印出的那幾列。本機留下填好的條目檔。跑完 `$JSC_HOME/usage/events.jsonl` 會多一筆 `{kind:skill,phase:end}` 事件,`name` 欄是 `jsc-log:worklog`,`status` 與 `exit` 兩欄對得上上一列講的判準,孤兒筆數落在 `detail` 欄;`jsc-hooks` 不在這台機器上時沒有這一筆,技能本身照樣跑完 | From fda0fca304c7466063b8d2af15c38c3f962e0625 Mon Sep 17 00:00:00 2001 From: Jeffery Date: Wed, 2 Sep 2026 17:22:33 +0800 Subject: [PATCH 5/5] =?UTF-8?q?chore(manifest):=20=E4=B8=89=E4=BB=BD=20man?= =?UTF-8?q?ifest=20=E5=8D=87=E7=89=88=E8=87=B3=200.1.9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 三份外掛清單的版號一起往上推一版。 三個目錄頁的版面與寫入參數都變了,呼叫端要靠版號才判得出手上這一份是新的 還是舊的。版號不動,版本閘門就不會提示更新,機器上會留著舊版敘述去寫新版 目錄頁。 三份清單各自被不同的 CLI 讀,值必須一致,所以一起改、一起提交。 範圍是外掛清單的版號宣告。 --- .claude-plugin/plugin.json | 2 +- .codex-plugin/plugin.json | 2 +- plugin.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 622305b..2c6fa76 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-log", - "version": "0.1.8", + "version": "0.1.9", "description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)", "skills": "./skills", "author": { diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 57b4094..6a86535 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-log", - "version": "0.1.8", + "version": "0.1.9", "description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)", "skills": "./skills", "jsc": { diff --git a/plugin.json b/plugin.json index 0eafa0a..7476c3c 100644 --- a/plugin.json +++ b/plugin.json @@ -1,6 +1,6 @@ { "name": "jsc-log", - "version": "0.1.8", + "version": "0.1.9", "description": "工作日誌(LOG_{HASH} wiki 頁)、技能使用統計與教訓紀錄(LEARN_{HASH} wiki 頁)", "skills": "./skills/", "jsc": {