fix: address review — guard grooming in gardener-poll.sh, doc fixes

- Add --recipes-only flag to gardener-poll.sh to skip grooming call when
  invoked by the formula's ci-escalation-recipes step (prevents double-run)
- Update formula step to pass --recipes-only
- Add lib/file-action-issue.sh to AGENTS.md shared helpers table
- Clarify TOML arg scope in gardener trigger description
- Fix log prefixes in gardener-run.sh (poll: → run:)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
openhands 2026-03-20 13:02:33 +00:00
parent 59b6d76afa
commit cc6a958245
4 changed files with 23 additions and 8 deletions

View file

@ -115,7 +115,8 @@ Claude to fix or escalate to a human via Matrix.
**Trigger**: `gardener-run.sh` runs 2x/day via cron. It files an `action`
issue referencing `formulas/run-gardener.toml`; the [action-agent](#action-action)
picks it up and executes the gardener steps in an interactive Claude tmux session.
Accepts an optional project TOML argument.
Accepts an optional project TOML argument (configures which project the action
issue is filed against).
**Key files**:
- `gardener/gardener-run.sh` — Cron wrapper: lock, memory guard, dedup check, files action issue
@ -283,6 +284,7 @@ sourced as needed.
| `lib/load-project.sh` | Parses a `projects/*.toml` file into env vars (`PROJECT_NAME`, `CODEBERG_REPO`, `WOODPECKER_REPO_ID`, monitoring toggles, Matrix config, etc.). | env.sh (when `PROJECT_TOML` is set), supervisor-poll (per-project iteration) |
| `lib/parse-deps.sh` | Extracts dependency issue numbers from an issue body (stdin → stdout, one number per line). Matches `## Dependencies` / `## Depends on` / `## Blocked by` sections and inline `depends on #N` patterns. Not sourced — executed via `bash lib/parse-deps.sh`. | dev-poll, supervisor-poll |
| `lib/matrix_listener.sh` | Long-poll Matrix sync daemon. Dispatches thread replies to the correct agent via well-known files (`/tmp/{agent}-escalation-reply`). Handles supervisor, gardener, dev, review, vault, and action reply routing. Run as systemd service. | Standalone daemon |
| `lib/file-action-issue.sh` | `file_action_issue()` — dedup check, label lookup, and issue creation for formula-driven cron wrappers. Sets `FILED_ISSUE_NUM` on success. | gardener-run.sh, planner-poll.sh |
| `lib/agent-session.sh` | Shared tmux + Claude session helpers: `create_agent_session()`, `inject_formula()`, `agent_wait_for_claude_ready()`, `agent_inject_into_session()`, `agent_kill_session()`, `monitor_phase_loop()`, `read_phase()`. `create_agent_session(session, workdir, [phase_file])` optionally installs a PostToolUse hook (matcher `Bash\|Write`) that detects phase file writes in real-time — when Claude writes to the phase file, the hook writes a marker so `monitor_phase_loop` reacts on the next poll instead of waiting for mtime changes. Also installs a StopFailure hook (matcher `rate_limit\|server_error\|authentication_failed\|billing_error`) that writes `PHASE:failed` with an `api_error` reason to the phase file and touches the phase-changed marker, so the orchestrator discovers API errors within one poll cycle instead of waiting for idle timeout. When `MATRIX_THREAD_ID` is exported, also installs a Stop hook (`on-stop-matrix.sh`) that streams each Claude response to the Matrix thread. `monitor_phase_loop` sets `_MONITOR_LOOP_EXIT` to one of: `done`, `idle_timeout`, `idle_prompt` (Claude returned to `` for 3 consecutive polls without writing any phase — callback invoked with `PHASE:failed`, session already dead), `crashed`, or a `PHASE:*` string. Agents must handle `idle_prompt` in both their callback and their post-loop exit handler. | dev-agent.sh, gardener-agent.sh, action-agent.sh |
---

View file

@ -191,7 +191,7 @@ needs = ["grooming"]
id = "ci-escalation-recipes"
title = "CI escalation recipes (bash — gardener-poll.sh)"
executor = "bash"
script = "gardener/gardener-poll.sh"
script = "gardener/gardener-poll.sh --recipes-only"
description = """
NOT a Claude step executed by gardener-poll.sh before/after the Claude session.
Documented here so the formula covers the full gardener run.

View file

@ -25,8 +25,16 @@ set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
FACTORY_ROOT="$(dirname "$SCRIPT_DIR")"
# --recipes-only: skip grooming (used by formula ci-escalation-recipes step
# to avoid double-running grooming which the formula handles as its own step)
RECIPES_ONLY=0
if [ "${1:-}" = "--recipes-only" ]; then
RECIPES_ONLY=1
shift
fi
# Load shared environment (with optional project TOML override)
# Usage: gardener-poll.sh [projects/harb.toml]
# Usage: gardener-poll.sh [--recipes-only] [projects/harb.toml]
export PROJECT_TOML="${1:-}"
# shellcheck source=../lib/env.sh
source "$FACTORY_ROOT/lib/env.sh"
@ -108,8 +116,13 @@ Instructions:
done
# ── Backlog grooming (delegated to gardener-agent.sh) ────────────────────
log "Invoking gardener-agent.sh for backlog grooming"
bash "$SCRIPT_DIR/gardener-agent.sh" "${1:-}" || log "WARNING: gardener-agent.sh exited with error"
# Skipped with --recipes-only (formula's grooming step handles this)
if [ "$RECIPES_ONLY" -eq 0 ]; then
log "Invoking gardener-agent.sh for backlog grooming"
bash "$SCRIPT_DIR/gardener-agent.sh" "${1:-}" || log "WARNING: gardener-agent.sh exited with error"
else
log "Skipping grooming (--recipes-only mode)"
fi
# ── Recipe matching engine ────────────────────────────────────────────────

View file

@ -27,7 +27,7 @@ log() { echo "[$(date -u +%Y-%m-%dT%H:%M:%S)Z] $*" >> "$LOG_FILE"; }
if [ -f "$LOCK_FILE" ]; then
LOCK_PID=$(cat "$LOCK_FILE" 2>/dev/null || true)
if [ -n "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2>/dev/null; then
log "poll: gardener-run running (PID $LOCK_PID)"
log "run: gardener-run running (PID $LOCK_PID)"
exit 0
fi
rm -f "$LOCK_FILE"
@ -38,7 +38,7 @@ trap 'rm -f "$LOCK_FILE"' EXIT
# ── Memory guard ──────────────────────────────────────────────────────────
AVAIL_MB=$(free -m | awk '/Mem:/{print $7}')
if [ "${AVAIL_MB:-0}" -lt 2000 ]; then
log "poll: skipping — only ${AVAIL_MB}MB available (need 2000)"
log "run: skipping — only ${AVAIL_MB}MB available (need 2000)"
exit 0
fi
@ -60,7 +60,7 @@ _rc=0
file_action_issue "run-gardener" "action: run-gardener — periodic housekeeping" "$ISSUE_BODY" || _rc=$?
case "$_rc" in
0) ;;
1) log "poll: open run-gardener action issue already exists — skipping"
1) log "run: open run-gardener action issue already exists — skipping"
log "--- Gardener run done ---"
exit 0 ;;
2) log "ERROR: 'action' label not found — cannot file gardener issue"