diff --git a/.github/agent/README.md b/.github/agent/README.md index c884fd4..681a4f8 100644 --- a/.github/agent/README.md +++ b/.github/agent/README.md @@ -38,10 +38,38 @@ supervises: daily report, stuck-PR detector, watchdog (prompts under 3. Run `scripts/setup-github.sh` (or `gh label create`) so the labels `agent-pr`, `agent:wip`, `agent:failed`, `agent:blocked` exist. 4. **`CURSOR_API_KEY`** (Actions secret, OPTIONAL): API key from the Cursor - dashboard. With it, `area: frontend` issues are implemented by the Cursor - CLI (`gpt-5.3-codex`) instead of OpenCode; without it every issue falls - back to OpenCode automatically. The reviewer always runs on OpenCode - (cross-engine review by design). + dashboard. With it: (a) `area: frontend` issues are implemented by the + Cursor CLI (`composer-2.5`, Cursor's agent-native model — cheapest in the + Pro plan's included "Cursor Models" pool) instead of OpenCode, and (b) the + reviewer tries frontier judgment first (`gpt-5.3-codex`, then `gpt-5.6-sol`, + both from Cursor's paid "Other Models" pool) before falling back to + OpenCode's `kimi-k3`. Without the secret, every issue implements on + OpenCode and the reviewer goes straight to `kimi-k3`. + +## Model routing + +Full fallback chains (2-3 models per task, cheapest-viable-first except where +judgment quality matters more than cost) are documented as comments next to +the constants in `pick-issue.sh` and in the Review step of +`agent-loop.yml`. Summary: + +| Task | Primary | Fallback(s) | Rationale | +|---|---|---|---| +| Review (judgment/security) | `gpt-5.3-codex` (Cursor) | `gpt-5.6-sol` (Cursor) → `kimi-k3` (OpenCode) | Frontier reasoning first, "chino premium" as the guaranteed final attempt | +| Implementation (general) | `kimi-k3` (OpenCode) | `glm-5.2` → `qwen3.7-max` | Chinese OSS models, flagship-class | +| Implementation (`area: docs`) | `minimax-m3` (OpenCode) | `deepseek-v4-flash` → `mimo-v2.5` | Cheapest tier, prose-heavy work | +| Implementation (`area: tests`) | `minimax-m3` (OpenCode) | `glm-5.2` → `deepseek-v4-flash` | Cheap but escalates for logic-heavy tests | +| Implementation (`area: frontend`) | `composer-2.5` (Cursor) | `grok-4.5`/`grok-4.6` | Included pool, no draw on the paid allowance | + +The Review cascade and the frontend fallback both draw from Cursor's same +$20/mo "Other Models" allowance when they escalate past the included pool — +if both escalate heavily in the same billing window they compete for it. +Several model ids above (Cursor's `gpt-5.3-codex`/`gpt-5.6-sol`, OpenCode's +`glm-5.2`/`qwen3.7-max`/`deepseek-v4-flash`/`mimo-v2.5`) were sourced from +web research in 2026-08 and are **not yet verified** against a live +`cursor-agent --list-models` / OpenCode catalog call — sanity-check with a +`workflow_dispatch` run on a low-stakes issue before trusting the 3x/day cron +on them. Until both secrets exist the workflow runs but disarms itself at the first step (no failures, no noise). diff --git a/.github/agent/pick-issue.sh b/.github/agent/pick-issue.sh index 83db1e6..6235733 100755 --- a/.github/agent/pick-issue.sh +++ b/.github/agent/pick-issue.sh @@ -14,16 +14,43 @@ # Engine routing: frontend issues go to Cursor (Codex-class models on the # Cursor subscription); everything else goes to OpenCode Go. If CURSOR_API_KEY # is not configured the workflow falls back to OpenCode, so the cursor route -# is best-effort. The reviewer always runs on OpenCode (cross-engine review). +# is best-effort. The reviewer runs its own frontier-first cascade (Cursor's +# "Other Models" pool, then OpenCode kimi-k3) — see agent-loop.yml's Review +# step, not this file. set -euo pipefail ALLOWED_AUTHORS='["VforVitorio", "Santisoutoo"]' EXCLUDED_LABELS='["epic", "agent:wip", "agent:blocked", "area: ci-cd", "question", "wontfix", "duplicate", "invalid"]' + +# Implementation (worker), general issues — Chinese OSS models via OpenCode +# Go, cheapest-first among flagship-class options. Fallback chain if kimi-k3's +# $-cap is exhausted (manual swap here, no auto-retry): +# 1) opencode-go/kimi-k3 Moonshot, flagship, $3.00/$15.00 per M (current) +# 2) opencode-go/glm-5.2 Zhipu, $1.40/$4.40 per M — cheaper flagship-class alt +# 3) opencode-go/qwen3.7-max Alibaba, $2.50/$7.50 per M — architecture-leaning alt MODEL_TOP="opencode-go/kimi-k3" -MODEL_CHEAP="opencode-go/minimax-m3" -# Composer is Cursor's agent-native model: fastest and lightest on the Pro -# quota. If weekly metrics show quality lagging on frontend issues, upgrade -# to claude-sonnet-5-thinking-medium here. + +# Implementation for area: docs — prose-heavy, cheapest tier. Fallback chain: +# 1) opencode-go/minimax-m3 MiniMax, $0.30/$1.20 per M (current) +# 2) opencode-go/deepseek-v4-flash DeepSeek, $0.14/$0.22-0.66 per M — cheapest, high volume +# 3) opencode-go/mimo-v2.5 Xiaomi, $0.14/$0.28 per M — equally cheap, different vendor +MODEL_DOCS="opencode-go/minimax-m3" + +# Implementation for area: tests — needs to read real code (asserts, mocks, +# edge cases), not pure prose, so it gets an escalation option docs doesn't. +# Fallback chain: +# 1) opencode-go/minimax-m3 MiniMax, $0.30/$1.20 per M (current) — mechanical fixtures/asserts +# 2) opencode-go/glm-5.2 Zhipu, $1.40/$4.40 per M — escalate for tests needing complex logic +# 3) opencode-go/deepseek-v4-flash DeepSeek, $0.14/$0.22-0.66 per M — ultra-cheap for large/simple batches +MODEL_TESTS="opencode-go/minimax-m3" + +# Cursor engine (area: frontend), all in the Pro plan's included "Cursor +# Models" pool (no draw on the $20/mo "Other Models" allowance). Fallback: +# 1) composer-2.5 agent-native, cheapest in the included pool (current) +# 2) grok-4.5 / grok-4.6 same included pool, alt if Composer quality/quota lags +# Escalating further to gpt-5.3-codex (Other Models pool) is possible but +# competes with the reviewer's frontier budget in agent-loop.yml — reserve +# for genuinely hard frontend issues, not a routine swap. MODEL_CURSOR="composer-2.5" fetch_candidates() { @@ -70,8 +97,10 @@ derive_model() { local engine="$1" labels="$2" if [ "$engine" = "cursor" ]; then echo "$MODEL_CURSOR" - elif echo "$labels" | jq -e 'index("area: docs") or index("area: tests")' >/dev/null; then - echo "$MODEL_CHEAP" + elif echo "$labels" | jq -e 'index("area: tests")' >/dev/null; then + echo "$MODEL_TESTS" + elif echo "$labels" | jq -e 'index("area: docs")' >/dev/null; then + echo "$MODEL_DOCS" else echo "$MODEL_TOP" fi diff --git a/.github/workflows/agent-loop.yml b/.github/workflows/agent-loop.yml index b1a5784..05b25ba 100644 --- a/.github/workflows/agent-loop.yml +++ b/.github/workflows/agent-loop.yml @@ -132,7 +132,10 @@ jobs: run: npm install -g opencode-ai@1.18.21 - name: Install Cursor CLI - if: steps.pick.outputs.engine == 'cursor' + # Needed whenever the secret exists, not only when the worker routes + # to Cursor: the reviewer's frontier cascade (below) tries Cursor's + # "Other Models" pool first regardless of which engine implemented. + if: steps.pick.outputs.empty == 'false' && secrets.CURSOR_API_KEY != '' run: | curl -fsS https://cursor.com/install | bash echo "$HOME/.local/bin" >> "$GITHUB_PATH" @@ -248,12 +251,12 @@ jobs: # ---- Post-PR inline review threads would deadlock the merge # ---- (required_conversation_resolution), so all review happens here. # ---- One fix round allowed; a second FIX verdict fails the run. - - name: Review (OpenCode reviewer) + - name: Review (Cursor frontier, chino-premium fallback) if: steps.implement.outputs.result == 'DONE' env: OPENCODE_CONFIG: ${{ github.workspace }}/.github/agent/opencode.json ISSUE_TITLE: ${{ steps.pick.outputs.title }} - CURSOR_API_KEY: ${{ steps.pick.outputs.engine == 'cursor' && secrets.CURSOR_API_KEY || '' }} + CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }} run: | compose_review_prompt() { { @@ -269,21 +272,44 @@ jobs: echo "" } > "$RUNNER_TEMP/review-prompt.txt" } - run_review() { + # Reviewer model cascade — judgment on security/correctness gets the + # strongest reasoning we can afford, cheapest-first fallback only + # when a step is unavailable or gives no parseable verdict: + # 1) gpt-5.3-codex Cursor "Other Models" pool — frontier, coding-specialised + # 2) gpt-5.6-sol Cursor "Other Models" pool — frontier alt, same pool/provider + # 3) opencode-go/kimi-k3 OpenCode Go — "chino premium", no extra secret needed, + # the guaranteed final attempt if CURSOR_API_KEY is unset or both frontier + # attempts fail (e.g. the $20/mo Other-Models allowance is exhausted). + # tee to stderr: each attempt's stdout must carry ONLY the verdict + # (it is captured with $(...)), while the log still shows the review. + run_review_attempt() { + local engine="$1" model="$2" compose_review_prompt - # Reviewer is ALWAYS OpenCode top model, regardless of the - # implementing engine — cross-engine review, and the picked model - # id may not even exist on opencode (cursor models). - # tee to stderr: this function's stdout must carry ONLY the verdict - # (it is captured with $(...)), while the log still shows the review. - opencode run --model "opencode-go/kimi-k3" "$(cat "$RUNNER_TEMP/review-prompt.txt")" \ + set +e + bash .github/agent/run-engine.sh "$engine" "$model" "$RUNNER_TEMP/review-prompt.txt" \ | tee "$RUNNER_TEMP/review-output.txt" >&2 + set -e grep -oE 'VERDICT: (APPROVE|FIX)' "$RUNNER_TEMP/review-output.txt" | tail -1 | cut -d' ' -f2 } + run_review() { + local verdict="" + if [ -n "${CURSOR_API_KEY:-}" ]; then + verdict=$(run_review_attempt cursor gpt-5.3-codex) + if [ -z "$verdict" ]; then + echo "Reviewer: gpt-5.3-codex gave no verdict, trying gpt-5.6-sol." >&2 + verdict=$(run_review_attempt cursor gpt-5.6-sol) + fi + fi + if [ -z "$verdict" ]; then + echo "Reviewer: falling back to opencode-go/kimi-k3." >&2 + verdict=$(run_review_attempt opencode "opencode-go/kimi-k3") + fi + echo "$verdict" + } verdict=$(run_review) if [ "$verdict" = "APPROVE" ]; then exit 0; fi if [ "$verdict" != "FIX" ]; then - echo "::error::Reviewer emitted no parseable verdict (infra)." + echo "::error::Reviewer emitted no parseable verdict from any model in the cascade (infra)." exit 1 fi echo "Reviewer requested fixes; running one fix round." diff --git a/README.md b/README.md index 9464989..c513c02 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,24 @@ Toda la API vive bajo `/api/v1/*`. Documentación interactiva en `/docs`; invent Flujo trunk-based: rama `feat/xxx` / `fix/xxx` / `docs/xxx` desde `main`, PR de vuelta a `main`, CI (`test`, `lint`, `typecheck`) en verde y sin squash — se preserva el histórico completo. +## Desarrollo autónomo (loop engineering) + +El roadmap restante se completa mediante un loop de agentes autónomo +(`agent-loop`, GitHub Actions) que recoge issues, implementa, revisa y abre +PR sin intervención humana, tres veces al día. Cada tarea del loop tiene su +propio agente/modelo, elegido por coste-beneficio: + +| Tarea | Modelo principal | Alternativas | +|---|---|---| +| Revisión (juicio/seguridad, pre-PR) | GPT-5.3 Codex (OpenAI, vía Cursor) | GPT-5.6 Sol (OpenAI) → Kimi K3 (Moonshot) como respaldo garantizado | +| Implementación general | Kimi K3 (Moonshot) | GLM-5.2 (Zhipu), Qwen3.7 Max (Alibaba) | +| Implementación — documentación | MiniMax M3 | DeepSeek V4 Flash, MiMo v2.5 (Xiaomi) | +| Implementación — tests | MiniMax M3 | GLM-5.2 (lógica compleja) | +| Implementación — frontend | Composer 2.5 (Cursor) | Grok 4.5 / 4.6 (Cursor) | + +Detalle de las cadenas de fallback y la política de coste en +[`.github/agent/README.md`](.github/agent/README.md). + ## Créditos Este proyecto existe gracias a [legalize-es](https://github.com/legalize-dev/legalize-es), que recopila y versiona legislación española en Markdown.