#+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.