Compare commits
1 Commits
enhance/tm
...
feat/feder
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e483d976e4 |
@@ -1,33 +0,0 @@
|
|||||||
# Issue #561 — Bare python on agent hosts
|
|
||||||
|
|
||||||
## Objective
|
|
||||||
|
|
||||||
Make the durable bootstrap/provisioning guidance ensure agent hosts provide a bare `python` command that resolves to Python 3.
|
|
||||||
|
|
||||||
## Scope
|
|
||||||
|
|
||||||
- Add Debian/Ubuntu `python-is-python3` to agent-host prerequisites in bootstrap docs.
|
|
||||||
- Check for actual OS package provisioning scripts and update only if an existing agent-host package install path exists.
|
|
||||||
- Do not touch live host state.
|
|
||||||
- Do not update `docs/TASKS.md`; repo guidance says workers read it but never modify it.
|
|
||||||
|
|
||||||
## Recon
|
|
||||||
|
|
||||||
- Issue #561 confirms repeated `python: command not found` failures from fleet agents that emit `python foo.py`.
|
|
||||||
- `guides/BOOTSTRAP.md` and `packages/mosaic/framework/guides/BOOTSTRAP.md` are the source and packaged framework copies of the bootstrap guide.
|
|
||||||
- Targeted repo sweep found no agent-host Debian package provisioning script. Existing `apt-get install` hits are CI/test helper paths or unrelated deployment docs.
|
|
||||||
|
|
||||||
## Plan
|
|
||||||
|
|
||||||
1. Add a host prerequisite section to both bootstrap guide copies.
|
|
||||||
2. Include `python-is-python3` in the Debian/Ubuntu package list with an issue comment.
|
|
||||||
3. Note the non-Debian equivalent as a `/usr/bin/python -> python3` symlink.
|
|
||||||
4. Validate markdown/diff, run shell syntax checks where applicable, run required review, commit, queue guard, and push.
|
|
||||||
|
|
||||||
## Validation Log
|
|
||||||
|
|
||||||
- `rg` recon: no existing agent-host Debian package provisioning script; only CI/test helper `apt-get install` paths and unrelated deployment docs.
|
|
||||||
- `git diff --check`: passed.
|
|
||||||
- `bash -n packages/mosaic/framework/install.sh tools/install.sh packages/mosaic/framework/tools/bootstrap/init-project.sh packages/mosaic/framework/tools/_scripts/mosaic-bootstrap-repo`: passed. No touched shell scripts.
|
|
||||||
- `~/.config/mosaic/tools/codex/codex-code-review.sh --uncommitted`: approved, 0 findings.
|
|
||||||
- `pnpm format:check`: initially blocked because `node_modules` was absent and `prettier` was unavailable; `pnpm install --frozen-lockfile` initially hit an invalid `/root` pnpm store path. Reran install with `--store-dir /home/hermes/agent-work/.pnpm-store`, then `pnpm format:check` passed.
|
|
||||||
@@ -15,22 +15,6 @@ This guide covers how to bootstrap a project so AI agents (Claude, Codex, etc.)
|
|||||||
7. Branching/merging is consistent: `branch -> main` via PR with squash-only merges
|
7. Branching/merging is consistent: `branch -> main` via PR with squash-only merges
|
||||||
8. Steered-autonomy execution is enabled so agents can run end-to-end with escalation-only human intervention
|
8. Steered-autonomy execution is enabled so agents can run end-to-end with escalation-only human intervention
|
||||||
|
|
||||||
## Agent Host Prerequisites
|
|
||||||
|
|
||||||
Agent hosts must provide the Python runtime shape that runtime agents and
|
|
||||||
Mosaic automation assume is present.
|
|
||||||
|
|
||||||
For Debian/Ubuntu hosts:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt-get update
|
|
||||||
# #561: bare python invocations from agents must resolve.
|
|
||||||
sudo apt-get install -y python3 python-is-python3
|
|
||||||
```
|
|
||||||
|
|
||||||
For non-Debian hosts, install the equivalent Python 3 runtime and ensure
|
|
||||||
`/usr/bin/python` resolves to `python3` (for example, via a managed symlink).
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -15,22 +15,6 @@ This guide covers how to bootstrap a project so AI agents (Claude, Codex, etc.)
|
|||||||
7. Branching/merging is consistent: `branch -> main` via PR with squash-only merges
|
7. Branching/merging is consistent: `branch -> main` via PR with squash-only merges
|
||||||
8. Steered-autonomy execution is enabled so agents can run end-to-end with escalation-only human intervention
|
8. Steered-autonomy execution is enabled so agents can run end-to-end with escalation-only human intervention
|
||||||
|
|
||||||
## Agent Host Prerequisites
|
|
||||||
|
|
||||||
Agent hosts must provide the Python runtime shape that runtime agents and
|
|
||||||
Mosaic automation assume is present.
|
|
||||||
|
|
||||||
For Debian/Ubuntu hosts:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo apt-get update
|
|
||||||
# #561: bare python invocations from agents must resolve.
|
|
||||||
sudo apt-get install -y python3 python-is-python3
|
|
||||||
```
|
|
||||||
|
|
||||||
For non-Debian hosts, install the equivalent Python 3 runtime and ensure
|
|
||||||
`/usr/bin/python` resolves to `python3` (for example, via a managed symlink).
|
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
|||||||
@@ -87,10 +87,6 @@ message crosses the wire as base64 (`-b`) to avoid all shell-quoting hazards.
|
|||||||
|
|
||||||
- `agent-send.sh` — inter-agent wrapper (preamble + local/remote dispatch).
|
- `agent-send.sh` — inter-agent wrapper (preamble + local/remote dispatch).
|
||||||
- `send-message.sh` — low-level reliable single-pane submitter (`-b` base64 input).
|
- `send-message.sh` — low-level reliable single-pane submitter (`-b` base64 input).
|
||||||
- `auto-submit-drafts.sh` — watchdog that flushes stable unsubmitted prompt
|
|
||||||
drafts on a coordinator pane (default target `mos-claude`); run it as a
|
|
||||||
long-lived process alongside the coordinator session.
|
|
||||||
- `agent-send.test.sh` — regression + grammar lock for `agent-send.sh`.
|
|
||||||
- `test-send-message-socket.sh` — smoke test for named-socket isolation.
|
- `test-send-message-socket.sh` — smoke test for named-socket isolation.
|
||||||
|
|
||||||
## Distribution
|
## Distribution
|
||||||
|
|||||||
@@ -1,80 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# auto-submit-drafts.sh — watchdog for Claude Code panes that receive channel
|
|
||||||
# messages but leave them as unsubmitted prompt drafts. Intended for Mos only.
|
|
||||||
set -uo pipefail
|
|
||||||
|
|
||||||
TARGET="${1:-mos-claude}"
|
|
||||||
INTERVAL="${INTERVAL:-2}"
|
|
||||||
STABLE_SECONDS="${STABLE_SECONDS:-4}"
|
|
||||||
LOG_PREFIX="[auto-submit-drafts:$TARGET]"
|
|
||||||
|
|
||||||
last_prompt=""
|
|
||||||
first_seen=0
|
|
||||||
|
|
||||||
prompt_text() {
|
|
||||||
tmux capture-pane -t "$TARGET" -p 2>/dev/null | python3 -c '
|
|
||||||
import sys, re
|
|
||||||
lines = sys.stdin.read().splitlines()
|
|
||||||
idx = None
|
|
||||||
for i in range(len(lines)-1, -1, -1):
|
|
||||||
if "❯" in lines[i]:
|
|
||||||
idx = i
|
|
||||||
break
|
|
||||||
if idx is None:
|
|
||||||
raise SystemExit
|
|
||||||
parts = []
|
|
||||||
after = lines[idx].split("❯", 1)[1]
|
|
||||||
parts.append(after)
|
|
||||||
for line in lines[idx+1:]:
|
|
||||||
# Stop at Claude Code separator/border lines.
|
|
||||||
if "─" in line or "╰" in line or "╭" in line:
|
|
||||||
break
|
|
||||||
s = line.replace("\u00a0", " ")
|
|
||||||
s = re.sub(r"[\x00-\x1f\x7f]", "", s).strip()
|
|
||||||
if s:
|
|
||||||
parts.append(s)
|
|
||||||
text = " ".join(parts).replace("\u00a0", " ")
|
|
||||||
text = re.sub(r"[\x00-\x1f\x7f]", "", text).strip()
|
|
||||||
print(text)
|
|
||||||
'
|
|
||||||
}
|
|
||||||
|
|
||||||
while true; do
|
|
||||||
if ! tmux has-session -t "$TARGET" 2>/dev/null; then
|
|
||||||
echo "$LOG_PREFIX target missing; waiting" >&2
|
|
||||||
sleep "$INTERVAL"
|
|
||||||
last_prompt=""
|
|
||||||
first_seen=0
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
current="$(prompt_text || true)"
|
|
||||||
now="$(date +%s)"
|
|
||||||
|
|
||||||
if [[ -z "$current" ]]; then
|
|
||||||
last_prompt=""
|
|
||||||
first_seen=0
|
|
||||||
sleep "$INTERVAL"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$current" != "$last_prompt" ]]; then
|
|
||||||
last_prompt="$current"
|
|
||||||
first_seen="$now"
|
|
||||||
sleep "$INTERVAL"
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
|
|
||||||
age=$(( now - first_seen ))
|
|
||||||
if (( age >= STABLE_SECONDS )); then
|
|
||||||
echo "$LOG_PREFIX submitting stable draft after ${age}s: ${current:0:120}" >&2
|
|
||||||
tmux send-keys -t "$TARGET" C-j
|
|
||||||
sleep 0.8
|
|
||||||
tmux send-keys -t "$TARGET" C-m
|
|
||||||
sleep 2
|
|
||||||
last_prompt=""
|
|
||||||
first_seen=0
|
|
||||||
else
|
|
||||||
sleep "$INTERVAL"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
@@ -77,20 +77,10 @@ snippet=$(printf '%s' "$MSG" | tr '\n' ' ' | tr -s ' ' | sed 's/[^[:print:]]//g'
|
|||||||
|
|
||||||
# 1) Paste the body as a bracketed paste so multi-line content does not submit
|
# 1) Paste the body as a bracketed paste so multi-line content does not submit
|
||||||
# line-by-line. load-buffer/paste-buffer is far safer than `send-keys -l`.
|
# line-by-line. load-buffer/paste-buffer is far safer than `send-keys -l`.
|
||||||
# Buffer name MUST be unique per invocation: concurrent senders on the shared
|
printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b __mosaic_send -
|
||||||
# tmux server race a fixed name (load overwrites load, -d deletes underneath),
|
|
||||||
# cross-delivering or dropping messages — bit the fleet on the 2026-07-09
|
|
||||||
# simultaneous restart (briefs swapped between sessions).
|
|
||||||
BUF="__mosaic_send_$$_$(date +%s%N)"
|
|
||||||
printf '%s' "$MSG" | "${tmux_cmd[@]}" load-buffer -b "$BUF" -
|
|
||||||
# -p = bracketed paste when the client supports it; fall back if not.
|
# -p = bracketed paste when the client supports it; fall back if not.
|
||||||
"${tmux_cmd[@]}" paste-buffer -d -p -b "$BUF" -t "$EFFECTIVE_TARGET" 2>/dev/null \
|
"${tmux_cmd[@]}" paste-buffer -d -p -b __mosaic_send -t "$EFFECTIVE_TARGET" 2>/dev/null \
|
||||||
|| "${tmux_cmd[@]}" paste-buffer -d -b "$BUF" -t "$EFFECTIVE_TARGET" \
|
|| "${tmux_cmd[@]}" paste-buffer -d -b __mosaic_send -t "$EFFECTIVE_TARGET"
|
||||||
|| "${tmux_cmd[@]}" delete-buffer -b "$BUF" 2>/dev/null
|
|
||||||
# ^ -d deletes the buffer only on a SUCCESSFUL paste; if both attempts fail
|
|
||||||
# (e.g. the target vanished since the liveness check), delete explicitly —
|
|
||||||
# named buffers are exempt from tmux's buffer-limit eviction, so orphans
|
|
||||||
# would otherwise accumulate forever.
|
|
||||||
sleep 0.5
|
sleep 0.5
|
||||||
|
|
||||||
# 2) Submit, then verify; flush with another Enter if it is still a draft.
|
# 2) Submit, then verify; flush with another Enter if it is still a draft.
|
||||||
|
|||||||
@@ -47,32 +47,4 @@ if capture_default | grep -qF "agent socket hello"; then
|
|||||||
fail "agent-send.sh leaked named-socket message to default tmux server"
|
fail "agent-send.sh leaked named-socket message to default tmux server"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Concurrency: parallel senders on one server must not cross-deliver or drop.
|
|
||||||
# Locks the unique-per-invocation paste buffer (a fixed buffer name raced:
|
|
||||||
# load overwrote load, -d deleted underneath — messages swapped between panes).
|
|
||||||
CONC_N=5
|
|
||||||
for i in $(seq 1 "$CONC_N"); do
|
|
||||||
tmux -L "$SOCKET" new-session -d -s "conc-$i" -c "$TMPDIR" 'bash --noprofile --norc -i'
|
|
||||||
done
|
|
||||||
pids=()
|
|
||||||
for i in $(seq 1 "$CONC_N"); do
|
|
||||||
"$SEND_MESSAGE" -L "$SOCKET" -t "=conc-$i" -m "CONCPAYLOAD-${i}-END" >/dev/null &
|
|
||||||
pids+=($!)
|
|
||||||
done
|
|
||||||
for pid in "${pids[@]}"; do
|
|
||||||
wait "$pid" || fail "concurrent send-message.sh invocation exited non-zero"
|
|
||||||
done
|
|
||||||
sleep 0.2
|
|
||||||
for i in $(seq 1 "$CONC_N"); do
|
|
||||||
pane=$(tmux -L "$SOCKET" capture-pane -t "=conc-$i:0.0" -p)
|
|
||||||
printf '%s' "$pane" | grep -qF "CONCPAYLOAD-${i}-END" \
|
|
||||||
|| fail "concurrent send dropped payload for pane conc-$i"
|
|
||||||
for j in $(seq 1 "$CONC_N"); do
|
|
||||||
[ "$j" = "$i" ] && continue
|
|
||||||
if printf '%s' "$pane" | grep -qF "CONCPAYLOAD-${j}-END"; then
|
|
||||||
fail "concurrent send cross-delivered payload $j to pane conc-$i"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
|
|
||||||
echo "ok - named tmux socket send tools"
|
echo "ok - named tmux socket send tools"
|
||||||
|
|||||||
Reference in New Issue
Block a user