fix(delivery): preserve exact identity pairs in check-inbox - #721
Conversation
d937e54 to
e837f05
Compare
check-inbox.sh derived its delivery targets from whoami.sh's human-readable output, which flattens the exact (team, agent) rows into independent agents= and teams= lists. With multiple identities the hook then polled the first agent against every team — including (team, agent) pairs that were never registered — and marked their inboxes read. Consume identities.sh's team/agent TSV directly instead: keep the existing first-agent policy, but subscribe only to that agent's actual team rows, so no cross-product pair is ever queried or marked read. The whoami.sh path also resolved the invocation path to the registered project root before the lookup; keep that by calling agmsg_resolve_project first — identities.sh itself is an exact registry lookup by design. Empty or malformed identity output delivers nothing, matching the previous not_joined/suggest behavior. Regressions: with (alpha, alice) and (beta, bob) registered for the same project and type, only (alpha, alice) is displayed and marked read — the nonexistent cross pairs and the second agent's row stay unread. A nested-subdirectory invocation still resolves to the registered project root and delivers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
e837f05 to
3643e3d
Compare
|
Thank you for this, and I'm sorry it sat this long without a word from us. Merged. We reproduced it on The approach is right. Reading We re-ran CI before merging rather than trusting the existing green — it was against a base from six days and forty-four commits back, so it was real but no longer current. This goes out in the next release. |
Summary
Derive
check-inbox.sh's delivery targets fromidentities.sh's exact(team, agent)rows instead ofwhoami.sh's flattened human-readableoutput, so the Stop hook never polls — or marks read — a
(team, agent)pair that was never registered.
Problem
identities.shreturns the registered pairs asteam<TAB>agentrows:whoami.shflattens those rows into independent lists for itshuman-readable output:
check-inbox.shparsed that output, taking the first agent andevery team. With multiple identities registered for the same
(project, type) it therefore polled the cross product — here
(alpha, alice)and(beta, alice)— although(beta, alice)wasnever registered. Any message row matching such a phantom pair is
displayed to the wrong session and marked read, so its actual
addressee never sees it.
Change
Consume
identities.sh's TSV directly:agent's actual team rows — no cross-product pair is ever queried or
marked read.
internally:
agmsg_resolve_projectruns before the lookup, sinceidentities.shitself is an exact registry lookup by design (itsother callers depend on that). Without it, a session started from a
nested subdirectory or a sibling worktree would silently stop
receiving messages.
previous
not_joined/suggestbehavior.Unchanged: the cooldown marker stays keyed by the selected agent, the
per-pair actas exclusivity check still runs inside the team loop, and
the #653 loop-failure capture (
CLAIM_RC) is untouched.The empty-array expansion in the dedup loop is guarded with the
${arr[@]+...}idiom already used inresolve-project.sh, since thescript runs under
set -uand macOS CI runs bash 3.2.Test plan
Two regression tests in
tests/test_inbox.bats:(alpha, alice)and(beta, bob)registered for the sameproject and type, only
(alpha, alice)is displayed and markedread; both cross pairs and the second agent's row stay unread.
Reverting the fix turns it red: the old parser polls
(beta, alice)and falsely marks it read.nested subdirectory still resolves to the root and delivers —
guarding the resolution step, which no existing test exercised at
the check-inbox call site.
Honest status: the suite is expected to run in this repo's PR CI; it
was not executed locally on our side (Windows dev machine, as with
#613).
Note for integration/remote
The same flatten-and-cross-product block exists unchanged on
integration/remote(the storage rework there did not touch theidentity derivation), so this fix will want a small port when that
branch lands on main. Both regression tests should carry over as-is.
🤖 Generated with Claude Code