<feed xmlns='http://www.w3.org/2005/Atom'>
<title>rulesets/claude-templates/.ai/scripts/todo-cleanup.el, branch main</title>
<subtitle>Claude Code skills, rules, and language bundles
</subtitle>
<id>https://git.cjennings.net/rulesets/atom?h=main</id>
<link rel='self' href='https://git.cjennings.net/rulesets/atom?h=main'/>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/'/>
<updated>2026-07-29T00:53:45+00:00</updated>
<entry>
<title>refactor(sentry): call one loop cycle a cycle, not a fire</title>
<updated>2026-07-29T00:53:45+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-29T00:53:45+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=f3f5bfd651962934b230785d5a3449b738ce44ed'/>
<id>urn:sha1:f3f5bfd651962934b230785d5a3449b738ce44ed</id>
<content type='text'>
I read "nine fires" as nine emergencies and went looking for what was burning. The word reaches me directly, since digest headings render as Fire 11 in the anchor I read every morning.

72 noun-sense instances in sentry.org become "cycle". The four verb-sense uses stay, because firing is the right word for a trigger.

home proposed "pass" and I turned it down. sentry.org already uses that as a numbered noun for the eleven hygiene passes, so "Pass 11" would have collided with a referent in the same file. home confirmed it by walking into exactly that: its anchor ended up carrying "Pass 11" three lines from "pass 12". "Cycle" appears nowhere in the file, and it is the word I reached for myself when I flagged the problem.

Three more instances lived outside sentry.org: wrap-it-up.org, todo-cleanup.el, and its test. All three named a sentry cycle, so they move too and the vocabulary stays consistent across files.
</content>
</entry>
<entry>
<title>fix: harden the org-file mutators and close four verified bugs</title>
<updated>2026-07-24T15:56:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-24T15:56:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=267d1de7b8a8e7fd22b156433567c88216ec3d0f'/>
<id>urn:sha1:267d1de7b8a8e7fd22b156433567c88216ec3d0f</id>
<content type='text'>
An overnight hygiene run over this repo, reviewed adversarially afterward. Every problem below was reproduced before it was fixed, and every fix was re-checked by a skeptic who was told to refute it.

cj-remove-block could silently destroy content. Its range check validated only the first and last lines, so a span from one cj block's opener to a later block's closer passed and the removal deleted everything between — prose, headings, whole tasks, with a zero exit. That is the failure the check exists to prevent, and drift is its normal case, since the calling skill edits the file while processing. It now refuses a range holding more than one block, backs up first, and writes atomically while preserving the file's mode and following symlinks to the real target.

todo-cleanup rewrote todo.org with no backup, alone among the three tools that mutate these files. It now copies to the temp dir like lint-org does. Both backup helpers suffix rather than overwrite, because a second-resolution stamp collapsed two back-to-back invocations into one backup holding already-mutated content — and running todo-cleanup twice in a row is the shipped path.

audit.bats failed about one run in eight, in teardown rather than in its assertions, so a passing test reported as a false red. git commit spawns a detached maintenance process that is still writing packs when the commit returns, racing the cleanup. The fixture disables it.

route_recommend downgraded a correct strong match to weak when two projects shared a basename. lint.sh now sweeps the scripts that get symlinked onto PATH, which were the only shell in the repo with no gate over them.

Two test defects found in review and fixed here: a suite that deleted real backups from the shared temp dir, and one that depended on that directory's contents. Isolation now lives in an autouse fixture rather than in each test's memory.

Known and filed, not closed: the range check still cannot prove the range is the block that was scanned, so drift by exactly one whole block deletes the wrong annotation. That needs a content assertion and a CLI contract change.
</content>
</entry>
<entry>
<title>feat(todo-cleanup): dated-seal archiving and planning-line strip on convert</title>
<updated>2026-07-19T02:03:50+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-19T02:03:50+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=ddbd47fbbea06e71456b7bc4228626d3a126bf28'/>
<id>urn:sha1:ddbd47fbbea06e71456b7bc4228626d3a126bf28</id>
<content type='text'>
Two batched changes to the todo hygiene tooling.

Dated-seal archiving: --archive-done aging now keeps one month in-file (retain default 7 → 31, named as tc-archive-retain-days-default) and a new --seal renames the working task-archive.org to resolved-YYYY-MM-DD.org beside it, letting the next aging start a fresh working file. Dating the sealed file by the seal run rather than the calendar quarter is what avoids mislabeling a task closed late in a quarter and archived after the boundary. The unparseable-CLOSED case is now explicit in the contract: a keyword-complete task with no readable close date ages out too. The sealed file inherits the todo file's gitignore status.

Planning-line strip: --convert-subtasks now drops the whole planning line when it rewrites a done sub-task to a dated event-log entry, not just the CLOSED cookie. A dated-log heading carries its date in the heading, so an active SCHEDULED/DEADLINE left on it pins the finished entry to the agenda as weeks-overdue forever (org renders any active planning timestamp, keyword or not). New lint-org checker dated-log-heading-active-timestamp backstops any that predate this. todo-format.md's sub-task and VERIFY completion rules gain the strip step.
</content>
</entry>
<entry>
<title>feat(todo-cleanup): add --convert-subtasks dated-rewrite mode</title>
<updated>2026-07-02T01:35:16+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-07-02T01:35:16+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=19ba7cb40c5a448bb28f0217d8cc4718dd450f91'/>
<id>urn:sha1:19ba7cb40c5a448bb28f0217d8cc4718dd450f91</id>
<content type='text'>
Rewrites every level-3+ DONE/CANCELLED/FAILED heading into a dated event-log entry from its CLOSED cookie, enforcing the todo-format depth rule that interactive closes and --archive-done (level-2 only) leave unapplied. A new lint-org checker (subtask-done-not-dated) flags stragglers, and the clean-todo, wrap-up, open-tasks, and task-review workflows now run the converter before archiving.

Removing the CLOSED cookie keeps a DEADLINE or SCHEDULED cookie that shares its planning line, rather than dropping the whole line.

From the .emacs.d handoff (2026-07-01 convert-subtasks bundle).
</content>
</entry>
<entry>
<title>feat(todo-cleanup): age the Resolved section out to a tracked archive</title>
<updated>2026-06-30T17:43:25+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-30T17:43:25+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=f67e72430845236ab5ed4ca00ba13afe87eda53a'/>
<id>urn:sha1:f67e72430845236ab5ed4ca00ba13afe87eda53a</id>
<content type='text'>
A project's in-file Resolved section grew without bound: --archive-done only moved closures from Open Work into Resolved, same file. .emacs.d's todo.org reached 768KB that way. So --archive-done now has a second step: a Resolved subtree closed longer ago than tc-archive-retain-days (default 7), or with no parseable CLOSED date, moves out to archive/task-archive.org beside the todo file. Only the last week of closures stays in the file. The step honors --check, and tc-archive-retain-days nil disables it.

I added a guard the proposal lacked: the archive inherits the todo file's gitignore status. gitignore-mode projects gitignore todo.org, so without this the aging step would shed previously-private task history into a tracked archive, a leak on any public repo. When the todo file is gitignored and the archive isn't, the script adds the archive path to .gitignore before the first write. Track-mode projects leave both tracked. Covered by two tests in a temp git repo, one per branch.
</content>
</entry>
<entry>
<title>fix(todo-cleanup): keep --archive-done silent on a real-mode no-op</title>
<updated>2026-06-12T20:26:22+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-06-12T20:26:22+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=21ab10ef9e2fff9b4726695036e95e33937e1fa9'/>
<id>urn:sha1:21ab10ef9e2fff9b4726695036e95e33937e1fa9</id>
<content type='text'>
The wrap runs --archive-done twice (wrap-it-up, then open-tasks.org Phase A). The first pass archives and reports. The second finds nothing and used to print "0 subtree(s) moved", which reads as alarming next to the first pass's diff. Now a real-mode run that moves nothing and skips nothing says nothing. Check mode still previews "0 would move", and a missing-section skip still reports, since those are conditions the caller needs.
</content>
</entry>
<entry>
<title>Merge commit '69c5e4ace81586c05dea6a9a3afd54dafa61a73b' as 'claude-templates'</title>
<updated>2026-05-15T21:56:39+00:00</updated>
<author>
<name>Craig Jennings</name>
<email>c@cjennings.net</email>
</author>
<published>2026-05-15T21:56:39+00:00</published>
<link rel='alternate' type='text/html' href='https://git.cjennings.net/rulesets/commit/?id=c1d4e3c4a42abd01bc7ef83b1d6ae036ee32ef1d'/>
<id>urn:sha1:c1d4e3c4a42abd01bc7ef83b1d6ae036ee32ef1d</id>
<content type='text'>
</content>
</entry>
</feed>
