fix(issue-34): a pane caller reaches an agent of another workspace on its own server; an unseen target is not recorded as busy

- mam_herdr: for name lookups (agent / has-session / kill-session) a
  HERDR_WORKSPACE_ID that is the caller pane's own workspace (it matches
  the HERDR_PANE_ID prefix) is dropped. Live agent names are unique per
  server (herdr --skill), so this cannot address a same-named agent of
  another workspace; an id the caller chose, or one set outside a pane,
  still scopes (H-18/H-21/H-23). The generated shim is unchanged (S11).
- wrapper: a preflight that cannot see the target closes the job
  cancelled with detail target_not_found instead of session_busy; the
  loop's sub-job table counts it as NOT-DISPATCHED.
- tests: T10-T12 with the measured pane env (same-server socket, caller
  w6, target w2); T8 follows the sub-job-armed guard.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
GodopuandClaude Opus 5.5 committed 2026-09-25 01:25:48 +00:00
1 parent 1d4f338b4c
commit ed293dcef4
4 files changed
+131 -12

No files matched your search

+30
View File
@@ -1446,6 +1446,31 @@ _herdr_call_is_cross_server() {
return 1
}
# _herdr_scope_is_pane_inherited <herdr argv...>
#
# ISSUE-34: true for a call that names an agent (agent / has-session /
# kill-session) from inside a pane whose own workspace is the exported
# HERDR_WORKSPACE_ID. A pane exports it together with HERDR_PANE_ID
# "<workspace>:<pane>"; when the two agree the id says where the caller lives,
# not where to look - a worktree creator in its own workspace must reach a
# reviewer in mam-agent on the same server. Live agent names are unique per
# server (herdr --skill: "Names must ... be unique among live agents", "IDs and
# live agent names are scoped to one server"), so a name lookup without that
# filter cannot pick a same-named agent of another workspace: the ISSUE-3 /
# F-2b hazard exists only across servers (_herdr_call_is_cross_server). An id
# that disagrees with the pane, or one set outside any pane, is still a
# deliberate scope (H-18/H-21/H-23); new-session and pane calls keep it, so a
# pane caller still creates panes in its own workspace.
_herdr_scope_is_pane_inherited() {
while [ "${1:-}" = "-L" ]; do shift 2 || return 1; done
case "${1:-}" in
agent|has-session|kill-session) ;;
*) return 1 ;;
esac
local pane="${HERDR_PANE_ID:-}"
[ -n "${HERDR_WORKSPACE_ID:-}" ] && [ -n "$pane" ] && [ "${pane%%:*}" = "$HERDR_WORKSPACE_ID" ]
}
mam_herdr() {
_init_herdr_isolation
# Built as an argv array and decided inside `if`: this runs under
@@ -1457,6 +1482,11 @@ mam_herdr() {
if [ "${MAM_SHIM_DEBUG:-0}" = "1" ]; then
echo "mam_herdr: dropping caller pane scope ids (caller server '$(_herdr_caller_server)' != target '${HERDR_SESSION_NAME:-}')" >&2
fi
elif _herdr_scope_is_pane_inherited "$@"; then
_scope_strip=(env -u HERDR_WORKSPACE_ID)
if [ "${MAM_SHIM_DEBUG:-0}" = "1" ]; then
echo "mam_herdr: '$1' looks up by name; the caller pane's own workspace ${HERDR_WORKSPACE_ID} is not a scope" >&2
fi
fi
${_scope_strip[@]+"${_scope_strip[@]}"} "$WORKSPACE_ROOT/.mam/shim/herdr" "$@"
}