1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
#+TITLE: Clean-Todo Workflow
#+AUTHOR: Craig Jennings
#+DATE: 2026-05-11
* Overview
On-demand cleanup of the project's =todo.org=: run the hygiene pass, then archive completed work, then report what changed. The wrap-up workflow already does both passes at session end; this workflow is the manual entry point — invoke it any time the todo file needs a tidy, without waiting for a wrap-up.
* When to Use This Workflow
When Craig says:
- "clean up todo.org" / "clean-todo" / "tidy the todo file"
- "archive the done items in todo.org"
- "run the todo cleanup"
Requires a =todo.org= at the project root. If there isn't one, say so and stop.
* The Workflow
** Step 1: Hygiene pass
#+begin_src bash
emacs --batch -q -l .ai/scripts/todo-cleanup.el todo.org
#+end_src
Deletes bogus =- State "X" from "X" [date]= log lines (state didn't actually change — these wedge between the heading and =DEADLINE:=/=SCHEDULED:= and break agenda parsing) and reports "orphan planning lines" (a body =DEADLINE:=/=SCHEDULED:= that =org-entry-get= can't read because it's out of canonical position — not auto-rewritten; surface for manual fix). Fast and idempotent. Capture the output.
To preview without writing, run =--check= first: =emacs --batch -q -l .ai/scripts/todo-cleanup.el --check todo.org=.
** Step 2: Convert done sub-tasks to dated entries
#+begin_src bash
emacs --batch -q -l .ai/scripts/todo-cleanup.el --convert-subtasks todo.org
#+end_src
Rewrites every heading at level 3 or deeper whose TODO state is DONE/CANCELLED/FAILED into a dated event-log entry (=<stars> YYYY-MM-DD Day @ HH:MM:SS -ZZZZ <text>=), dropping the keyword, priority cookie, and tags, and removing the =CLOSED:= line. Enforces the depth rule that a completed sub-task becomes dated history — a shape interactive org closes and =--archive-done= (level-2 only) leave unapplied. Timestamp comes from each entry's =CLOSED= cookie; heading text kept verbatim; idempotent; a done sub-task with no parseable =CLOSED= is flagged and left alone. Run before archiving so a parent's sub-tasks are already dated when it moves. Capture the output.
To preview without writing: =emacs --batch -q -l .ai/scripts/todo-cleanup.el --convert-subtasks --check todo.org=.
** Step 3: Archive completed work
#+begin_src bash
emacs --batch -q -l .ai/scripts/todo-cleanup.el --archive-done todo.org
#+end_src
Moves every level-2 subtree whose TODO state is DONE or CANCELLED out of the "Open Work" section into the "Resolved" section, subtree intact. The two sections are matched by a unique level-1 heading containing "Open Work" (case-insensitive) and one containing "Resolved" — if either is missing or ambiguous, the file is skipped with a message, no crash. Only direct level-2 children move; a DONE entry nested under an open parent stays put. Idempotent. Capture the output.
To preview the moves without writing: =emacs --batch -q -l .ai/scripts/todo-cleanup.el --archive-done --check todo.org=.
** Step 4: Summarize
Report to Craig from the three captured outputs:
- Hygiene: how many bogus state-log lines were deleted; any orphan-planning warnings (file:line + heading), or "none".
- Convert: how many done sub-tasks were rewritten to dated entries (heading + line), any flagged for no =CLOSED= date, or "nothing to convert".
- Archive: how many subtrees moved and which (heading + line), or "nothing to move" / the skip reason if a section was missing or ambiguous.
- If the file changed, note that =todo.org= now has an uncommitted edit — review =git diff -- todo.org= and commit it (in this repo's commit style) if it looks right. If nothing changed, say so and stop.
Don't auto-commit. The summary is the review point; Craig decides whether the diff goes in.
* Principles
- *Both passes apply, not just preview.* The workflow is invoked because cleanup is wanted. Use the =--check= variants only when Craig asks for a dry run.
- *Separate modes, separate invocations.* =--convert-subtasks=, =--archive-done=, and the hygiene pass are each their own mode and don't run the others; run all three.
- *Never auto-commit todo.org.* Surface the diff and let Craig commit it. The cleanup is a working-tree change, fully reversible until committed.
- *Trust the script.* It's fast and idempotent; if there's nothing to do, it reports zero and exits clean. No pre-checks.
* Living Document
Update this workflow if =todo-cleanup.el= grows new modes or the section-matching rules change.
|