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}