aboutsummaryrefslogtreecommitdiff
path: root/docs/design/2026-06-29-todo-cleanup-aging-proposal.org
blob: 5a18990867f3d69f1b62dc728f36d6fd53132888 (plain)
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
#+TITLE: todo-cleanup.el — add Resolved-section file-aging to --archive-done

* What changed (from .emacs.d, 2026-06-29)

Extended =todo-cleanup.el='s =--archive-done= mode (the =make task-sorted=
target) with a SECOND step, run after the existing Open Work -> Resolved move:

- *Age the Resolved section.* Level-2 DONE/CANCELLED subtrees whose CLOSED date
  is older than =tc-archive-retain-days= (default 7) — AND any with no parseable
  CLOSED date — move out of the in-file Resolved section to =tc-archive-file=
  (default =archive/task-archive.org= beside the todo file). Only tasks closed
  within the last week stay in todo.org itself.

Two files are attached (the edited canonical candidates):
- =todo-cleanup.el=
- =tests/test-todo-cleanup.el=

* Why

Craig's .emacs.d todo.org had grown to 768KB / 9616 lines, ~44% of it a
243-task in-file "Resolved" section. The existing =--archive-done= only moved
closures Open Work -> Resolved (same file), so the file grew without bound. The
new step keeps only the last week of closed tasks in the file and sheds the rest
to a git-tracked archive sibling. After this run: 207 aged out, todo.org
9616 -> 5625 lines.

* Design notes for the canonical

- New defvars: =tc-archive-retain-days= (7; nil disables the step, preserving
  legacy in-file-only behavior), =tc-archive-reference-date= ((YEAR MONTH DAY),
  nil=real today — mockable for deterministic tests), =tc-archive-file= (nil =>
  =archive/task-archive.org= beside the todo file).
- Policy: KEEP iff CLOSED date present AND within the window (cutoff inclusive).
  Older OR undated => archive. The undated->archive call is deliberate ("keep
  the last week and that's it"); an earlier undated->keep version left 14 legacy
  undated tasks behind and read as two weeks.
- The aging step honors =--check= (previews + reports, writes nothing).
- Report: an additive "N aged subtree(s) moved to task-archive.org" line, only
  when N>0, so the existing real-mode-no-op silence tests are unaffected.
- Archive file scaffold: =#+TITLE: Task Archive= / =#+FILETAGS: :archive:= /
  =* Resolved (archived)=; aged subtrees append as level-2 children; created on
  first use, appended to thereafter (one scaffold, never duplicated).
- Tests: =tc-test--reset= now sets the aging knobs OFF (retain nil) so the
  existing in-file-move + sync tests are untouched by the wall clock; a new
  =tc-test--age= harness re-enables them with a fixed reference date and a temp
  archive file. 6 new tests (old+undated move, cutoff-inclusive stay, disabled,
  idempotent, check-no-write, straggler pipeline, append-preserves). 34/34 green.

* Cross-project consideration for your value gate

Default is ON (retain 7) for ALL consuming projects once this syncs. A project's
first =task-sorted= after the sync will shed everything in its Resolved section
older than a week to a new =archive/task-archive.org=. That's the intended
feature, but flag it — projects with a large historical Resolved section will see
a big first-run move (git-tracked, recoverable). Adjust the default or gate it if
you'd rather it be opt-in per project.

* Companion (project-local, NOT synced)

.emacs.d's Makefile =task-sorted= target now also runs =lint-org.el todo.org=
after the archive, as a structural-safety pass (org-lint catches links/drawers/
blocks; we separately verified heading-level structure by hand). Makefiles aren't
template-synced, so this is per-project — noting it in case the pattern is worth
documenting alongside the tool.