aboutsummaryrefslogtreecommitdiff
path: root/.ai/scripts/task-review-staleness.sh
diff options
context:
space:
mode:
authorCraig Jennings <c@cjennings.net>2026-07-11 02:13:15 -0500
committerCraig Jennings <c@cjennings.net>2026-07-11 02:13:15 -0500
commitcfbabf1da65c5224a4e3c7302c71baa98935d0e4 (patch)
treeb84a7484279dbffc27f396a18da3dd474538c702 /.ai/scripts/task-review-staleness.sh
parent06ca6c7c0a825fa9fed66cf17f94be8c0ed4d3da (diff)
downloadrulesets-cfbabf1da65c5224a4e3c7302c71baa98935d0e4.tar.gz
rulesets-cfbabf1da65c5224a4e3c7302c71baa98935d0e4.zip
fix(task-review): accept org-native LAST_REVIEWED stamps, warn on bad ones
task-review-staleness.sh expected a bare 2026-07-09 and treated an org inactive timestamp ([2026-07-09 Thu], the form matching the CREATED:/CLOSED: cookies in the same drawer) as unparseable. The count branch folded that into the stale count, so a freshly-reviewed task reported as never-reviewed, and a full review pass never dropped the startup nudge. Both the bare and bracketed forms now normalize to the ISO date. A value that is neither warns loudly to stderr (file:line:value) and stays out of the count, since a data error shouldn't hide as "never reviewed." task-review.org documents the accepted format. Tested red/green: 5 new bats cases (bracketed fresh and stale, list-mode sort by real date, malformed warns and is excluded). Full suite 273 ok.
Diffstat (limited to '.ai/scripts/task-review-staleness.sh')
-rwxr-xr-x.ai/scripts/task-review-staleness.sh40
1 files changed, 33 insertions, 7 deletions
diff --git a/.ai/scripts/task-review-staleness.sh b/.ai/scripts/task-review-staleness.sh
index ed43712..50e0257 100755
--- a/.ai/scripts/task-review-staleness.sh
+++ b/.ai/scripts/task-review-staleness.sh
@@ -19,9 +19,14 @@
# deeper headings, and cookie-less headings are not review units.
#
# A task is stale (count mode), and sorts oldest (list mode), when its
-# :LAST_REVIEWED: property is missing or unparseable (NIL sorts first), or
-# when its age strictly exceeds the threshold (age > N days; age == N is
-# still fresh).
+# :LAST_REVIEWED: property is missing (NIL sorts first) or when its age
+# strictly exceeds the threshold (age > N days; age == N is still fresh).
+#
+# :LAST_REVIEWED: accepts a bare date (2026-07-09) or an org-native
+# timestamp ([2026-07-09 Thu] or <2026-07-09 Thu ...>); both normalize to
+# the ISO date. A value that is present but parses to neither is a data
+# error: the script warns loudly to stderr (file:line:value) and leaves it
+# out of the stale count rather than silently treating it as never-reviewed.
set -euo pipefail
@@ -46,7 +51,7 @@ num="$2"
# only the property drawer between a qualifying heading and the next heading
# is scanned.
extract_tasks() {
- awk '
+ awk -v fname="$todo_file" '
function flush() {
if (in_task) printf "%s\t%s\t%s\n", hline, (have_lr ? lr : "NONE"), heading
}
@@ -65,7 +70,21 @@ extract_tasks() {
v = $0
sub(/^[ \t]*:LAST_REVIEWED:[ \t]*/, "", v)
sub(/[ \t]*$/, "", v)
- lr = v; have_lr = 1
+ raw = v
+ # Accept org-native stamps: strip a leading [ or < (inactive/active
+ # timestamp bracket) and take the leading ISO date, so [2026-07-09 Thu]
+ # and 2026-07-09 both normalize to 2026-07-09.
+ sub(/^[[<]/, "", v)
+ if (match(v, /^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]/)) {
+ lr = substr(v, RSTART, RLENGTH); have_lr = 1
+ } else {
+ # Present but unparseable — a data error, not "never reviewed". Warn
+ # loudly (file:line:value) instead of silently folding it into the
+ # stale count, where a re-review in the same bad format would never
+ # drop the number and nothing would explain why.
+ printf "task-review-staleness: %s:%d: unparseable :LAST_REVIEWED: %s (expected YYYY-MM-DD or [YYYY-MM-DD Day])\n", fname, NR, raw > "/dev/stderr"
+ lr = "INVALID"; have_lr = 1
+ }
next
}
END { flush() }
@@ -98,9 +117,16 @@ while IFS=$'\t' read -r hline value heading; do
continue
fi
- # Unparseable date → treat as NIL (stale).
+ # Malformed stamp: extract_tasks already warned. A data error is not
+ # "never reviewed", so it stays out of the stale count.
+ if [ "$value" = "INVALID" ]; then
+ continue
+ fi
+
+ # A shape-valid but impossible date (e.g. 2026-13-45) slips past the awk
+ # regex; warn and skip rather than silently counting it.
if ! rev_epoch=$(date -d "$value" +%s 2>/dev/null); then
- count=$((count + 1))
+ echo "task-review-staleness: $todo_file: unparseable :LAST_REVIEWED: $value" >&2
continue
fi