diff options
Diffstat (limited to 'claude-rules/todo-format.md')
| -rw-r--r-- | claude-rules/todo-format.md | 108 |
1 files changed, 107 insertions, 1 deletions
diff --git a/claude-rules/todo-format.md b/claude-rules/todo-format.md index 2cdc76c..8038b98 100644 --- a/claude-rules/todo-format.md +++ b/claude-rules/todo-format.md @@ -1,3 +1,8 @@ +--- +paths: + - "**/*.org" +--- + # Todo Entry Format Applies to: `**/*.org` (org-mode todo and inbox files) @@ -5,6 +10,56 @@ Applies to: `**/*.org` (org-mode todo and inbox files) How task entries are structured in org-mode todo files (`todo.org`, `inbox.org`, any GTD-style org file). Same shape across every project. +## Stamp `:LAST_REVIEWED:` when you create the task, not a cycle later + +Every task filed at `**` with a priority cookie carries a `:LAST_REVIEWED:` +property from the moment it's written: + +``` +** TODO [#B] Terse topic phrase :tag: +:PROPERTIES: +:LAST_REVIEWED: 2026-07-23 +:END: +Body. +``` + +Use today's date, from `date +%F`. The org-native `[YYYY-MM-DD Day]` form is +equally valid; both parse. + +**Why.** Writing a task *is* reviewing it. Whoever files it has just written +the body, chosen the wording, and graded the priority against the scheme — that +is the same judgment `task-review` applies, made with better context, because +the reason for the task is still in the room. Leaving the stamp off asserts the +opposite: `task-review-staleness.sh` sorts a missing property first, as +never-reviewed, so a task filed today arrives at the top of tomorrow's review +batch and gets "reviewed" by someone re-deriving what its author knew a day +earlier. That is ceremony, and ceremony teaches people to click through the +real thing. + +The concrete case: the 2026-07-23 sweep filed eight tasks in one night. Every +one landed unstamped, and the staleness count went 13 → 22 while the list got +*more* accurate, not less. The number stopped measuring drift and started +measuring recent activity. + +**This applies to every path that files a task**, not just the inbox: inbox +filing, triage intake, spec decomposition, task audit, a bug found mid-session, +a task you write by hand. If you wrote a task body today, stamp it today. + +**What it does not do.** The stamp never means "correct forever" — it means +"a person judged this on that date." A task filed today still enters the +review rotation on the normal cycle; it just enters it on the *next* cycle +rather than immediately. And it's a claim about a real event, so don't stamp +a task you didn't actually consider: a bulk import of someone else's list +is genuinely unreviewed, and stamping it would convert "nobody has read +these" into a false "reviewed today." + +**Enforcement.** `lint-org.el`'s `task-missing-last-reviewed` checker flags any +open `**` task with a priority cookie and no stamp, scoped to exactly the +headings `task-review-staleness.sh` selects, so the checker and the staleness +count never disagree. It's judgment-only and never auto-fixes: nothing can know +when an unstamped task was actually last considered, and writing today's date +onto an old one would destroy the very signal the property carries. + ## Priority and Tag Scheme Header Every project's `todo.org` opens with a top-level section named @@ -41,6 +96,9 @@ fixed definitions everywhere, because autonomous execution eligibility gate and trusts the author's tag rather than re-deriving autonomy at run time. +“Speedrunnable” is shorthand for `:solo:`. The `:quick:` tag plays no part +in that definition. + - **`:solo:` — autonomy.** The task can be completed *and verified* without Craig's involvement beyond at most one or two quick decisions that can be stated and answered before work starts. No open design question, no @@ -64,6 +122,49 @@ step** in the task-review and task-audit workflows, so the run-time gate can trust the tag. A review or audit that skips the `:solo:`/`:quick:` assessment is incomplete. +### Making an open-ended task measurable (so it can be `:solo:`) + +A task phrased as the *absence* of something — "find bugs until none are +visible," "refactor until no worthwhile opportunities remain," "clean this +up until it's good" — cannot be `:solo:`, because it fails the +*verifiable-by-the-agent* gate. Absence isn't falsifiable: an agent can +always look once more, so "done" is a judgment call, which is exactly what +`:solo:` forbids. The fix is not to drop the task but to give it an +objective completion criterion. Four moves convert a fuzzy goal into a +measurable one: + +1. **Bound the surface.** Enumerate the concrete units the task covers (the + N functions, the M files, the named code paths). The done-set is that + list, not the platonic set of all possible defects. Every claim is made + against the list, so "covered" is checkable where "found everything" + isn't. +2. **Net the behavior.** Bring the surface under characterization tests + (Normal/Boundary/Error per unit — see `testing.md`, and the + `testing-standards` skill for the characterization recipe) before changing + anything. This is the objective floor: writing a characterization test is + mechanical (record what the code does, not what it should), so it scales + across the surface, and it doubles as the safety net that makes any + later refactor falsifiable. +3. **Disposition every finding.** Run the relevant audits (a fixed + footgun/OWASP checklist, `/refactor`, `/review-code`) and give **every** + finding a verdict: fixed, filed as its own task, or declined with a + one-line reason. "Looked and it's fine" is not a disposition. The + measurement is zero undispositioned findings, not zero findings. +4. **Gate on an objective floor.** Static analysis clean (linter, + type-checker, `shellcheck`), the test suite green before and after, and + coverage of the enumerated surface (a per-unit test checklist, or a real + coverage number where the tooling exists). + +The **qualifying answer** is then a dispositioned report — surface split +covered/uncovered, tests before → after, static-analysis result, the audit +matrix fully dispositioned, all green — not a claim of perfection. The +honest limit stays honest ("no visible bugs" means "every enumerated path +passes its characterization set and clears the audit," never "zero bugs +exist"), but the criterion is now falsifiable, which is what lets the task +carry `:solo:`. Write these criteria into the task body at creation or +review time; a task that can't be given them stays non-`:solo:` until it +can. + ### Bug priority from severity × frequency (mandatory where a codebase exists) Some projects carry a codebase — source the project maintains under version @@ -217,6 +318,7 @@ A completed sub-task disappears as a task and becomes an in-place event-log entr 2. Generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`. 3. Reword the original imperative title into the past-tense action that landed. Trim or restate if the original wording doesn't fit the action. 4. Drop the `TODO`/`DOING` keyword, the priority cookie, and the tags. The body stays as the record of what was done (if useful). +5. Remove any `SCHEDULED:`/`DEADLINE:` planning line. The completion time lives in the heading now, so `CLOSED:` is redundant and an active planning date on a historical log entry is always wrong. Org renders any headline carrying an active `<...>` `SCHEDULED`/`DEADLINE` on the agenda, keyword or not, so a stale one pins the finished entry there as weeks-overdue forever. An interactive close (`org-log-done`) stamps `CLOSED:` but never strips a pre-existing planning line, which is exactly how the stale dates survive. **Example:** @@ -226,7 +328,7 @@ becomes *** 2026-05-15 Fri @ 12:58:08 -0500 Wired yasnippet for universal availability -**Enforcement.** This is applied at close time by whoever closes the task, but an interactive org close (`org-log-done` flips the keyword to `DONE` and stamps `CLOSED:`) never applies the dated rewrite, so level-3+ closes accumulate as `DONE` keywords. `todo-cleanup.el --convert-subtasks` (run in the `clean-todo` and wrap-up cleanup passes) normalizes them mechanically: it rewrites any level-3+ `DONE`/`CANCELLED`/`FAILED` heading into the dated form above, pulling the timestamp from the `CLOSED` cookie and keeping the heading text verbatim (a batch tool can't reliably past-tense a title — polish wording by hand where it matters). `lint-org.el` flags any that slip through (checker `subtask-done-not-dated`). So the depth rule holds even when tasks are closed interactively rather than by an agent applying this section. +**Enforcement.** This is applied at close time by whoever closes the task, but an interactive org close (`org-log-done` flips the keyword to `DONE` and stamps `CLOSED:`) never applies the dated rewrite, so level-3+ closes accumulate as `DONE` keywords. `todo-cleanup.el --convert-subtasks` (run in the `clean-todo` and wrap-up cleanup passes) normalizes them mechanically: it rewrites any level-3+ `DONE`/`CANCELLED`/`FAILED` heading into the dated form above, pulling the timestamp from the `CLOSED` cookie, dropping the whole planning line (`CLOSED`, `SCHEDULED`, and `DEADLINE` together — step 5), and keeping the heading text verbatim (a batch tool can't reliably past-tense a title — polish wording by hand where it matters). `lint-org.el` flags any that slip through: checker `subtask-done-not-dated` for a still-keyworded sub-task, and `dated-log-heading-active-timestamp` for a dated entry that kept an active `SCHEDULED`/`DEADLINE`. So the depth rule holds even when tasks are closed interactively rather than by an agent applying this section. ### Why depth-based @@ -309,6 +411,10 @@ tasks — dated entries at `***` and deeper, terminal keyword at `**`. *** 2026-05-15 Fri @ 14:00:00 -0500 <what was answered or done> Generate the timestamp with `date "+%Y-%m-%d %a @ %H:%M:%S %z"`. + Remove any `SCHEDULED:`/`DEADLINE:` planning line too, same as the + sub-task rule above — a dated event-log entry carries its date in the + heading, and an active planning date left on it pins the finished entry + to the agenda forever. - **At `**` — terminal keyword, like any top-level task.** Change `VERIFY` to `DONE` (answered / check passed) or `CANCELLED` (abandoned), |
