aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.ai/protocols.org10
-rwxr-xr-x.ai/scripts/inbox-status50
-rw-r--r--.ai/scripts/tests/inbox-status.bats56
-rw-r--r--.ai/workflows/INDEX.org2
-rw-r--r--.ai/workflows/monitor-inbox.org92
-rw-r--r--claude-templates/.ai/protocols.org10
-rwxr-xr-xclaude-templates/.ai/scripts/inbox-status50
-rw-r--r--claude-templates/.ai/scripts/tests/inbox-status.bats56
-rw-r--r--claude-templates/.ai/workflows/INDEX.org2
-rw-r--r--claude-templates/.ai/workflows/monitor-inbox.org92
10 files changed, 420 insertions, 0 deletions
diff --git a/.ai/protocols.org b/.ai/protocols.org
index 1499fbb..25e634a 100644
--- a/.ai/protocols.org
+++ b/.ai/protocols.org
@@ -180,6 +180,16 @@ When the user starts a new task that will produce file artifacts, propose the =w
Canonical rule: =~/code/rulesets/claude-rules/working-files.md=.
+** Inbox Monitoring Cadence — Check at Task Boundaries
+
+Check =inbox/= at every task boundary (after finishing a unit of work, before reporting back or asking "what's next") — not only at startup or when asked. Handoffs arrive mid-session; this keeps them from piling up unseen. The check is one command:
+
+#+begin_src bash
+.ai/scripts/inbox-status -q
+#+end_src
+
+Exit 1 means handoffs are pending — process them per =process-inbox.org=. For each accepted handoff, the act-vs-file rule: *act now* when it's clear, bounded, low-risk, in-scope, and cheaper than deferring — just do it, no asking; *file* otherwise — ask first, with filing as option 1 and "do it now" as option 2; *ask* if unsure. Always reply to a handoff's sender (confirm on accept, the why on reject). Full process, the reply discipline, and the opt-in background-monitor =/loop= recipe live in =monitor-inbox.org=.
+
* Important Terminology
** "Let's run the [X] workflow" vs "I want to create an [X] workflow"
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"* ]]
+}
diff --git a/.ai/workflows/INDEX.org b/.ai/workflows/INDEX.org
index 9c9c32c..c8554d4 100644
--- a/.ai/workflows/INDEX.org
+++ b/.ai/workflows/INDEX.org
@@ -42,6 +42,8 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e
- Triggers: "clean up todo.org", "clean-todo", "tidy the todo file", "archive the done items in todo.org", "run the todo cleanup"
- =process-inbox.org= — evaluate each inbox item against a three-question value gate (advances an existing TODO / improves the project / serves the mission), then implement, fold, file, defer, or reject per source (Craig / project handoff / script). Auto-invoked by startup when inbox is non-empty. Source-aware rejection flow: handoff rejections write a response back via =inbox-send= naming the failed gate question and any reconsideration condition.
- Triggers: "process inbox", "process the inbox", "handle the inbox", "what's in inbox", "what's in the inbox", "let's clear the inbox", "let's process the inbox items"
+- =monitor-inbox.org= — the cadence + act-vs-file + reply layer over process-inbox: check =inbox-status= at every task boundary, decide act-now (just do it) vs file (ask, file = option 1), and confirm back to handoff senders. Includes the opt-in background-monitor =/loop= recipe.
+ - Triggers: "monitor the inbox", "watch the inbox", "respond to the handoffs", "handle the handoffs"
** Calendar
diff --git a/.ai/workflows/monitor-inbox.org b/.ai/workflows/monitor-inbox.org
new file mode 100644
index 0000000..dd545d9
--- /dev/null
+++ b/.ai/workflows/monitor-inbox.org
@@ -0,0 +1,92 @@
+#+TITLE: Monitor Inbox Workflow
+#+AUTHOR: Craig Jennings & Claude
+#+DATE: 2026-05-31
+
+* Overview
+
+Keep the project's =inbox/= responsive: notice handoffs on a cadence, triage each one, decide whether to act now or file it, and reply to the sender. This workflow is the /when, how-often, and act-vs-file/ layer. The per-item disposition mechanics — the value gate, the implement/fold/file classification, the per-source rejection flow — live in [[file:process-inbox.org][process-inbox.org]] and are not duplicated here. Think of it as: monitor-inbox decides /that/ an item gets handled and /how I respond/; process-inbox decides /what disposition/ each item gets.
+
+The gap this closes: handoffs that arrive mid-session used to sit unseen until the user asked or the next startup ran. A handoff the sender can't see being handled trains them to escalate around the inbox channel.
+
+* When to Use This Workflow
+
+Trigger phrases:
+
+- "monitor the inbox" / "watch the inbox"
+- "respond to the handoffs" / "handle the handoffs"
+
+Cadence auto-trigger (the main mechanism — see Cadence below): check at every task boundary during a session, not only when asked.
+
+* Cadence — how often to check
+
+*Default: check at every task boundary.* After finishing a unit of work, before reporting back or asking "what's next," run the cheap status check:
+
+#+begin_src bash
+.ai/scripts/inbox-status -q
+#+end_src
+
+Exit 1 means handoffs are pending — list them (drop =-q=) and process per process-inbox.org. Exit 0 means clean; say nothing. This is one =find=; it costs nothing to run often, and it's the fix for handoffs piling up unseen during long sessions.
+
+*Startup and wrap-up already cover their ends.* Startup Phase C processes a non-empty inbox; the wrap-up sanity check refuses to wrap with unprocessed handoffs. The task-boundary cadence fills the middle.
+
+*Mid-task arrivals.* If a handoff lands while you're mid-task and it's urgent (blocks the current work, or is time-sensitive), surface it right away. Otherwise batch it to the next task boundary so the current work isn't thrashed.
+
+*Unattended / background monitoring (opt-in).* When the user is working elsewhere and wants rulesets handoffs handled without being present, run a polling loop:
+
+#+begin_src
+/loop 15m check the inbox with inbox-status and process any handoffs per process-inbox.org
+#+end_src
+
+This is opt-in, not the default — continuous polling has a cost, and most handoffs aren't urgent. Pick an interval matched to how fast handoffs actually arrive (a burst of cross-project work warrants a tighter loop; a quiet day warrants none).
+
+* The act-vs-file decision
+
+Every accepted handoff (one that clears process-inbox's value gate) is then either acted on now or filed as a task. The rule, and how to surface it:
+
+*Act immediately — and just do it, no asking — when all of these hold:*
+- *Clear* — the action is unambiguous; no design decision or option-choice is needed.
+- *Bounded* — small, finishable this session, ideally a tight file set.
+- *Low-risk and verifiable now* — not a risky change to load-bearing infra (or trivially revertible), and testable/lintable this session.
+- *In-scope and safe* — within this project, not destructive or outward-facing without confirmation, not across a project boundary.
+- *Cheaper than deferring* — doing it now costs less than filing plus re-triaging later.
+
+When you decide to act, queue the work and do it. Don't ask first.
+
+*File a task when any of these hold:*
+- It needs a judgment call, a design decision, or an option the user would pick.
+- It's large, multi-session, or sprawls across many files.
+- It's blocked (a dependency, an external thing, the user is away).
+- It's risky enough to want the user's eyes before it lands.
+- It's off the session's active goal and acting now would derail it (file and keep going, unless it's urgent).
+
+When you decide to file, *ask first* — inline numbered options per =interaction.md=, with *filing as option 1 (the recommendation)* and *"do it now" as option 2*:
+
+#+begin_example
+<handoff> wants <X>. My read: file it (needs <reason>).
+
+1. File as a TODO ([#?] :tags:) — Recommended
+2. Do it now instead
+3. Something else
+
+Pick a number.
+#+end_example
+
+*Always ask if you're unsure* which side of the line an item falls on. Decisiveness on clear act-now items is the point of the rule; the ask is for genuine ambiguity and for filing.
+
+* Replying to handoffs
+
+A handoff came from another project's agent (or the user). Close the loop:
+
+- *Accepted and acted on* — send a confirmation to the sender via =inbox-send <sender> --text "..."=, naming what landed and the commit, so they're not left guessing (they can't see this project's git log). =inbox-send= excludes the current project as a target, so a self-sourced item is handled in-session, not sent.
+- *Accepted and filed* — a short confirmation that it's filed and where, so the sender knows it wasn't dropped.
+- *Rejected* — always state the why (which value-gate question failed), per process-inbox's per-source rejection flow.
+
+Cross-project boundary: never act on a file under another project's =.ai/= scope from here — route it back as a handoff (see =cross-project.md=).
+
+* The inbox-status script
+
+=.ai/scripts/inbox-status= lists unprocessed handoffs and exits nonzero when any are pending. Exclusions match the wrap-up sanity check (=.gitkeep=, =lint-followups.org=, =PROCESSED-*=). Exit 0 = clean, 1 = pending, 2 = no inbox/ or bad usage. Use =-q= for the count-only form the cadence check calls.
+
+* Living Document
+
+Tune the cadence if task-boundary checking proves too frequent or too sparse in practice. Refine the act-vs-file criteria as edge cases recur. If the background-monitor loop becomes a common pattern, capture the interval that worked. The decision rule itself — act-now is silent, filing asks with file-as-option-1, ambiguity asks — is the stable core (set by Craig, 2026-05-30).
diff --git a/claude-templates/.ai/protocols.org b/claude-templates/.ai/protocols.org
index 1499fbb..25e634a 100644
--- a/claude-templates/.ai/protocols.org
+++ b/claude-templates/.ai/protocols.org
@@ -180,6 +180,16 @@ When the user starts a new task that will produce file artifacts, propose the =w
Canonical rule: =~/code/rulesets/claude-rules/working-files.md=.
+** Inbox Monitoring Cadence — Check at Task Boundaries
+
+Check =inbox/= at every task boundary (after finishing a unit of work, before reporting back or asking "what's next") — not only at startup or when asked. Handoffs arrive mid-session; this keeps them from piling up unseen. The check is one command:
+
+#+begin_src bash
+.ai/scripts/inbox-status -q
+#+end_src
+
+Exit 1 means handoffs are pending — process them per =process-inbox.org=. For each accepted handoff, the act-vs-file rule: *act now* when it's clear, bounded, low-risk, in-scope, and cheaper than deferring — just do it, no asking; *file* otherwise — ask first, with filing as option 1 and "do it now" as option 2; *ask* if unsure. Always reply to a handoff's sender (confirm on accept, the why on reject). Full process, the reply discipline, and the opt-in background-monitor =/loop= recipe live in =monitor-inbox.org=.
+
* Important Terminology
** "Let's run the [X] workflow" vs "I want to create an [X] workflow"
diff --git a/claude-templates/.ai/scripts/inbox-status b/claude-templates/.ai/scripts/inbox-status
new file mode 100755
index 0000000..b917144
--- /dev/null
+++ b/claude-templates/.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/claude-templates/.ai/scripts/tests/inbox-status.bats b/claude-templates/.ai/scripts/tests/inbox-status.bats
new file mode 100644
index 0000000..bc8a734
--- /dev/null
+++ b/claude-templates/.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"* ]]
+}
diff --git a/claude-templates/.ai/workflows/INDEX.org b/claude-templates/.ai/workflows/INDEX.org
index 9c9c32c..c8554d4 100644
--- a/claude-templates/.ai/workflows/INDEX.org
+++ b/claude-templates/.ai/workflows/INDEX.org
@@ -42,6 +42,8 @@ This index must list every =.org= file in =.ai/workflows/= except this one and e
- Triggers: "clean up todo.org", "clean-todo", "tidy the todo file", "archive the done items in todo.org", "run the todo cleanup"
- =process-inbox.org= — evaluate each inbox item against a three-question value gate (advances an existing TODO / improves the project / serves the mission), then implement, fold, file, defer, or reject per source (Craig / project handoff / script). Auto-invoked by startup when inbox is non-empty. Source-aware rejection flow: handoff rejections write a response back via =inbox-send= naming the failed gate question and any reconsideration condition.
- Triggers: "process inbox", "process the inbox", "handle the inbox", "what's in inbox", "what's in the inbox", "let's clear the inbox", "let's process the inbox items"
+- =monitor-inbox.org= — the cadence + act-vs-file + reply layer over process-inbox: check =inbox-status= at every task boundary, decide act-now (just do it) vs file (ask, file = option 1), and confirm back to handoff senders. Includes the opt-in background-monitor =/loop= recipe.
+ - Triggers: "monitor the inbox", "watch the inbox", "respond to the handoffs", "handle the handoffs"
** Calendar
diff --git a/claude-templates/.ai/workflows/monitor-inbox.org b/claude-templates/.ai/workflows/monitor-inbox.org
new file mode 100644
index 0000000..dd545d9
--- /dev/null
+++ b/claude-templates/.ai/workflows/monitor-inbox.org
@@ -0,0 +1,92 @@
+#+TITLE: Monitor Inbox Workflow
+#+AUTHOR: Craig Jennings & Claude
+#+DATE: 2026-05-31
+
+* Overview
+
+Keep the project's =inbox/= responsive: notice handoffs on a cadence, triage each one, decide whether to act now or file it, and reply to the sender. This workflow is the /when, how-often, and act-vs-file/ layer. The per-item disposition mechanics — the value gate, the implement/fold/file classification, the per-source rejection flow — live in [[file:process-inbox.org][process-inbox.org]] and are not duplicated here. Think of it as: monitor-inbox decides /that/ an item gets handled and /how I respond/; process-inbox decides /what disposition/ each item gets.
+
+The gap this closes: handoffs that arrive mid-session used to sit unseen until the user asked or the next startup ran. A handoff the sender can't see being handled trains them to escalate around the inbox channel.
+
+* When to Use This Workflow
+
+Trigger phrases:
+
+- "monitor the inbox" / "watch the inbox"
+- "respond to the handoffs" / "handle the handoffs"
+
+Cadence auto-trigger (the main mechanism — see Cadence below): check at every task boundary during a session, not only when asked.
+
+* Cadence — how often to check
+
+*Default: check at every task boundary.* After finishing a unit of work, before reporting back or asking "what's next," run the cheap status check:
+
+#+begin_src bash
+.ai/scripts/inbox-status -q
+#+end_src
+
+Exit 1 means handoffs are pending — list them (drop =-q=) and process per process-inbox.org. Exit 0 means clean; say nothing. This is one =find=; it costs nothing to run often, and it's the fix for handoffs piling up unseen during long sessions.
+
+*Startup and wrap-up already cover their ends.* Startup Phase C processes a non-empty inbox; the wrap-up sanity check refuses to wrap with unprocessed handoffs. The task-boundary cadence fills the middle.
+
+*Mid-task arrivals.* If a handoff lands while you're mid-task and it's urgent (blocks the current work, or is time-sensitive), surface it right away. Otherwise batch it to the next task boundary so the current work isn't thrashed.
+
+*Unattended / background monitoring (opt-in).* When the user is working elsewhere and wants rulesets handoffs handled without being present, run a polling loop:
+
+#+begin_src
+/loop 15m check the inbox with inbox-status and process any handoffs per process-inbox.org
+#+end_src
+
+This is opt-in, not the default — continuous polling has a cost, and most handoffs aren't urgent. Pick an interval matched to how fast handoffs actually arrive (a burst of cross-project work warrants a tighter loop; a quiet day warrants none).
+
+* The act-vs-file decision
+
+Every accepted handoff (one that clears process-inbox's value gate) is then either acted on now or filed as a task. The rule, and how to surface it:
+
+*Act immediately — and just do it, no asking — when all of these hold:*
+- *Clear* — the action is unambiguous; no design decision or option-choice is needed.
+- *Bounded* — small, finishable this session, ideally a tight file set.
+- *Low-risk and verifiable now* — not a risky change to load-bearing infra (or trivially revertible), and testable/lintable this session.
+- *In-scope and safe* — within this project, not destructive or outward-facing without confirmation, not across a project boundary.
+- *Cheaper than deferring* — doing it now costs less than filing plus re-triaging later.
+
+When you decide to act, queue the work and do it. Don't ask first.
+
+*File a task when any of these hold:*
+- It needs a judgment call, a design decision, or an option the user would pick.
+- It's large, multi-session, or sprawls across many files.
+- It's blocked (a dependency, an external thing, the user is away).
+- It's risky enough to want the user's eyes before it lands.
+- It's off the session's active goal and acting now would derail it (file and keep going, unless it's urgent).
+
+When you decide to file, *ask first* — inline numbered options per =interaction.md=, with *filing as option 1 (the recommendation)* and *"do it now" as option 2*:
+
+#+begin_example
+<handoff> wants <X>. My read: file it (needs <reason>).
+
+1. File as a TODO ([#?] :tags:) — Recommended
+2. Do it now instead
+3. Something else
+
+Pick a number.
+#+end_example
+
+*Always ask if you're unsure* which side of the line an item falls on. Decisiveness on clear act-now items is the point of the rule; the ask is for genuine ambiguity and for filing.
+
+* Replying to handoffs
+
+A handoff came from another project's agent (or the user). Close the loop:
+
+- *Accepted and acted on* — send a confirmation to the sender via =inbox-send <sender> --text "..."=, naming what landed and the commit, so they're not left guessing (they can't see this project's git log). =inbox-send= excludes the current project as a target, so a self-sourced item is handled in-session, not sent.
+- *Accepted and filed* — a short confirmation that it's filed and where, so the sender knows it wasn't dropped.
+- *Rejected* — always state the why (which value-gate question failed), per process-inbox's per-source rejection flow.
+
+Cross-project boundary: never act on a file under another project's =.ai/= scope from here — route it back as a handoff (see =cross-project.md=).
+
+* The inbox-status script
+
+=.ai/scripts/inbox-status= lists unprocessed handoffs and exits nonzero when any are pending. Exclusions match the wrap-up sanity check (=.gitkeep=, =lint-followups.org=, =PROCESSED-*=). Exit 0 = clean, 1 = pending, 2 = no inbox/ or bad usage. Use =-q= for the count-only form the cadence check calls.
+
+* Living Document
+
+Tune the cadence if task-boundary checking proves too frequent or too sparse in practice. Refine the act-vs-file criteria as edge cases recur. If the background-monitor loop becomes a common pattern, capture the interval that worked. The decision rule itself — act-now is silent, filing asks with file-as-option-1, ambiguity asks — is the stable core (set by Craig, 2026-05-30).