From c50bd49b4aa8c700447257aa675006733eda8f44 Mon Sep 17 00:00:00 2001 From: Jef Roosens Date: Sat, 30 May 2026 19:59:02 +0200 Subject: [PATCH] Add weekly-review skill and project guide Add .claude/skills/weekly-review: a Claude Code skill that builds the weekly timesheet review (narrative summary, reviews split out, loose ends) and writes it into the Joplin week note, plus a distill/refine pass over the status/distill and status/refine knowledge queue. Also track CLAUDE.md, the project/agent guide. Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/skills/weekly-review/SKILL.md | 352 ++++++++++++++++++++++++++ CLAUDE.md | 1 + 2 files changed, 353 insertions(+) create mode 100644 .claude/skills/weekly-review/SKILL.md create mode 120000 CLAUDE.md diff --git a/.claude/skills/weekly-review/SKILL.md b/.claude/skills/weekly-review/SKILL.md new file mode 100644 index 0000000..c837044 --- /dev/null +++ b/.claude/skills/weekly-review/SKILL.md @@ -0,0 +1,352 @@ +--- +name: weekly-review +description: >- + Generate a weekly timesheet review (narrative summary + loose ends) from the + Joplin week note and write it into that note's Review section, and process the + knowledge queue (notes tagged status/distill and status/refine). Use when the + user asks for a weekly review, a week recap, to "review my week", to fill in + the Review section of a timesheet week note, or to "process distill/refine + tags", "distill my notes", or "clear the knowledge queue". Optionally takes a + date or week to target a week other than the current one. +--- + +# Weekly timesheet review + +Build a review of one week of timesheets and write it into the Review section +of the corresponding Joplin week note. The point is that reading it tells you +what the week was about. The review has these parts: a narrative **Summary** of +the work done, a short **Reviews** section for work that was only reviewed (not +the user's own tickets), and a **Loose ends** section for carry-over. + +Altitude matters: explain what happened and the conclusions, not the precise +technical detail. The user has explicitly trimmed hard metrics, config values, +and internal symbol names out of the summary (the story-log notes already hold +those). Aim for "I can read this and immediately know what the week was about", +not a technical report. See the Voice section. + +The full weekly ritual has **two parts**: + +1. **Part 1 — Week review**: build the Summary / Reviews / Loose ends and write + it into the week note (steps below). +2. **Part 2 — Distill & refine**: process the knowledge queue, i.e. notes tagged + `status/distill` and `status/refine` (at the end of this file). + +Part 2 is **independent of the week** chosen in Part 1: the tags are a standing +queue, not week-scoped. It can be run on its own. Do both parts when running a +full weekly review unless the user only asked for one. + +Read `README.md` and `CLAUDE.md` first if you are not already familiar with the +project. The timesheet structure, table format, and Joplin notebook layout are +described there. + +## Inputs + +- Optional target week. Accept a date (`2026-05-29`, `yesterday`, `last week`) + or an ISO week. Default to the current week (today's date). +- The CLI reads timesheets from Joplin with `--joplin`. Config (token, project + map) is already set up; do not ask for it. + +## Steps + +### 1. Resolve the target week and locate the note + +- Determine the target date. Default to today. +- Get the structured data straight from the CLI (it resolves the week itself): + + ```sh + uv run timesheets stories -w --joplin # themes + per-story time + uv run timesheets summary -w -ss --joplin # per-day totals + week total + ``` + + `` is optional; omit it for the current week. The `stories` output is + the **source of truth** for the `## Stories` block; do not re-derive it by + hand. +- Find the Joplin note to write into. Week notes are titled `YYYY - WNN` in + `Work / Timesheets / YYYY`. Compute the ISO year and week from the target + date, then: + + ``` + find_notes_in_notebook("Work/Timesheets/") + ``` + + Match the title `YYYY - WNN` and keep its `note_id`. + +### 2. Read context + +- `get_note(note_id, force_full=true)` for the target week. From it, collect: + - Free-text notes under each day (often Dutch; that is fine). + - Checklist items: `- [ ]` (open) and `- [x]` (done). + - The `Note` column of each table row. This is NOT a personal scratchpad: it + is part of the timesheet and gets pushed to the timesheet system (Odoo) as + the entry description. The user uses it to section off parts of a longer + ticket ("fix tests", "respond to comments", "rebase", "review"). Read it for + the *what/why* of each entry, but never suggest the user put extra detail + there. Richer per-ticket detail belongs in the story-log notes, not here. + - **`review` as the note marks reviewed work, not the user's own work.** An + entry whose note is "review" means the user was reviewing someone else's MR + on that ticket, not building the ticket themselves. These are handled + separately (see step 3, Reviews). Treat the literal note "review" as the + signal. +- **Read the linked story notes.** The week note contains Joplin links of the + form `[label](:/)` in the Story column, the Note column, and the + free text. Extract every `(:/<32-hex-id>)` link, dedupe the ids, and + `get_note(id, force_full=true)` each one **before** writing the Summary. +- **Find story notes that are not linked in the timesheet.** Not every ticket is + linked inline, but a note may still exist. For each PBI/Bug worked on this + week, look for its backing note in `Work / Story Logs` (and + `Work / Story Logs / Archive / `): list the notebook and/or + `find_notes(" ")`, and match by ticket number. + Build a map of `ticket -> note_id` for every story that has a note. Read the + ones with real content. Note titles follow `PBI : ` / + `Bug <num>: <title>`, or a descriptive title for research notes + (e.g. "Recticel profiling research"). Many notes are near-empty section + templates; link them but do not mine them for narrative. + + Story-log notes carry the substance (Context / Investigation / Decision / + Solution / Learnings, or research findings). The narrative should draw on + that, not just the one-line story titles. +- Read the **previous one or two** week notes the same way (the next-lower + `WNN`). You need them only to detect carry-over in step 4. + +### 3. Write the narrative Summary (and split out Reviews) + +**First, separate reviewed work from own work.** Go through the raw rows and +classify each story: + +- A story is **reviewed work** if all its entries that week have the note + "review" (the user only checked someone else's MR, did not build it). +- Everything else is **own work**. + +Own work goes in the Summary. Reviewed-only work goes in a separate, short +`## Reviews` section (see step 5). Do not mix the two: the user does not count +reviewing someone else's MR as part of their own work done. A story with a mix +of real work and review entries stays own work. + +Then write the Summary: + +- Group own work by the project/task labels that `stories` produced (these are + the themes). Lead with the themes that took the most time. +- For each significant theme, write a few plain sentences on what happened that + week and where it landed. Convey the gist, not the precise detail (no exact + percentages, config values, or internal symbol names; the story-log note holds + those). Someone reading it should know what the week was about. +- Call out the shape of the week where it is real: a dominant feature, a heavy + bug-fixing stretch, lots of context switching, an interruption (incoming + requests, customer work like Recticel), or a spike/research day. +- **Mention noteworthy meetings explicitly.** The daily standup and Factry Flow + are routine, fold them into a brief catch-all line at most. Every *other* + meeting is often noteworthy and should be named: brainstorms (e.g. Historian + BrAInStorm, AI-usecases), sprint retros, growth-path sessions, customer or + technical meetings (e.g. the Recticel stress-test meeting). Identify these + from `internal`, `product`, `incoming`, customer projects, and the `Note` + column ("brainstorm", "meeting", "retro", "Growth Path", etc.). Say what the + meeting was about if the notes make it clear. +- Mention the week total and anything notable about it (e.g. leave days, a short + or long week) using the `summary` output. + +#### Voice — write it in the user's polished English + +The review is the user's own log, written for themselves, so it can sit a notch +looser than a doc meant for others. But it must be **clean English in the user's +polished voice**, not the rough Dutch/English mix they use while drafting +timesheet free-text. The reference for the target voice is their knowledge notes +(e.g. the "Express Guide" OpenTelemetry note); the self-notes (e.g. the looser +"OpenTelemetry" note) show the acceptable looser end. + +Their polished style: + +- **English, full sentences, normal capitalization.** No Dutch. No + lowercase-everything. No `gwn`/`da`/`ge`/`et`. +- **Direct, at the right altitude.** Active voice. Explain plainly what + happened and the conclusion ("ArgoCD is gitops CICD for Kubernetes, it + reconciles the cluster against the git repo; got it running, plenty still to + do"). A short conceptual explanation is welcome. Hard numbers, exact config + values, and internal symbol names are not: the user trims those out (e.g. they + cut `GOGC=200` / `~50%` / `buildRange` down to "changed GC to be less + aggressive with `GOGC`"). When in doubt, go one level more abstract. +- **Terse, no throat-clearing.** Skip "This week was dominated by...". Lead with + the substance. Bullets are fine where they read better than prose. +- **`->` for results/consequences** is fine and matches their notes. +- **No em dashes.** Use commas, parentheses, `->`, or new sentences. +- Light first person is fine ("I got ArgoCD running via port-forward"), matching + how they write in their own logs. +- Do not over-polish. The user's own prose has the odd typo and loose phrasing; + clean readable English is the target, not editorial perfection. + +Pull the substance and phrasing from the linked story notes, but render it in +this clean English voice. Accurate before polished. + +#### Referencing tickets — paraphrase, then ticket in brackets + +Never drop a literal story title into the prose; many are too long. Paraphrase +what the ticket was about in your own short words, then put the ticket id in +round brackets after it. Put the note link on the bracketed id when a note +exists. + +- With a note: `setting up ArgoCD for the Hatchry demos ([PBI 34839](:/<note_id>))` +- Without a note: `the homepage audit logs not checking privileges (Bug 34960)` + +Only link tickets you actually found a note for (the `ticket -> note_id` map from +step 2). Never invent note ids. Reference each ticket this way the first time it +appears; afterwards a short phrase is fine. + +### 4. Build the Loose ends section + +Carry-over is the point of this section. Include: + +- **Open checklist items** (`- [ ]`) still present in the target week note. +- **Recurring unclosed stories**: stories worked on this week that also appear + in the previous week(s) and are clearly still in flight (still being reviewed, + rebased, "respond to comments", failing tests, etc.). Use judgement from the + `Note` column; do not flag a story as carry-over just because the number + recurs. +- **Still-open todos** from the previous week note that were never checked off + and are still relevant. + +If there is genuinely nothing to carry over, write a single line saying so +rather than padding. + +### 5. Assemble the review block + +Produce this structure: + +```markdown +## Summary + +<narrative of own work, grouped by theme, most time first; meetings line near the end> + +## Reviews + +<short list of stories that were only reviewed (note = "review"), with rough total time> + +## Loose ends + +<open checklist items, recurring unclosed stories, carry-over todos> +``` + +- The **Reviews** section is a short bullet list, not prose. Paraphrase each + reviewed ticket and put the id in brackets, same format as the Summary. If + nothing was only-reviewed that week, drop the section. +- Theme times in the Summary should reflect **own work only**; a story that + moved to Reviews takes its time with it (its per-story total from `stories` + is its review time, since it is review-only). +- The older notes (W20, W21) carried a verbatim `## Stories` block (the raw CLI + output with full ticket titles). The user prefers paraphrased references and + does not want literal story names dumped in, so do **not** add that block by + default. Only include it if the user asks for the raw ticket/time ledger. + +### 6. Confirm, then write into the note + +- **Show the full draft to the user first and ask for confirmation.** This is + LLM-generated prose going into a durable note; do not write blindly. Apply any + edits the user asks for. +- Write it into the Review section with `edit_note` in Replace mode, anchored on + the review heading so the day tables below are untouched. The review heading is + the first heading of the note, of the form `# Week of YYYY-MM-DD - Review` (or + just `# Review` in older notes). Replace from that heading down to (but not + including) the first day heading (`# <weekday> - ...`). + + Concretely: read the current text between the review heading and the first day + heading, pass it as `old_string`, and pass the heading plus the new block as + `new_string`. If the Review section is an empty placeholder (heading + immediately followed by the first day heading), anchor on the heading line + alone and insert the block after it. +- After writing, re-fetch the note (or report the diff) so the user can verify. + +## Notes and edge cases + +- Tables have no `Duration` column; the CLI computes duration from Start/End. + Trust the CLI's times, not a hand count. +- A leave day shows as `~verlof` / a `VERLOF:` heading prefix and counts toward + totals but has no stories. Mention leave in the Summary when it shortened the + week. +- Overlapping entries exist in the raw tables; the CLI already resolves them. + Never sum durations by hand from the raw note. +- Do not modify the day tables or their `Note` columns. Only the Review section. + +--- + +# Part 2 — Distill and refine the knowledge queue + +Clearing the knowledge queue: notes the user flagged during the week for +knowledge work. This is **week-independent** (it processes whatever is currently +tagged, a standing queue) and can be run on its own. + +## What the tags mean + +- **`status/distill`**: the note holds general, reusable knowledge (a gotcha, a + fact, how something works) that should be promoted into a proper Knowledge + page. Example: OpenAPI gotchas written down while doing a REST-migration ticket + were distilled into the `OpenAPI Cheat Sheet`. Ticket-specific debugging detail + stays in the story log; only the generalizable knowledge gets distilled. +- **`status/refine`**: the note itself needs rewriting/cleanup. The content + stays in the same note, it just gets cleaned up. This applies mainly to + story-log and similar notes. Important exclusions: + - **Checkin notes** (in `Work / Meetings / Checkins`) are never refined by the + skill. The user does those themselves. Skip them entirely. + - For any **meeting note**, never touch the `## Topics` section. Those are the + user's own words and stay as-is. Refine only the other sections if they hold + rough content that needs cleanup. + - Refine in the note's **original language**. If the note mixes languages, use + English. + +Note: `status/read` is unrelated to this queue. It means the note contains a +link to something that still needs to be read, NOT "already processed". Do not +treat it as a done-state and do not touch it here. + +A note can carry both tags. + +## Steps + +1. **Collect the queue.** `find_notes_with_tag("status/distill")` and + `find_notes_with_tag("status/refine")`. Read each note in full. +2. **Know the knowledge base.** Read `Knowledge/_Index` for the existing pages + (cheat sheets, concept pages, infra, Factry-internal, etc.). Distilled content + should land in an existing page; only propose a new page when nothing fits, + and mark it clearly as new. +3. **For each `distill` note, propose:** + - the target Knowledge page: an existing page when one fits, or a new page + when the topic deserves its own (e.g. ArgoCD got its own page because the + user will keep researching it). Mark new pages as new and place a link under + the right `Knowledge/_Index` heading. **Before proposing a new page, search + (`find_notes`) for an existing one on the topic and check `Knowledge/_Index` + — merge into it rather than creating a duplicate.** Note that the available + Joplin tools cannot delete notes, so a wrongly-created duplicate can only be + blanked, not removed; check first. + - the actual distilled content, written in the user's knowledge-note voice. + The reference for knowledge pages is the "Express Guide" OpenTelemetry note: + fuller and more explanatory than the weekly summary, well-structured, clean + English, no em dashes. + - Quote the source lines you are distilling so the user can verify nothing was + invented or distorted. +4. **For each `refine` note, propose** the rewritten note (or rewritten + sections), preserving the user's stances, opinions, and meaning exactly. Do + not soften or invent content. Honor the exclusions above (skip checkin notes, + never touch a meeting note's `## Topics`, keep the original language / English + if mixed). If a note's only content is in an off-limits section, there is + nothing to refine: leave it for the user and do not remove its tag. +5. **List everything and get approval before writing anything.** Present one + list: per note, what would be written and where (knowledge page edit, or note + rewrite). The user approves or rejects each item individually. This mirrors + Part 1's confirm-before-write rule and is a hard requirement here. +6. **After approval**, for each approved item: + - distill: merge the content into the target Knowledge page with `edit_note` + (and add a link in `Knowledge/_Index` if a new page was created). Leave the + source note's content intact. + - refine: rewrite the note in place with `edit_note`. + - **Tag handling:** once a note has actually been processed, remove the + processed tag with `untag_note` (drop `status/distill` and/or + `status/refine`). Only remove a tag for work that was actually done and + approved. Never move it to `status/read` (that tag means something else). + +## Edge cases + +- **`Inbox` is intentionally tagged `distill`** so the user does not lose track + of it; it is a triage list, not knowledge. Ignore it: do not distill it and do + not remove its tag. +- More generally, **not everything tagged `distill` is promotable knowledge.** If + a tagged note holds none, say so and leave it (and its tag) alone rather than + forcing a Knowledge edit. +- If the queue is empty (or everything in it is excluded), say so. Do not invent + work. +- Never write to a Knowledge page or rewrite a note without explicit approval. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 120000 index 0000000..47dc3e3 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +AGENTS.md \ No newline at end of file