aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ai/workflows/wrap-it-up.org33
-rw-r--r--claude-templates/.ai/workflows/wrap-it-up.org33
2 files changed, 20 insertions, 46 deletions
diff --git a/.ai/workflows/wrap-it-up.org b/.ai/workflows/wrap-it-up.org
index e2a535b..fc8c398 100644
--- a/.ai/workflows/wrap-it-up.org
+++ b/.ai/workflows/wrap-it-up.org
@@ -174,11 +174,11 @@ Preview without writing — same flags as =--check= on the other scripts:
The wrap-up never blocks on judgment items — they're deferred by design.
For an interactive walk of the judgments mid-day, run =/lint-org todo.org=.
-*** Date-coverage scan (surface =[#A]= / =[#B]= tasks lacking a timestamp)
+*** Review-habit health check (surface a slipped daily task-review)
-Scan =todo.org= for open =[#A]= and =[#B]= tasks that have neither a =DEADLINE:= nor a =SCHEDULED:= line directly under the heading, and surface the candidates to the follow-ups file so the morning's daily-prep flags them for review.
+The daily task-review habit walks the open top-level tasks on a rotating cycle, stamping =:LAST_REVIEWED:= as it goes (see =task-review.org=). This check is the watchdog for that habit. When tasks have gone too long unreviewed, the habit has slipped, and the wrap-up says so in one line — it does not re-list the tasks.
-The two timestamps mean different things (=DEADLINE:= = external, consequence-bearing; =SCHEDULED:= = social, accountability-bearing — see the priority spec at the top of =todo.org=). High-priority work that carries neither is suspicious: either it has an implicit deadline that should be made explicit, or it has someone waiting that should surface in the agenda, or its priority is wrong. The scan flags candidates; the operator decides.
+=task-review-staleness.sh= counts top-level =[#A]= / =[#B]= / =[#C]= tasks (TODO/DOING/VERIFY) whose =:LAST_REVIEWED:= is missing or older than the threshold. Threshold 30 days is about 2.5 review cycles of slack at the default batch size — one missed week is fine, three weeks signals a problem.
#+begin_src bash
if [ -n "$LINT_ORG_FOLLOWUPS" ]; then
@@ -188,29 +188,16 @@ elif [ -d "./inbox" ]; then
else
followups=".ai/lint-followups.org"
fi
-[ -f todo.org ] && awk '
- /^\*\* (TODO|DOING|VERIFY) \[#[AB]\]/ {
- if (heading != "" && !has_date) print line ": " heading
- heading = $0
- line = NR
- has_date = 0
- next
- }
- /^(SCHEDULED|DEADLINE|CLOSED):/ { has_date = 1; next }
- /^\*+ / { if (heading != "" && !has_date) print line ": " heading; heading = ""; next }
- END { if (heading != "" && !has_date) print line ": " heading }
-' todo.org > /tmp/date-coverage.out
-if [ -s /tmp/date-coverage.out ]; then
- {
- printf "\n* %s — Date coverage: [#A] / [#B] tasks without DEADLINE or SCHEDULED\n" "$(date '+%Y-%m-%d %a')"
- printf "%s\n" "Review each: add a date, drop the priority, or confirm 'no-date by intent' inline."
- sed 's/^/- /' /tmp/date-coverage.out
- } >> "$followups"
+if [ -f todo.org ]; then
+ stale=$(.ai/scripts/task-review-staleness.sh todo.org 30 2>/dev/null || echo 0)
+ if [ "$stale" -gt 0 ]; then
+ printf "\n* %s — Task-review health: %s top-level [#A]/[#B]/[#C] tasks unreviewed for >30 days (daily review may have slipped)\n" \
+ "$(date '+%Y-%m-%d %a')" "$stale" >> "$followups"
+ fi
fi
-rm -f /tmp/date-coverage.out
#+end_src
-The scan is intentionally conservative — it surfaces every candidate. False positives (tasks that legitimately have no date) are cheap to dismiss; false negatives would let high-priority work drift undated. No-date is a valid resting state for some tasks (research, watch-list), and the operator can mark those as such in the daily-prep review rather than tagging them in =todo.org=.
+A non-zero count writes one summary line and nothing else — the per-task walk is the review habit's job, not the wrap-up's. This supersedes the old date-coverage scan, which flagged every dateless =[#A]= / =[#B]= task on the wrong assumption that high-priority work needs a date. No-date is a valid resting state for research and watch-list tasks; staleness, not datelessness, is the real signal.
** Step 3.5: Linear ticket-state hygiene (skip if project doesn't use Linear)
diff --git a/claude-templates/.ai/workflows/wrap-it-up.org b/claude-templates/.ai/workflows/wrap-it-up.org
index e2a535b..fc8c398 100644
--- a/claude-templates/.ai/workflows/wrap-it-up.org
+++ b/claude-templates/.ai/workflows/wrap-it-up.org
@@ -174,11 +174,11 @@ Preview without writing — same flags as =--check= on the other scripts:
The wrap-up never blocks on judgment items — they're deferred by design.
For an interactive walk of the judgments mid-day, run =/lint-org todo.org=.
-*** Date-coverage scan (surface =[#A]= / =[#B]= tasks lacking a timestamp)
+*** Review-habit health check (surface a slipped daily task-review)
-Scan =todo.org= for open =[#A]= and =[#B]= tasks that have neither a =DEADLINE:= nor a =SCHEDULED:= line directly under the heading, and surface the candidates to the follow-ups file so the morning's daily-prep flags them for review.
+The daily task-review habit walks the open top-level tasks on a rotating cycle, stamping =:LAST_REVIEWED:= as it goes (see =task-review.org=). This check is the watchdog for that habit. When tasks have gone too long unreviewed, the habit has slipped, and the wrap-up says so in one line — it does not re-list the tasks.
-The two timestamps mean different things (=DEADLINE:= = external, consequence-bearing; =SCHEDULED:= = social, accountability-bearing — see the priority spec at the top of =todo.org=). High-priority work that carries neither is suspicious: either it has an implicit deadline that should be made explicit, or it has someone waiting that should surface in the agenda, or its priority is wrong. The scan flags candidates; the operator decides.
+=task-review-staleness.sh= counts top-level =[#A]= / =[#B]= / =[#C]= tasks (TODO/DOING/VERIFY) whose =:LAST_REVIEWED:= is missing or older than the threshold. Threshold 30 days is about 2.5 review cycles of slack at the default batch size — one missed week is fine, three weeks signals a problem.
#+begin_src bash
if [ -n "$LINT_ORG_FOLLOWUPS" ]; then
@@ -188,29 +188,16 @@ elif [ -d "./inbox" ]; then
else
followups=".ai/lint-followups.org"
fi
-[ -f todo.org ] && awk '
- /^\*\* (TODO|DOING|VERIFY) \[#[AB]\]/ {
- if (heading != "" && !has_date) print line ": " heading
- heading = $0
- line = NR
- has_date = 0
- next
- }
- /^(SCHEDULED|DEADLINE|CLOSED):/ { has_date = 1; next }
- /^\*+ / { if (heading != "" && !has_date) print line ": " heading; heading = ""; next }
- END { if (heading != "" && !has_date) print line ": " heading }
-' todo.org > /tmp/date-coverage.out
-if [ -s /tmp/date-coverage.out ]; then
- {
- printf "\n* %s — Date coverage: [#A] / [#B] tasks without DEADLINE or SCHEDULED\n" "$(date '+%Y-%m-%d %a')"
- printf "%s\n" "Review each: add a date, drop the priority, or confirm 'no-date by intent' inline."
- sed 's/^/- /' /tmp/date-coverage.out
- } >> "$followups"
+if [ -f todo.org ]; then
+ stale=$(.ai/scripts/task-review-staleness.sh todo.org 30 2>/dev/null || echo 0)
+ if [ "$stale" -gt 0 ]; then
+ printf "\n* %s — Task-review health: %s top-level [#A]/[#B]/[#C] tasks unreviewed for >30 days (daily review may have slipped)\n" \
+ "$(date '+%Y-%m-%d %a')" "$stale" >> "$followups"
+ fi
fi
-rm -f /tmp/date-coverage.out
#+end_src
-The scan is intentionally conservative — it surfaces every candidate. False positives (tasks that legitimately have no date) are cheap to dismiss; false negatives would let high-priority work drift undated. No-date is a valid resting state for some tasks (research, watch-list), and the operator can mark those as such in the daily-prep review rather than tagging them in =todo.org=.
+A non-zero count writes one summary line and nothing else — the per-task walk is the review habit's job, not the wrap-up's. This supersedes the old date-coverage scan, which flagged every dateless =[#A]= / =[#B]= task on the wrong assumption that high-priority work needs a date. No-date is a valid resting state for research and watch-list tasks; staleness, not datelessness, is the real signal.
** Step 3.5: Linear ticket-state hygiene (skip if project doesn't use Linear)