aboutsummaryrefslogtreecommitdiff
path: root/.ai/workflows
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-05-20 15:06:17 -0400
committerCraig Jennings <c@cjennings.net>2026-05-20 15:06:17 -0400
commite9bd073b8133c50a2df425196e32fdf3f5c4c2bd (patch)
treeace469011d84044a78f93a2e6883de673b9a198d /.ai/workflows
parent49898a8c364430abf792567d2a51ac09db97a94f (diff)
downloadrulesets-e9bd073b8133c50a2df425196e32fdf3f5c4c2bd.tar.gz
rulesets-e9bd073b8133c50a2df425196e32fdf3f5c4c2bd.zip
feat(workflows): add task-review list-hygiene habit
The new task-review.org workflow is the daily habit that retires the old date-coverage scan. It surfaces the oldest-unreviewed top-level tasks, walks them one at a time, and records each outcome — keep, re-grade, kill, mark DOING, or edit — stamping :LAST_REVIEWED: as it goes. It's a pure Claude workflow, no elisp. open-tasks.org displays the list; this one changes it. task-review-staleness.sh gains a --list mode that emits the N oldest-unreviewed tasks (line, review date, heading), oldest first, so the workflow walks a deterministic batch instead of eyeballing todo.org. Never-reviewed and unparseable-date tasks sort oldest. Seven new bats cases cover ordering, the count limit, exclusions, and output format; count mode is unchanged. startup.org gains the matching nudge. Phase A counts tasks unreviewed for >7 days and Phase C surfaces one line when that count is non-zero, pointing at the workflow. It lives in the template startup.org rather than the project-only startup-extras layer, so every project picks it up the same way it picks up the wrap-up health check. The INDEX entry is added with the "task review" triggers the rename freed up.
Diffstat (limited to '.ai/workflows')
-rw-r--r--.ai/workflows/INDEX.org2
-rw-r--r--.ai/workflows/startup.org2
-rw-r--r--.ai/workflows/task-review.org97
3 files changed, 101 insertions, 0 deletions
diff --git a/.ai/workflows/INDEX.org b/.ai/workflows/INDEX.org
index 5a68765..7e65763 100644
--- a/.ai/workflows/INDEX.org
+++ b/.ai/workflows/INDEX.org
@@ -26,6 +26,8 @@ This index must list every =.org= file in =.ai/workflows/= except this one. Star
- =open-tasks.org= — list all open tasks (list mode) or pick the next task (next mode).
- Triggers: "what's next", "what should I work on", "list open tasks", "show me all tasks", "what's on my plate", "show me my tasks", "I need a recommendation"
+- =task-review.org= — daily list-hygiene habit: walk the oldest-unreviewed top-level tasks, re-grade / kill / keep / mark DOING, and stamp =:LAST_REVIEWED:=. Distinct from =open-tasks.org=, which only displays.
+ - Triggers: "task review", "review tasks", "let's do a task review", "groom my tasks", "task-review"
- =daily-prep.org= — prep brief for the next workday. Two modes: full-prep (default) or standup-only.
- Full-prep triggers: "let's prep for tomorrow", "daily prep"
- Standup-only triggers: "what's my standup report", "let's do the daily standup report", "give me the standup brief"
diff --git a/.ai/workflows/startup.org b/.ai/workflows/startup.org
index fabd607..3503a60 100644
--- a/.ai/workflows/startup.org
+++ b/.ai/workflows/startup.org
@@ -103,6 +103,7 @@ These calls have no dependencies on each other. Issue them all together in one m
8. =cross-agent-status 2>/dev/null || true= — snapshot of pending cross-agent messages across local projects. This is layer A of the cold-start design from =cross-agent-comms.org=: pending messages from other agents (delivered while no session was active here) get surfaced on session start. The =|| true= keeps Phase A from failing if =cross-agent-status= isn't installed yet — older projects without the script still boot cleanly. If HALT is active, =cross-agent-status= prints a banner; surface that prominently in Phase C.
9. Read =.ai/notes.org= — Project-Specific Context, Active Reminders, Pending Decisions sections (skip About This File).
10. Read =.ai/project-workflows/startup-extras.org= if it exists.
+11. =[ -f todo.org ] && .ai/scripts/task-review-staleness.sh todo.org 7 || true= — count top-level tasks overdue for review (the daily task-review habit's startup nudge). The =[ -f todo.org ]= guard skips projects without a root todo.org; =|| true= keeps Phase A from failing if the script isn't synced yet. Threshold 7 days is one review cycle of slack — softer than the wrap-up health check's 30-day alarm.
Notes on the rsync commands:
- Trailing slashes on both source and destination matter — they tell rsync to sync /contents/ rather than nest a directory inside.
@@ -131,6 +132,7 @@ This phase touches the user and runs sequentially:
- Surface Active Reminders from notes.org immediately.
- Mention Pending Decisions from notes.org.
- Briefly note significant template updates noticed during sync (new workflows, protocol changes).
+ - *Task-review nudge.* If the Phase A staleness count (step 11) is greater than zero, surface one line: "=<N>= top-level tasks unreviewed for >7 days — say 'let's do a task review' to run a cycle." If zero, say nothing.
- *Surface pending cross-agent messages.* If =cross-agent-status= reported any pending messages, list them with their =cross-agent-recv= decision (process / query / reject) per file. For =process= messages in this project's inbox, propose handling now or after the current task. For pending in other projects, mention the count so the user knows to switch projects when ready. If HALT was active, surface that prominently — cross-agent activity is paused until =cross-agent-resume= clears it.
2. *Process inbox if non-empty.* Mandatory — don't ask, just do it. For each file: determine action, recommend filing, get approval, move. For =.eml= files use the extract script (not raw Read):
#+begin_src bash
diff --git a/.ai/workflows/task-review.org b/.ai/workflows/task-review.org
new file mode 100644
index 0000000..febb986
--- /dev/null
+++ b/.ai/workflows/task-review.org
@@ -0,0 +1,97 @@
+#+TITLE: Task Review Workflow
+#+AUTHOR: Craig Jennings & Claude
+#+DATE: 2026-05-20
+
+* Overview
+
+A short, daily list-hygiene habit. Each run surfaces the handful of top-level tasks that have gone longest without a review, walks them with Craig one at a time, and records the outcome — re-grade, kill, mark in-progress, or just confirm it's still right — stamping =:LAST_REVIEWED:= as it goes.
+
+The point is /trust/: when Craig opens =todo.org=, the priorities and wording should feel accurate. Tasks drift — an =[#A]= from six months ago may not be urgent now, a commitment keeps getting deferred without ever being killed, a body loses the context that made it actionable. Walking a small rotating batch every day keeps the list honest without a big periodic purge.
+
+This is *not* =open-tasks.org=, which just displays the list or picks the next task. This workflow *changes* tasks. Full design and rationale: [[file:../../docs/design/task-review.org][docs/design/task-review.org]].
+
+* When to Use This Workflow
+
+User says one of: "task review", "review tasks", "let's do a task review", "groom my tasks", "task-review".
+
+Cadence is daily and rotational — the batch is the oldest-unreviewed tasks, so consecutive days cover different tasks. At the default batch size of 7 over ~80 open tasks, the full list rotates about every 12 days.
+
+* Phase A: Precondition
+
+Confirm =todo.org= exists at the project root. If it doesn't, say "this project has no =todo.org= to review" and stop — there's nothing to do.
+
+* Phase B: Select the batch
+
+Run the staleness script in list mode to get the oldest-unreviewed top-level tasks. Default batch size is 7; let Craig override if he asks for more or fewer.
+
+#+begin_src bash
+.ai/scripts/task-review-staleness.sh --list todo.org 7
+#+end_src
+
+Each output line is tab-separated: =<line> <LAST_REVIEWED-or-NONE> <heading>=. The list is already ordered oldest-first (never-reviewed tasks sort before dated ones), so walk it top to bottom. Selection only includes depth-2 =**= headings that are =TODO=/=DOING=/=VERIFY= with an =[#A]=/=[#B]=/=[#C]= cookie — DONE/CANCELLED and deeper headings are out by construction.
+
+If the list is empty, report "nothing to review — every task has been looked at recently" and stop.
+
+Read =todo.org= once now so the body of each selected task is in context for the walk. Don't re-read per task.
+
+* Phase C: Walk the batch
+
+Get today's date once for the stamp:
+
+#+begin_src bash
+date "+%Y-%m-%d"
+#+end_src
+
+Then take the tasks one at a time, in list order. For each, show Craig the heading, its priority, when it was last reviewed (or "never"), and a one-line reminder of the body. Then ask what to do. The actions:
+
+| Action | What it does | Stamp =:LAST_REVIEWED:=? |
+|--------+--------------+--------------------------|
+| *Keep* | Still correct as-is — no change beyond recording the review | Yes |
+| *Re-grade A/B/C* | Change the priority cookie to =[#A]= / =[#B]= / =[#C]= | Yes |
+| *Kill* | Abandon it: =TODO=/=DOING= → =CANCELLED= plus a =CLOSED: [YYYY-MM-DD Day]= line | No (it leaves the pool as CANCELLED) |
+| *Doing* | =TODO= → =DOING= (no-op if already DOING/VERIFY) | Yes |
+| *Edit* | Reword the heading or body — do it with Craig, then record the review | Yes |
+| *Skip* | Not now — leave it untouched so it resurfaces next run | No |
+
+Keep is the common case — most tasks are still right and just need re-stamping. Be decisive and quick; this is a 5-minute habit, not a planning session.
+
+*** Stamping =:LAST_REVIEWED:=
+
+Set =:LAST_REVIEWED:= to today's date (from above) in the task's =:PROPERTIES:= drawer:
+
+- If the task already has a =:PROPERTIES:= drawer, update or add the =:LAST_REVIEWED:= line inside it.
+- If it has no drawer, insert one immediately after the heading — or, when the heading carries a =DEADLINE:=/=SCHEDULED:=/=CLOSED:= planning line, immediately after that line (org requires the planning line to sit directly under the heading, with the drawer below it):
+
+#+begin_example
+** TODO [#A] Some topic :tag:
+:PROPERTIES:
+:LAST_REVIEWED: 2026-05-20
+:END:
+Body...
+#+end_example
+
+The exact date string matters: =task-review-staleness.sh= and the wrap-up health check both parse =:LAST_REVIEWED: YYYY-MM-DD=.
+
+*** Killing a task
+
+Follow the completion rules in [[file:../../claude-rules/todo-format.md][todo-format.md]]. A killed top-level =**= task stays task-shaped: change the keyword to =CANCELLED=, add a =CLOSED: [YYYY-MM-DD Day]= line under the heading (generate with =date "+%Y-%m-%d %a"=), and leave the priority and tags intact. It's then a candidate for =--archive-done= at the next cleanup. Don't stamp =:LAST_REVIEWED:= on a kill — it's leaving the review pool anyway.
+
+* Phase D: Close out
+
+When the batch is done (or Craig calls it early):
+
+1. Summarize what changed — re-grades, kills, anything marked DOING — in a couple of lines. Don't list the keeps individually; "the rest were confirmed as-is" is enough.
+2. The edits are already written to =todo.org=. If Craig keeps =todo.org= open in Emacs, remind him to revert the buffer so his editor picks up the changes (and flag that any unsaved edits he had open could collide — re-running picks up whatever's on disk).
+
+* Common Mistakes
+
+1. *Re-reading =todo.org= per task* — read it once in Phase B; the in-memory view is canonical for the walk.
+2. *Stamping a killed task* — a kill leaves the pool; no =:LAST_REVIEWED:= needed.
+3. *Walking more than the batch* — staleness ordering means the batch is the right set; don't expand it into a full-list review.
+4. *Turning it into planning* — this is hygiene (is each task still right?), not "what should I do today". For that, use =open-tasks.org=.
+5. *Drifting the date format* — =:LAST_REVIEWED:= must be =YYYY-MM-DD=, or the staleness script won't read it.
+6. *Marking a kill DONE instead of CANCELLED* — DONE means finished, CANCELLED means abandoned. A task review kills tasks that shouldn't be done at all.
+
+* Living Document
+
+Adjust the batch size if 7/day feels wrong — smaller for a tighter daily touch, larger to rotate faster. If the action set grows (e.g. a "defer with a date" action), add it to the Phase C table and note the stamping behavior.