Everyone agrees you should do weekly reviews. Reflect on the week, consolidate your notes, track progress against goals. GTD says so. Every productivity system says so. You nod, you agree, you don’t do it.
I didn’t either. For months. I had a daily log in Org-mode, hundreds of entries with timestamps and tags, and no system to turn that raw material into anything useful. The daily entries accumulated. The weekly review kept getting pushed to “next Monday.” Quarterly reviews? I couldn’t even tell you what I did last quarter.
The problem isn’t discipline. The problem is that sitting down to review a week of entries — scanning through seven days of headings, figuring out what mattered, writing a summary from scratch — is genuinely tedious. If the review process itself is painful, you won’t do it. No amount of habit-tracking apps will fix that.
So I built a system. Progressive summarization, powered by Org-mode tools that do the tedious parts for you. Daily entries get filtered into weekly highlights. Weekly highlights get cherry-picked into quarterly milestones. Each level compresses the one below it. By the time you sit down for a quarterly review, 90% of the work is already done.
The core idea: progressive summarization
The insight is simple: don’t write reviews. Compress them.
Instead of sitting down on Friday and trying to reconstruct your week from memory, you do a tiny amount of work each day — tag your highlights as they happen — and then the weekly review is just filtering and condensing what’s already marked.
Four levels:
| Level | Cadence | What it captures | Effort |
|---|---|---|---|
| Daily log | As it happens | Everything — tasks, notes, events, with tags | ~0 (you’re already logging) |
| Weekly summary | End of week | 1-sentence per goal, cherry-picked from daily | ~15 min |
| Quarterly milestones | End of quarter | Key achievements, metrics, patterns | ~30 min |
| Yearly review | End of year | Themes and trajectory from quarterly notes | ~1 hour |
Each level reads from the one below it. You never summarize from scratch. The daily log is the raw material, and each subsequent level is just a filter pass.
The trick that makes this work: tag your highlights in real time. When something notable happens during the day — a win, a lesson, a setback — tag it with :HL: (highlight). When you sit down for the weekly review, you don’t scan seven days of entries. You filter for :HL: and only look at what you already marked as interesting.
The structure
Here’s what the actual Org file looks like. I use a single file per year, with nested headings for time periods:
* 2024
** 2024-W15
*** Plan
**** [#A] English: improve speaking fluency [0/2]
***** TODO [#A] 7h practice conversation with GPT
***** TODO [#B] 7h review and enrich cheat sheet
**** [#B] Health: maintain workout streak [0/1]
***** TODO [#A] 4 gym sessions this week
*** Tools for Review
**** clocktable (properties)
**** clocktable (time by tag)
*** Mon <2024-04-08 Mon>
**** 09:00 standup :1work:
**** 14:00 deep work on migration PR :1work:HL:
**** 19:00 gym - chest day :1heal:
*** Tue <2024-04-09 Tue>
**** ...
** 2024-W15 SCP
*** Report
*** Summary
*** Plan (next week)
The key structural choices:
- Level 2 = time period (week, month, quarter)
- Level 3 = plan (with goals), tools (clocktables), or daily entries
- Level 4 = categories within the plan, or individual events within a day
- Level 5 = specific items with priority tags
The SCP sibling heading (Summary, Cherry-pick, Plan) is where the compressed output goes. The weekly review means looking at the raw week on the left and writing into the SCP on the right. That’s the whole process.
Tool 1: clocktables
Clocktables are Org’s built-in time reporting feature. If you clock in and out of tasks (C-c C-x C-i / C-c C-x C-o), clocktables aggregate that data into tables.
Even if you don’t clock religiously, clocktables are useful for reviews because they can also pull in headline properties — weight, sleep hours, workout status, whatever you track.
Here’s a clocktable that pulls properties from a specific week:
#+BEGIN: clocktable :scope file :block 2024-W15 :maxlevel 3 :link t
:compact t :narrow 15! :indent t
:properties ("weight" "diet" "sleepd" "sleept" "workout")
#+END:
Put your cursor on it and hit C-c C-c. Org generates a table with your properties for that week’s entries.
And here’s one filtered by tags — show me only entries tagged both :HL: and :1heal::
#+BEGIN: clocktable :scope file :block 2024-W15 :maxlevel 5
:tags t :link t :compact t :match "HL+1heal"
#+END:
The parameters that matter:
| Parameter | What it does | Example |
|---|---|---|
:scope file |
Search the whole file | :scope file |
:block 2024-W15 |
Limit to ISO week 15 | :block 2024-W15 |
:maxlevel 3 |
Show headings up to depth 3 | :maxlevel 5 |
:match "HL+1heal" |
Filter by tag expression | :match "HL" |
:tags t |
Show tags column in output | :tags t |
:properties (...) |
Include named properties | :properties ("weight") |
:link t |
Make headings clickable | :link t |
:step week |
Break results by week | :step week |
The :match parameter uses the same syntax as Org agenda tag matching. + means AND, | means OR, - means NOT. So "HL+1work" means “tagged both :HL: and :1work:.”
You can have multiple clocktables in the “Tools for Review” section of your weekly structure — one for health metrics, one for work highlights, one for time allocation. They auto-update on C-c C-c. Zero manual work.
Tool 2: sparse trees
Sparse trees are Org’s way of filtering a buffer in-place. Instead of creating a separate view, they fold everything except matching entries. You see your matches in their original context, with the rest of the document collapsed.
The built-in command is C-c / m — it prompts for a tag and folds the buffer to show only matching headings. Quick, but limited: it only does single-tag matching.
For more expressive filtering, you can write a small helper. I use one called zz/org-tags-sparse-tree that supports AND/OR logic:
(defun zz/org-tags-sparse-tree ()
"Create a sparse tree matching a tag expression.
Supports full Org tag match syntax: + (AND), | (OR), - (NOT)."
(interactive)
(let ((match (read-string "Match: ")))
(org-match-sparse-tree nil match)))
Bind it to something convenient (Fn+5 or whatever your muscle memory likes), and now you can do HL+1work to see all work highlights, or HL|LL to see both highlights and low-lights.
The visual result: your entire weekly log collapses, leaving only the tagged entries visible. You scan them, pick the important ones, and write one-liners into the weekly summary. The rest stays folded — out of sight, not deleted, just not relevant right now.
Sparse trees are great for the audit step. You’re not context-switching to a different buffer. You’re looking at your actual notes, filtered. It’s like grep for your Org file, but structure-aware.
Tool 3: org-ql
org-ql is the query language Org-mode deserves. If clocktables are “show me time data” and sparse trees are “filter this buffer,” org-ql is “ask arbitrary questions across all your Org files.”
Install it from MELPA, then:
;; Find all highlights tagged :1heal: across agenda files
(org-ql-select (org-agenda-files)
'(and (tags "HL")
(tags "1heal"))
:action (lambda ()
(message (org-get-heading t t t t))))
This searches all your agenda files for entries tagged both :HL: and :1heal:, and prints each headline. You can also use org-ql-search for an interactive buffer:
;; Interactive search --- opens results in a buffer you can browse
(org-ql-search (org-agenda-files)
'(and (tags "HL")
(ts :from "2024-04-08" :to "2024-04-14")))
The query language is Lisp-based but readable:
| Query | Meaning |
|---|---|
(tags "HL") |
Has the :HL: tag |
(todo "TODO") |
Is a TODO item |
(ts :from "2024-04-01") |
Has a timestamp after April 1 |
(and ...) |
All conditions must match |
(or ...) |
Any condition can match |
(priority "A") |
Priority is A |
(heading "meeting") |
Heading contains “meeting” |
For reviews, org-ql shines when you’re asking cross-file questions: “What were all my health highlights this quarter?” or “How many A-priority tasks did I complete in March?” Things that would be painful to do manually across multiple weekly sections.
I’ll be honest — I haven’t fully integrated org-ql into my review workflow yet. Clocktables and sparse trees handle the weekly review. Org-ql is where I want to get for monthly and quarterly reviews, where you’re pulling patterns across weeks.
Tool 4: indirect buffers
This is the workflow glue that ties everything together.
An indirect buffer is a second view into the same buffer. Same underlying data, different point position, different narrowing. Edit in one, it changes in the other. It’s like having two windows open to the same file, except each window can be narrowed to a different section.
Here’s why this matters for reviews. Your weekly review involves two things:
- Reading the daily entries (source material)
- Writing the weekly summary (compressed output)
Both live in the same file. Without indirect buffers, you’re scrolling up and down constantly — read an entry, scroll to the summary section, type a line, scroll back. It’s miserable.
With indirect buffers:
- Navigate to your weekly plan heading
C-x 4 c(clone-indirect-buffer-other-window) — or a custom command likez nif you bindorg-tree-to-indirect-buffer- Now you have two windows: one narrowed to the plan/summary, one showing the full file
- In the full-file window, run a sparse tree filter (
C-c / mfor:HL:) - Audit: scan filtered entries on the right, type summaries on the left
Two side-by-side views of the same data. One filtered for reading, one narrowed for writing. This is the actual ergonomics that make weekly reviews not terrible.
+---------------------------+---------------------------+
| Buffer A (indirect) | Buffer B (original) |
| Narrowed to: W15 Plan | Sparse tree: :HL: tag |
| | |
| **** English [0/2] | *** Mon |
| - 0408 practiced w/ GPT | **** deep work on PR HL|
| - 0410 chsh review done | **** ... |
| **** Health [0/1] | *** Wed |
| - 0409 gym chest day | **** gym chest day HL|
| - 0411 gym leg day | *** Fri |
| | **** ... HL|
+---------------------------+---------------------------+
The weekly review workflow
Here’s the actual procedure, start to finish. Takes about 15 minutes once you’ve done it a few times.
Setup (one-time):
- Structure your Org file with the heading hierarchy described above
- Tag entries with
:HL:as they happen during the day - Have clocktables configured in the “Tools for Review” section
The review (every week):
- Open your Org file, navigate to this week’s heading
- Update clocktables — cursor on each
#+BEGIN: clocktable, hitC-c C-c. Tables regenerate with this week’s data. Glance at the numbers. - Create an indirect buffer for the weekly plan: navigate to the
Planheading,C-x 4 c(orz nif you’ve bound it). This window is where you’ll write. - In the other window, filter for highlights:
C-c / mthen typeHL. The buffer collapses to show only highlighted entries. - Audit: scan the highlighted entries day by day. For each one, write a one-liner under the appropriate goal in the plan buffer. Include the date shorthand:
0408 practiced conversation with GPT; 0410 reviewed cheat sheet. - Rate your goals: after writing summaries, mark each goal’s completion.
[2/2]or[1/2]. - Cherry-pick into quarterly: if anything this week was a genuine milestone — not just a task completed, but something you’d want to remember in three months — copy that one-liner into the quarterly tracking section.
Step 5 is where the time savings compound. You’re not reading everything from the week. You’re reading only what past-you already flagged as important. The :HL: tag is past-you doing a favor for future-you.
Exporting reviews
Sometimes you need the review outside of Emacs — sharing with a manager, pasting into a doc, posting somewhere.
Org’s export system handles this. Navigate to the subtree you want to export, then:
C-c C-e (org-export-dispatch)
→ C-s (set scope to subtree)
→ choose format (HTML, Markdown, plain text, etc.)
Two settings that save headaches:
;; Don't choke on broken internal links
(setq org-export-with-broken-links t)
;; Drop section numbers --- they look weird for review documents
(setq org-export-with-section-numbers nil)
The broken links thing is important. Your Org file probably has id: links to other files, Roam-style links, and other references that won’t resolve in the export context. Without that setting, export will error out on the first broken link and you’ll spend five minutes fixing references instead of sharing your review.
Making it stick
The reason this system works — for me, at least — is that the hard part isn’t the review itself. The hard part was always the setup cost of sitting down to review. Open the file, figure out where to look, start reading from Monday, get distracted by a thing from Tuesday, lose the thread, give up.
The tools eliminate the setup cost:
- Clocktables give you the numbers without manual counting
- Sparse trees collapse everything except what matters
- Org-ql answers specific questions across files
- Indirect buffers let you read and write side by side
And the progressive structure — daily tags to weekly summaries to quarterly milestones — means you’re never doing more than one level of compression at a time. The weekly review isn’t “summarize your whole week.” It’s “look at the things you already highlighted and write one sentence each.” That’s a 15-minute task, not a 90-minute existential crisis.
The quarterly review, when it comes, is just reading 12-13 weekly summaries and picking the highlights. The yearly review is reading four quarterly summaries. Each level is a compression pass on the previous one. You never start from raw data.
Tag your highlights today. Review them on Friday. That’s it.