diff --git a/.agents/README.md b/.agents/README.md index b0a99e0..4f60c17 100644 --- a/.agents/README.md +++ b/.agents/README.md @@ -24,7 +24,7 @@ bin/dev CLI: `dev implement --harness [--dry-run]` - **Groom** (interactive, Claude): `/groom ` runs a seeded grill-me → writes a Ready spec into the issue body → sets Effort/Priority → leaves Stage at `Research`. You review and flip to `Ready`. -- **Implement** (any harness): `.agents/bin/dev implement --harness ` +- **Implement** (recommended harnesses: `codex` or `cursor`): `.agents/bin/dev implement --harness ` (or `/implement …` in Claude to supervise) → isolated worktree → runs the harness headless on the spec → build gate → opens a PR → sets Stage `In Review`. - **Review + merge**: use the `review` skill from a harness different from the @@ -42,8 +42,17 @@ local adapter state. ## Adding / fixing a harness Edit one function in `harnesses.sh`: `harness_ `, running the -tool non-interactively in `` on the prompt. `claude`/`codex`/`opencode` are -wired; `pi`/`cursor` are stubs — confirm their headless flags before trusting. +tool non-interactively in `` on the prompt. For current implementation +dispatch, prefer `codex` or `cursor`; `claude` and `opencode` remain available, and +`pi` still needs flag verification before trusting. + +`agy` / Antigravity is deliberately disabled for non-dry-run dispatch as of +2026-07-04. Phase 1 testing found the headless path unreliable: with +`--new-project` it ignored the prompt and tried to scaffold, while without it the CLI +could resume a stale conversation and hang past the print timeout. Keep using +`.agents/bin/dev implement --harness agy --dry-run` for prompt inspection +only; real dispatch should use `codex` or `cursor` until a fresh Antigravity +headless invocation is verified and documented. Always `--dry-run` a new harness first: it renders the exact prompt and plan, touching nothing (no worktree, PR, or state change). diff --git a/.agents/bin/dev b/.agents/bin/dev index 92e59ef..3fd59d3 100755 --- a/.agents/bin/dev +++ b/.agents/bin/dev @@ -1,7 +1,7 @@ #!/usr/bin/env bash # .agents/bin/dev — box-box agentic dev CLI. Works from any harness's shell. # -# dev implement --harness [--dry-run] [--base ] +# dev implement --harness [--dry-run] [--base ] # # Grooming is driven interactively via the Claude Code /groom skill; this CLI covers # the implement lane (dispatch a Ready issue to a harness → worktree → gate → PR). @@ -15,7 +15,7 @@ box-box dev CLI dev implement --harness [--dry-run] [--base ] -harnesses: claude, codex, opencode (supported) · pi, cursor (verify flags in .agents/harnesses.sh) +harnesses: codex, cursor (recommended) · claude, opencode (available) · agy (dry-run only, disabled for dispatch) · pi (verify flags) --dry-run render the prompt + plan, touch nothing (no worktree/PR/state change) --base base branch for the worktree/PR (default: main) EOF diff --git a/.agents/harnesses.sh b/.agents/harnesses.sh index 0142a2a..295044a 100644 --- a/.agents/harnesses.sh +++ b/.agents/harnesses.sh @@ -11,6 +11,18 @@ # ---- MUST-HAVE ---- +harness_disabled_reason() { # -> reason on stdout; 0 means disabled + case "$1" in + agy) + cat <<'EOF' +agy is disabled for implementation dispatch as of 2026-07-04: Antigravity headless mode was observed to ignore prompts with --new-project, resume stale conversations without it, and hang past print timeouts. Use codex or cursor until a fresh headless invocation is verified. +EOF + return 0 + ;; + *) return 1 ;; + esac +} + harness_claude() { # Claude Code — print mode, auto-accept edits local dir="$1" prompt="$2" ( cd "$dir" && claude -p "$(cat "$prompt")" --permission-mode acceptEdits ) @@ -31,14 +43,11 @@ harness_cursor() { # Cursor CLI agent — composer-2.5, hea ( cd "$dir" && cursor-agent -p "$(cat "$prompt")" --model composer-2.5 --force --trust ) } -harness_agy() { # Antigravity CLI — UNRELIABLE headless (2026-07): with - # --new-project it ignores the prompt and asks to scaffold a project; without it, it - # resumes the previous conversation (silently keeping its old model — --model only - # applies to new conversations) and can hang past the print timeout. Do not trust for - # dispatch until fixed upstream; verify with a trivial prompt first. - local dir="$1" prompt="$2" - ( cd "$dir" && agy --print --print-timeout 60m \ - --model="Gemini 3.1 Pro (High)" --dangerously-skip-permissions "$(cat "$prompt")" ) +harness_agy() { # Antigravity CLI — disabled until headless is verified + local reason + reason="$(harness_disabled_reason agy)" + echo "harness_agy: $reason" >&2 + return 2 } # ---- NICE-TO-HAVE (verify the exact invocation for your version before trusting) ---- diff --git a/.agents/lib/dispatch.sh b/.agents/lib/dispatch.sh index 6be654e..64d911d 100644 --- a/.agents/lib/dispatch.sh +++ b/.agents/lib/dispatch.sh @@ -52,6 +52,15 @@ dispatch() { # [--dry-run] [--base ] if ! declare -f "harness_$harness" >/dev/null 2>&1; then echo "no adapter for harness '$harness' — add harness_$harness() to .agents/harnesses.sh" >&2; return 2 fi + if [ "$dry" != 1 ] && declare -f harness_disabled_reason >/dev/null 2>&1; then + local disabled_reason + if disabled_reason="$(harness_disabled_reason "$harness")"; then + echo "harness '$harness' is disabled for non-dry-run dispatch." >&2 + echo " $disabled_reason" >&2 + echo " Use --dry-run for prompt inspection, or dispatch with --harness codex/cursor." >&2 + return 2 + fi + fi local repo_root title body slug branch wt prompt repo_root="$(git rev-parse --show-toplevel)" || return 1 diff --git a/.agents/test/dispatch_disabled_harness_test.sh b/.agents/test/dispatch_disabled_harness_test.sh new file mode 100755 index 0000000..56a3312 --- /dev/null +++ b/.agents/test/dispatch_disabled_harness_test.sh @@ -0,0 +1,59 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)" +cd "$ROOT" + +# shellcheck source=/dev/null +source "$ROOT/.agents/lib/dispatch.sh" + +issue_title() { echo "Disable agy harness smoke"; } +issue_body() { echo "## Spec"; echo; echo "Smoke prompt body"; } +get_field() { echo "Ready"; } +set_stage() { echo "unexpected set_stage $*" >&2; return 99; } +run_gate() { echo "unexpected run_gate $*" >&2; return 99; } + +unexpected_git_file="$(mktemp "${TMPDIR:-/tmp}/boxbox-agy-git.XXXX")" +rm -f "$unexpected_git_file" +git() { + if [ "${1:-}" = "rev-parse" ]; then + command git "$@" + return + fi + echo "unexpected git $*" >&2 + touch "$unexpected_git_file" + return 99 +} + +set +e +non_dry_output="$(dispatch 47 agy 2>&1)" +non_dry_status=$? +set -e + +[ "$non_dry_status" -eq 2 ] || { + echo "expected agy non-dry-run to exit 2, got $non_dry_status" >&2 + echo "$non_dry_output" >&2 + exit 1 +} +[[ "$non_dry_output" == *"harness 'agy' is disabled"* ]] || { + echo "expected disabled-harness message" >&2 + echo "$non_dry_output" >&2 + exit 1 +} +[ ! -e "$unexpected_git_file" ] || { + echo "agy non-dry-run reached git before failing" >&2 + echo "$non_dry_output" >&2 + exit 1 +} + +dry_output="$(dispatch 47 agy --dry-run 2>&1)" +[[ "$dry_output" == *"[dry-run] no worktree / harness / PR / state change"* ]] || { + echo "expected agy dry-run to render dispatch preview" >&2 + echo "$dry_output" >&2 + exit 1 +} +[[ "$dry_output" == *"Smoke prompt body"* ]] || { + echo "expected agy dry-run prompt body" >&2 + echo "$dry_output" >&2 + exit 1 +}