From a2fe3ecb83c1c6487977277ad864f990ac0b5c54 Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 21 Mar 2026 07:51:27 +0000 Subject: [PATCH 1/2] =?UTF-8?q?fix:=20formula=20agents=20run=20in=20isolat?= =?UTF-8?q?ed=20git=20worktrees=20=E2=80=94=20no=20session=20collisions=20?= =?UTF-8?q?(#460)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- lib/formula-session.sh | 39 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/lib/formula-session.sh b/lib/formula-session.sh index 21622e7..9f18c79 100644 --- a/lib/formula-session.sh +++ b/lib/formula-session.sh @@ -84,19 +84,48 @@ $(cat "$ctx_path") # ── Session management ─────────────────────────────────────────────────── # start_formula_session SESSION WORKDIR PHASE_FILE -# Kills stale session, resets phase file, creates new tmux + claude session. +# Kills stale session, resets phase file, creates a per-agent git worktree +# for session isolation, and creates a new tmux + claude session in it. +# Sets _FORMULA_SESSION_WORKDIR to the worktree path (or original workdir +# on fallback). Callers must clean up via remove_formula_worktree after +# the session ends. # Returns 0 on success, 1 on failure. start_formula_session() { local session="$1" workdir="$2" phase_file="$3" agent_kill_session "$session" rm -f "$phase_file" + + # Create per-agent git worktree for session isolation. + # Each agent gets its own CWD so Claude Code treats them as separate + # projects — no resume collisions between sequential formula runs. + _FORMULA_SESSION_WORKDIR="/tmp/disinto-${session}" + # Clean up any stale worktree from a previous run + git -C "$workdir" worktree remove "$_FORMULA_SESSION_WORKDIR" --force 2>/dev/null || true + if git -C "$workdir" worktree add "$_FORMULA_SESSION_WORKDIR" HEAD --detach 2>/dev/null; then + log "Created worktree: ${_FORMULA_SESSION_WORKDIR}" + else + log "WARNING: worktree creation failed — falling back to ${workdir}" + _FORMULA_SESSION_WORKDIR="$workdir" + fi + log "Creating tmux session: ${session}" - if ! create_agent_session "$session" "$workdir" "$phase_file"; then + if ! create_agent_session "$session" "$_FORMULA_SESSION_WORKDIR" "$phase_file"; then log "ERROR: failed to create tmux session ${session}" return 1 fi } +# remove_formula_worktree +# Removes the worktree created by start_formula_session if it differs from +# PROJECT_REPO_ROOT. Safe to call multiple times. No-op if no worktree was created. +remove_formula_worktree() { + if [ -n "${_FORMULA_SESSION_WORKDIR:-}" ] \ + && [ "$_FORMULA_SESSION_WORKDIR" != "${PROJECT_REPO_ROOT:-}" ]; then + git -C "$PROJECT_REPO_ROOT" worktree remove "$_FORMULA_SESSION_WORKDIR" --force 2>/dev/null || true + log "Removed worktree: ${_FORMULA_SESSION_WORKDIR}" + fi +} + # formula_phase_callback PHASE # Standard crash-recovery phase callback for formula sessions. # Requires globals: SESSION_NAME, PHASE_FILE, PROJECT_REPO_ROOT, PROMPT. @@ -113,7 +142,7 @@ formula_phase_callback() { fi _FORMULA_CRASH_COUNT=$(( ${_FORMULA_CRASH_COUNT:-0} + 1 )) log "WARNING: tmux session died unexpectedly — attempting recovery" - if create_agent_session "${_MONITOR_SESSION:-$SESSION_NAME}" "$PROJECT_REPO_ROOT" "$PHASE_FILE" 2>/dev/null; then + if create_agent_session "${_MONITOR_SESSION:-$SESSION_NAME}" "${_FORMULA_SESSION_WORKDIR:-$PROJECT_REPO_ROOT}" "$PHASE_FILE" 2>/dev/null; then agent_inject_into_session "${_MONITOR_SESSION:-$SESSION_NAME}" "$PROMPT" log "Recovery session started" else @@ -234,5 +263,9 @@ run_formula_and_monitor() { fi matrix_send "$agent_name" "${agent_name^} session finished (${FINAL_PHASE:-no phase})" 2>/dev/null || true + + # Clean up per-agent worktree — "the runtime creates and destroys" + remove_formula_worktree + log "--- ${agent_name^} run done ---" } From d19160a65809781e38910faa71a2687dbee6d671 Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 21 Mar 2026 07:53:29 +0000 Subject: [PATCH 2/2] =?UTF-8?q?ci:=20retrigger=20=E2=80=94=20push=20pipeli?= =?UTF-8?q?ne=20#651=20flaked=20(PR=20pipeline=20#652=20passed=20same=20co?= =?UTF-8?q?mmit)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- BOOTSTRAP.md | 16 ++++++++++++++++ action/action-agent.sh | 0 action/action-poll.sh | 0 3 files changed, 16 insertions(+) mode change 100644 => 100755 action/action-agent.sh mode change 100644 => 100755 action/action-poll.sh diff --git a/BOOTSTRAP.md b/BOOTSTRAP.md index a9b83aa..336327a 100644 --- a/BOOTSTRAP.md +++ b/BOOTSTRAP.md @@ -302,3 +302,19 @@ Meanwhile: | Approved PRs never merge (HTTP 405) | `review_bot` not in merge/approvals whitelist | Add as write collaborator; set both `approvals_whitelist_username` and `merge_whitelist_usernames` in branch protection | | Dev-agent churns through issues without waiting for open PRs to land | No single-threaded enforcement | `WAITING_PRS` check in dev-poll holds new work — verify TOML `name` is consistent across invocations | | Label ping-pong (issue reopened then immediately re-closed) | `already_done` handler doesn't close issue | Review dev-agent log; `already_done` status should auto-close the issue | + +## Action Runner — disinto (harb-staging) + +Added 2026-03-19. Polls disinto repo for `action`-labeled issues. + +``` +*/5 * * * * cd /home/debian/dark-factory && bash action/action-poll.sh projects/disinto.toml >> /tmp/action-disinto-cron.log 2>&1 +``` + +Runs locally on harb-staging — same box where Caddy/site live. For formulas that need local resources (publish-site, etc). + +### Fix applied: action-agent.sh needs +x +The script wasn't executable after git clone. Run: +```bash +chmod +x action/action-agent.sh action/action-poll.sh +``` diff --git a/action/action-agent.sh b/action/action-agent.sh old mode 100644 new mode 100755 diff --git a/action/action-poll.sh b/action/action-poll.sh old mode 100644 new mode 100755