From 8c0eca8375db2c2d346f5fd08ac752209349f94e Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Sun, 31 May 2026 00:07:03 -0500 Subject: feat(workflows): add monitor-inbox workflow + inbox-status script Handoffs that arrive mid-session used to sit unseen until the next startup or a manual check. Today's burst of cross-project handoffs made that gap obvious. I added monitor-inbox.org, the cadence-and-decision layer over process-inbox: check the inbox at every task boundary, decide act-now (just do it) versus file (ask, with filing as option 1), and reply to the sender. An opt-in background-monitor /loop recipe covers unattended watching. inbox-status (with bats tests) is the cheap check the cadence calls. It lists unprocessed handoffs and exits nonzero when any are pending, using the same artifact exclusions as the wrap-up sanity check. protocols.org gets a short cadence note so the habit fires every session, and INDEX.org lists the new workflow. The act-vs-file rule (act-now is silent, filing asks with file as option 1, ambiguity asks) is the decision protocol we settled today. --- .ai/scripts/inbox-status | 50 +++++++++++++++++++++++++++++++++ .ai/scripts/tests/inbox-status.bats | 56 +++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) create mode 100755 .ai/scripts/inbox-status create mode 100644 .ai/scripts/tests/inbox-status.bats (limited to '.ai/scripts') diff --git a/.ai/scripts/inbox-status b/.ai/scripts/inbox-status new file mode 100755 index 0000000..b917144 --- /dev/null +++ b/.ai/scripts/inbox-status @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# inbox-status — list unprocessed inbox handoffs; exit nonzero if any are pending. +# +# "Unprocessed" = a regular file in inbox/ that isn't a pipeline artifact. The +# exclusions match the wrap-up inbox sanity check: .gitkeep, lint-followups.org +# (the lint-org pipeline file daily-prep consumes), and any PROCESSED-* file +# (explicitly deferred). +# +# Prints one indented line per pending handoff, then a summary count. +# +# Exit codes (so a cadence check can gate cheaply — `inbox-status -q || ...`): +# 0 inbox clean (only artifacts) +# 1 one or more handoffs pending +# 2 no inbox/ directory, or bad usage +# +# Flags: +# -q / --quiet print only the summary count line, not the per-item lines +# +# Run from a project root (where inbox/ lives). +set -euo pipefail + +quiet=0 +case "${1:-}" in + -q|--quiet) quiet=1 ;; + "") ;; + *) echo "usage: inbox-status [-q|--quiet]" >&2; exit 2 ;; +esac + +if [ ! -d inbox ]; then + echo "inbox-status: no inbox/ directory" >&2 + exit 2 +fi + +mapfile -t pending < <(find inbox -maxdepth 1 -type f \ + ! -name '.gitkeep' \ + ! -name 'lint-followups.org' \ + ! -name 'PROCESSED-*' \ + -printf '%f\n' 2>/dev/null | sort) + +n=${#pending[@]} + +if [ "$quiet" -eq 0 ]; then + for f in "${pending[@]}"; do + printf ' %s\n' "$f" + done +fi +printf 'inbox-status: %d pending handoff(s)\n' "$n" + +[ "$n" -gt 0 ] && exit 1 +exit 0 diff --git a/.ai/scripts/tests/inbox-status.bats b/.ai/scripts/tests/inbox-status.bats new file mode 100644 index 0000000..bc8a734 --- /dev/null +++ b/.ai/scripts/tests/inbox-status.bats @@ -0,0 +1,56 @@ +#!/usr/bin/env bats +# Tests for inbox-status: list unprocessed inbox handoffs, exit nonzero if any. + +setup() { + SCRIPT="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)/inbox-status" + TMP="$(mktemp -d)" +} + +teardown() { + rm -rf "$TMP" +} + +@test "inbox-status: no inbox/ dir exits 2" { + cd "$TMP" + run "$SCRIPT" + [ "$status" -eq 2 ] +} + +@test "inbox-status: only .gitkeep is clean (exit 0)" { + mkdir "$TMP/inbox" + touch "$TMP/inbox/.gitkeep" + cd "$TMP" + run "$SCRIPT" + [ "$status" -eq 0 ] + [[ "$output" == *"0 pending"* ]] +} + +@test "inbox-status: a handoff is pending (exit 1, listed)" { + mkdir "$TMP/inbox" + touch "$TMP/inbox/.gitkeep" + echo body > "$TMP/inbox/2026-05-31-from-work-thing.org" + cd "$TMP" + run "$SCRIPT" + [ "$status" -eq 1 ] + [[ "$output" == *"1 pending"* ]] + [[ "$output" == *"2026-05-31-from-work-thing.org"* ]] +} + +@test "inbox-status: excludes lint-followups.org and PROCESSED-* artifacts" { + mkdir "$TMP/inbox" + touch "$TMP/inbox/.gitkeep" "$TMP/inbox/lint-followups.org" "$TMP/inbox/PROCESSED-old.org" + cd "$TMP" + run "$SCRIPT" + [ "$status" -eq 0 ] + [[ "$output" == *"0 pending"* ]] +} + +@test "inbox-status: -q suppresses the per-item lines" { + mkdir "$TMP/inbox" + echo body > "$TMP/inbox/handoff.org" + cd "$TMP" + run "$SCRIPT" -q + [ "$status" -eq 1 ] + [[ "$output" == *"1 pending"* ]] + [[ "$output" != *" handoff.org"* ]] +} -- cgit v1.2.3