feat(issue-21): persist and forward the control-plane binding in create/resume/stop; status and reconcile honor MAM_JOBS_DIR
A session row now records the control plane it belongs to, and the pane gets it explicitly instead of relying on env inheritance. - lib_py/control_plane.py (+ thin lib.sh wrappers mam_control_plane_json / mam_control_plane_prefix): the binding is WORKSPACE_ROOT, AGENT_SESSIONS_YAML, MAM_JOBS_DIR, DELEGATE_JOB_LOGS_DIR as absolute paths. - create: the binding is always persisted on the row (`control_plane`) and forwarded to the pane only when it differs from what --workspace alone implies, as leading K=V tokens that lib.sh's new-session already turns into --env (fresh pane) or `env K=V` (reused idle pane). pane.cmd_full and start_command stay unprefixed. A binding with whitespace that would have to be forwarded is refused with exit 2 (lib.sh splits the flags unquoted); the ~/.local/bin wrapper path records the binding but warns it is not forwarded. - resume: the row's binding wins over the caller's env (a difference is reported on stderr) and is forwarded again; a legacy row without one uses the caller's binding and update_yaml_resumed.sh records it. - stop: registry order MAM_JOBS_DIR > the row's mam_jobs_dir > $WORKSPACE_ROOT/.mam/jobs > .mam/jobs, exported for delegate_publish_event. - status: both get_job_status copies look in MAM_JOBS_DIR, the row's registry and the matching audit logs before the old candidates. - reconcile: MAM_JOBS_DIR wins in the subscribe heredoc and is passed in. Tests T3-0..T3-8 (mock herdr: forwarding is verified at the herdr-call level only; real pane env propagation is left to item 7). T3-7 is a static guard. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017R3Eg7MEpSWnm1oB9ZPP6G
This commit is contained in:
1 parent
d907359746
commit
ee1d8966c1
9 files changed
+409
-11
No files matched your search
@@ -202,6 +202,22 @@ if [ -z "$CMD_FULL" ]; then
|
||||
esac
|
||||
fi
|
||||
|
||||
# Issue #21 (item 3): the control-plane binding. Persisted on the row always;
|
||||
# forwarded to the pane (leading K=V tokens that lib.sh's new-session turns
|
||||
# into --env) only when it differs from what --workspace alone implies.
|
||||
# lib.sh splits those flags unquoted (and the idle-pane path evals them), so a
|
||||
# binding with whitespace that would have to be forwarded is refused.
|
||||
CP_JSON="$(mam_control_plane_json "$WORKSPACE" 2>/dev/null || true)"
|
||||
CP_PREFIX=""
|
||||
if [ -n "$CP_JSON" ]; then
|
||||
cp_rc=0
|
||||
CP_PREFIX="$(mam_control_plane_prefix "$CP_JSON")" || cp_rc=$?
|
||||
if [ "$cp_rc" -eq 3 ]; then
|
||||
echo "ERROR: control-plane path contains whitespace; cannot forward to the pane" >&2
|
||||
exit 2
|
||||
fi
|
||||
fi
|
||||
|
||||
spawn() {
|
||||
if [ -z "$HERDR_SERVER_OPT" ]; then
|
||||
if [ -z "${HERDR_SESSION_NAME:-}" ] || [ "$HERDR_SESSION_NAME" = "default" ]; then
|
||||
@@ -213,20 +229,21 @@ spawn() {
|
||||
if { [ -x "$WRAPPER" ] && [ "$(basename "$WRAPPER")" != "claude" ]; } || [ "$USE_WRAPPER" = "1" ]; then
|
||||
SESSION_UUID=""
|
||||
CMD_FULL="$("$(_delegate_py_bin)" -m lib_py.agents spawn-spec "$AGENT" "$RESOLVED_BIN" "" "true" 2>/dev/null || echo "${RESOLVED_BIN} --dangerously-skip-permissions")"
|
||||
[ -z "$CP_PREFIX" ] || echo "WARNING: the ~/.local/bin wrapper path bypasses herdr; the control-plane binding is recorded but not forwarded" >&2
|
||||
nohup "$WRAPPER" >/dev/null 2>&1 &
|
||||
disown
|
||||
else
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CP_PREFIX$CMD_FULL"
|
||||
fi
|
||||
;;
|
||||
agy|hermes|grok|codex)
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CP_PREFIX$CMD_FULL"
|
||||
;;
|
||||
opencode)
|
||||
if [ -z "${OPENCODE_PERMISSION:-}" ]; then
|
||||
export OPENCODE_PERMISSION='{"*":"allow"}'
|
||||
fi
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CMD_FULL"
|
||||
HERDR_SESSION_NAME="$HERDR_SESSION_NAME" _herdr new-session -d -s "$SESSION_NAME" -x 140 -y 40 -c "$WORKSPACE" "$CP_PREFIX$CMD_FULL"
|
||||
;;
|
||||
*) echo "ERROR: --agent must be claude, agy, hermes, grok, opencode, or codex, got: $AGENT" >&2; exit 2 ;;
|
||||
esac
|
||||
@@ -391,7 +408,7 @@ atomic_dump_yaml "$AGENT_SESSIONS_YAML" \
|
||||
MAM_WS_LABEL="$MAM_WS_LABEL" \
|
||||
SESSION_UUID="$SESSION_UUID" \
|
||||
LAST_VISIBLE_STATUS_OVERRIDE="${LAST_VISIBLE_STATUS_OVERRIDE:-}" \
|
||||
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" ROLE="$ROLE" <<'PYEOF'
|
||||
DELEGATE_JOB_ID="$DELEGATE_JOB_ID" ROLE="$ROLE" CONTROL_PLANE_JSON="$CP_JSON" <<'PYEOF'
|
||||
name = os.environ['SESSION_NAME']
|
||||
agent = os.environ['AGENT']
|
||||
role = os.environ['ROLE']
|
||||
@@ -487,6 +504,15 @@ last_vis_override = os.environ.get('LAST_VISIBLE_STATUS_OVERRIDE', '')
|
||||
if last_vis_override:
|
||||
entry['last_visible_status'] = last_vis_override
|
||||
|
||||
# Issue #21: the control-plane binding this row was created against.
|
||||
_cp_raw = os.environ.get('CONTROL_PLANE_JSON', '')
|
||||
if _cp_raw:
|
||||
try:
|
||||
import json as _json
|
||||
_cp = _json.loads(_cp_raw)
|
||||
entry['control_plane'] = {k: _cp[k] for k in ('workspace_root', 'agent_sessions_yaml', 'mam_jobs_dir', 'delegate_job_logs_dir')}
|
||||
except Exception:
|
||||
pass
|
||||
sessions.append(entry)
|
||||
|
||||
snap = d.setdefault('snapshot', {})
|
||||
|
||||
Reference in new issue
Block a user