--- 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, and record **where** the note lives, because location signals whether the ticket is finished: - A note in the **top-level `Work / Story Logs`** notebook is an **active** ticket: still in flight, not finished. - A note in an **archive** subnotebook (`Work / Story Logs / Archive / `) is a **finished** ticket: the user archives a story log once its work is done. 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. Use the story note's location (from step 2) to say whether the ticket finished: a ticket whose note has moved to an archive subnotebook is done (say it landed / merged / closed); one whose note is still in the top-level `Story Logs` is still active, so do not imply it is finished. - 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. The story note's location corroborates this: a note still in the top-level `Story Logs` confirms the ticket is active and a real carry-over candidate, while one already moved to an archive subnotebook is finished and should not be listed as a loose end. - **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.