Files
log/skills/report/SKILL.md
T
jiantw83 aee17e45c7 feat(wiki): 三個目錄頁改走專用存取庫,比對鍵改用裸 HASH
What:LOG_CONTENTS、LEARN_CONTENTS、REPORT_CONTENTS 改由 wiki-repo CONTENTS
解析並透過 wiki-contents.sh upsert 寫入,內容頁仍各走自己的型別。LOG 與 REPORT
的目錄頁新增一欄裸 HASH 當比對鍵。

Why:比對鍵原本是含網址的儲存格,換主機、換存取庫或 URL 編碼有差就比對不到,
upsert 會走附加分支,同一頁多出第二列而舊列永遠不再更新。REPORT 更脆:存取庫一換,
雜湊與網址同時變,四個期間的列會一次全部重複。

How:worklog-pending.sh 的 valid_hash 放寬成 40 碼、8 碼與 H 加 7 碼三種形狀。
放寬的是長度不是字元集——先剝字元再比長度的順序保留,路徑穿越與換行注入照樣擋下。
年報不得沿用日誌存取庫那條規則收斂到內容頁,目錄頁同住一庫是另一回事。

Who:jsc-log
2026-09-02 11:02:21 +08:00

14 KiB

name, description
name description
report 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.

report — summarise work logs by period

Reading and aggregating the log pages MUST run as a sub agent: it reads every page in LOG_CONTENTS and only a handful of entries survive the date filter. Report content is written in Traditional Chinese (STE100).

1. Period

Ask for the period per the jsc-ask:ask rules when the caller did not name one: daily, weekly, monthly, yearly. Each option states what it covers.

Run tools/report-range.sh {period} [yyyy-MM-dd]. It prints {start}<TAB>{end}<TAB>{label}<TAB>{period}, both dates inclusive. The base date defaults to today; pass one to re-run an earlier period.

Exit Meaning Do
0 Range printed Read start, end and label out of the four fields
2 Period name or base date rejected Ask per the jsc-ask:ask rules which of the four periods was meant, or fix the yyyy-MM-dd base date, then rerun
4 This machine's date does no date arithmetic Stop and report that the range cannot be computed here. Never work the dates out by hand: week boundaries and month lengths are exactly where a hand-rolled range quietly loses a day

Done when start, end and label are known.

2. Resolve and collect

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.

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}<TAB>{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.
  4. Report repo. gitea.sh wiki-repo REPORT, so step 3 has its target ready.

Exit branches for the external calls above:

Call Exit Do
report-template.sh resolve 0 Use the path; name the project or skill source in the final report. project means the working directory holds .jsc/templates/report-{period}.md and that file wins — the same period rendered from two templates has to be traceable to the file that shaped it
report-template.sh resolve 2 Period name or start directory rejected. Rerun from the working directory with the period from step 1
report-template.sh resolve 3 Neither the project copy nor the skill's own copy exists. Stop and report that templates/report-{period}.md is missing from the plugin; do not invent a layout
gitea.sh wiki-repo CONTENTS 3 Stop and report that no wiki repo is configured for the directory pages, naming JSC_WIKI_REPO_CONTENTS and JSC_WIKI_REPO. Ask per the jsc-ask:ask rules, then rerun. Without LOG_CONTENTS there is no list of log pages to read
gitea.sh wiki-repo LOG 3 Stop and report that no wiki repo is configured for LOG, naming JSC_WIKI_REPO_LOG and JSC_WIKI_REPO. Ask per the jsc-ask:ask rules, then rerun. Without log pages there is nothing to summarise
gitea.sh wiki-repo LEARN 3 Fill the 全年教訓 section with 無 and say the LEARN wiki repo is unset. The rest of the yearly report still stands
gitea.sh wiki-repo REPORT 3 Carry on collecting; step 3 handles the skipped write
gitea.sh wiki-repo any type 2 The page type was misspelled. Fix the argument and rerun
jsc-gitea:wiki read 4 The page is genuinely absent. Only this code lets LOG_CONTENTS or a listed log page count as zero entries; name it in the close-out
jsc-gitea:wiki read 7 The token is invalid or lacks permission. Stop and report the token problem. Counting this as zero entries publishes a report that says a period held no work when the work is sitting on a page nobody managed to read
jsc-gitea:wiki read 8 Some other API failure. Stop and report that status; a page that failed to load must never be counted as an empty page

Then aggregate. Save the collected page contents to files and run:

tools/log-aggregate.sh {start} {end} {page file} ...

It prints ENTRIES=, REPOS=, one REPO= line per repository, ELAPSED_MINUTES=, ELAPSED_ENTRIES=, ELAPSED_MISSING=, one TOKEN= line per CLI, TOKEN_MISSING= and one STATUS= line per status. It enforces the no-estimate rule in code: an entry with no 花費時間 stays out of the total and lands in ELAPSED_MISSING, and a range where nothing carried a time prints ELAPSED_MINUTES=無資料 rather than 0.

Exit Meaning Do
0 Aggregates printed Carry the printed values into the template unchanged. Never recompute or round them by hand
2 Dates rejected, or start later than end Rerun with the step 1 values
3 Zero entries inside the range; the zero-filled aggregate is still printed Produce the report anyway, with counts of 0 and a line naming the empty range. A silent "no report" cannot be told apart from a failure
4 A page file is unreadable Stop and report which file, rather than reporting a smaller total

Blockers and unfinished work packages come from the 任務狀態 and 遇到的困難與解決方式 parts of the surviving entries; the sub agent lists them verbatim.

Done when the template path, its source, the aggregate lines and the blocker list are all in hand.

3. Write

Follow the template's headings and tables exactly, including ones with no data: an empty section stated as 無 is information, a silently dropped section is not.

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.

  • 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 by the absolute URL from gitea.sh wiki-url <REPORT repo> REPORT_{HASH}; [[REPORT_{HASH}]] resolves only inside one wiki and would dead-link from here. 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:

    jsc-gitea/tools/wiki-contents.sh upsert REPORT 2 "{HASH}" {row 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.

Call Exit Do
gitea.sh wiki-repo REPORT 3 Print the finished report and say the write was skipped because no wiki repo is configured for REPORT. The report itself is still the deliverable
gitea.sh hash-id 1 No SHA-1 helper on this machine. Stop and report that sha1sum or shasum has to be installed. Never hand-compute the hash
gitea.sh hash-id 2 The input was empty, which means the REPORT wiki repo or the period never reached it. Fix the string and rerun; the empty string has a valid SHA-1 and would file the report on a page nobody reads
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 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 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.

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.

4. Close

State the period label, entry count, repositories covered, template source, and the page URL. Name every unfinished work package that carried over — that list is what the next period starts from. State ELAPSED_MISSING and TOKEN_MISSING whenever either is above 0, so a small total is read as missing data rather than a light week.

Done when those five facts, the carry-over list and the two missing-data counts are stated.