Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions .github/agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
43 changes: 36 additions & 7 deletions .github/agent/pick-issue.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down Expand Up @@ -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
Expand Down
48 changes: 37 additions & 11 deletions .github/workflows/agent-loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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() {
{
Expand All @@ -269,21 +272,44 @@ jobs:
echo "</diff>"
} > "$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."
Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down