Lesson #1459
← Back to Knowledge Board
or-loop + wf-fix-loop + wf-survey-then-fix tooling stack (built 2026-05-05)
- ID
- 1459
- Author
- Agent
- agent-claude
- Reviewed
- ✓ Yes
- Source authority
- 75 / 100
- Source
- Reliable Python OR-orchestrator stack β replaces flaky or-agent npm SDK
- Source issue
- β
- Created at
- 2026-05-12T10:00:23.307662+00:00
- Valid until
- β
- Deprecated at
- β
- Supersedes
- β
- Obsidian path
- /root/.claude/projects/-nvmetank1-projects/memory/feedback_or_loop_tooling.md
- Obsidian hash
- 39580fab7a5c681f2319dd36128dc0cf
- Tags
- claude-memory,feedback
Content
**Built 2026-05-05 to bypass or-agent npm SDK reliability issues.** ## /usr/local/bin/or-loop (~280 LOC Python) Direct OR-API tool-loop. Tools: `read_file`, `write_file`, `edit_file` (single-replace), `list_dir`, `run_bash`. System prompt enforces "wrap-up discipline" (commit before stop). **Usage:** ```sh or-loop --cwd /repo/path --max-turns 15 --max-cost 0.30 --model qwen/qwen3-coder-plus "prompt" ``` **Defaults:** - model: `qwen/qwen3-coder-plus` ($0.20/M in, $0.80/M out) - max-turns: 25 - max-cost: $1.00 **Sandbox-locked to --cwd.** Won't escape via `..`. ## /usr/local/bin/wf-fix-loop (~180 LOC bash) End-to-end pipeline: worktree β or-loop β push β PR β merge β redeploy β verify HTTP 200. **Usage:** ```sh PR_TITLE="fix(area): ..." wf-fix-loop /nvmetank1/projects/<repo> <branch-suffix> /tmp/prompt.md [--rebuild] ``` Repo-aware: yoga rebuilds image (Dockerfile.slim), glug live-mount restart, rag-stack restart. **Failure rate ~50%** when prompt asks "find and fix N bugs" β qwen3-coder-plus over-investigates. ## /usr/local/bin/wf-survey-then-fix (~200 LOC bash) β RECOMMENDED 2-stage workflow that splits investigation from fix-application. Lower failure rate. **Usage:** ```sh wf-survey-then-fix /nvmetank1/projects/<repo> <branch-suffix> "<bug-pattern-description>" [--rebuild] ``` **Stage 1 (5-7 turns, $0.10):** or-loop produces `SURVEY.md` with 3 candidates (file:line, evidence, suggested-fix). Commits SURVEY.md. **Stage 2 (12-15 turns, $0.20):** or-loop reads SURVEY.md, applies each fix surgically. Commits. **Always produces a committable artifact** even if stage 2 stalls (SURVEY.md remains as audit trail). **When to use:** - Prefer for "find-and-fix N bugs" prompts (wf-survey-then-fix wins ~70% vs wf-fix-loop ~50%) - Use plain wf-fix-loop only when target is **specific and known** (e.g. "add csrf_check to these 3 specific routes") **Smoke-test result 2026-05-05:** PR yoga#88 (href-hash-placeholders), $0.03, 3 files +47/-7, 3 SURVEY.md candidates β all 3 fix-attempts in stage 2. ## Cost snapshot (typical runs) | Run-type | Cost | Wall-clock | |---|---|---| | or-loop trivial (smoke-test) | $0.001 | 30s | | wf-fix-loop simple-fix (1 file) | $0.04 | 4-6 min | | wf-fix-loop hunt-and-fix (3-5 files) | $0.05-0.10 | 8-12 min | | wf-survey-then-fix end-to-end | $0.03-0.10 | 4-8 min | OR balance check: `agent-quota-poll get openrouter`. ## Anti-patterns (learned the hard way) 1. **Don't ask qwen3-coder-plus to "find AND fix" in one prompt** β split via wf-survey-then-fix. 2. **Don't run multiple or-loop instances on overlapping repos** β same-file race conditions. 3. **Don't trust max-turns alone** β also set --max-cost (some prompts burn budget on 5-turn loops with huge contexts). 4. **Don't forget the wrap-up commit** β system prompt enforces it but qwen sometimes skips. Orchestrator wrappers (wf-fix-loop / wf-survey-then-fix) auto-commit-on-exit as safety net. ## Tool-call truncation fix (2026-05-05) **Symptom:** qwen (and other models on Alibaba/OR providers) returned `<400> InvalidParameter: function.arguments must be in JSON format` after `[turn N] out=4096 finish=tool_calls` β never a clean tool_call dispatch. **Root cause:** `or-loop` had `max_tokens: 4096` hardcoded. A `write_file` payload >~3KB blew the cap β JSON tool-args got truncated mid-string β upstream provider rejected. **Fix applied 2026-05-05:** raised default to 16384, env-overridable via `OR_LOOP_MAX_TOKENS`. See `/usr/local/bin/or-loop` line ~239. **How to spot the failure pattern:** `out=4096` exactly + `finish=tool_calls` + next turn HTTP 400 with InvalidParameter. Don't switch model β bump cap.