2cbe62a70c
Bundles a long-overdue set of fixes that surfaced while watching the single-PR pipeline test against PR #30 the morning of 2026-05-07. # F1 — long-worker liveness contract The dispatcher heartbeat was only refreshed *between* worker sessions. On a 30-minute review the heartbeat file went stale, and any heartbeat-watchdog (dispatchers-launcher.sh / cleveragents-dispatchers.service) would SIGTERM a perfectly-healthy worker mid-cycle, orphaning the OpenCode session and the auto/claimed-* lock. ``_opencode_worker.run_session_blocking`` now accepts an ``on_poll`` callback fired once per status-poll iteration; ``_dispatch_runtime.dispatch_one`` and ``conflict_drive.py`` wire it to ``write_heartbeat(cfg.heartbeat_path)``. Callback exceptions are logged and swallowed so a transient EROFS on the heartbeat path can never mask a successful worker completion. # F2 — in-flight cycle visibility (schema v5) The ``dispatch_*_cycles`` tables previously only recorded a row at cycle *end*. While a worker was running, the operator's only signal was the heartbeat file — and even that became stale (see F1). Schema bumped to v5: ``ended_at`` is now nullable and ``cycle_id`` carries a UNIQUE index. ``begin_cycle`` writes the in-flight row at start; ``finish_cycle`` updates it at end. ``run_one_cycle``'s try/finally guarantees ``finish_cycle`` runs even when ``collect_candidates`` / ``dispatch_one`` raises, so an orphan ``ended_at IS NULL`` can no longer be stuck forever after a crash. The v4→v5 migration is now defined in ONE place — a set of helpers in ``_forgejo_cache.py`` (``DISPATCH_CYCLE_TABLES``, ``_dispatch_cycle_create_sql``, ``_dispatch_cycle_index_sqls``, ``migrate_dispatch_cycle_table_to_v5``, ``ensure_dispatch_cycle_schema``). Both ``ForgejoCache._migrate_to_v5_in_flight_rows`` and ``_dispatch_runtime.ensure_cycle_table`` import from there, eliminating the drift risk of the previous duplicated DDL. Pre-existing rows are preserved verbatim across the migration. # F3 — telemetry surface for the new state ``/api/health`` now returns ``in_flight_cycle: {cycle_id, started_at, session_id, candidates_count, elapsed_s}`` per dispatcher daemon and a ``running_long_worker: bool`` flag (heartbeat older than 600s AND a matching pid alive — should never fire under healthy F1 operation, so when it does it points at a real bug). The Drivers and Overview tabs in ``.opencode/telemetry/{index.html,app.js,style.css}`` render in-flight rows with a tinted background + "in flight" pill, daemon tiles get a dashed border for the long-worker state, and each tile shows the running cycle's id + elapsed time inline. # Fix A — reasoningEffort high → medium for pr-review-worker On its own that change alone would not have been enough, but combined with Fix C below it dropped a representative cycle from "timed out at 30:00" to a target ~2-3min. Pure config change in ``.opencode/agents/pr-review-worker.md``; no code path touched. # Fix C — pre-fetch PR diff in dispatch_review and embed in prompt The reviewer used to spawn a ``git-isolator-util`` subagent, which shelled out to ``git clone``, ``git fetch``, and ``git diff master...HEAD``. That subagent burned 90+ seconds and several token budgets per cycle. ``dispatch_review.py`` now fetches the unified diff via the Forgejo ``/pulls/{n}.diff`` endpoint and embeds it into the worker prompt under an ``UNTRUSTED CONTENT`` fence with explicit BEGIN_PR_DIFF / END_PR_DIFF markers, head_sha pinning, character-count metadata, and END marker redaction to defeat patch-text injection. The worker is instructed to use the embedded diff and skip the isolator subagent entirely when it is present. Falls back to the old path on fetch failure or via the ``REVIEW_DISPATCHER_EMBED_DIFF=0`` env switch. # Cross-cutting bash rules The ``pr-review-worker``'s shell tool calls kept hitting ``permission denied`` because OpenCode's permission engine matches the *raw, unexpanded* command string against allow-globs. Chained commands (``&&``, ``||``, ``;``, ``|``), command substitution (``$(...)``), bare variable assignments, multi-line continuations (``\\\n``), heredocs, and inline ``python3 -c "..."`` strings all contain characters the permission glob cannot span, and were silently denied. Added ``.opencode/instructions/bash-commands.md`` (wired into ``opencode.json`` via the ``instructions`` array so it appends to EVERY agent's system prompt globally), with hard rules + recovery recipes (``printf > /tmp/file`` instead of heredocs; ``printf > /tmp/script.py`` + ``python3 /tmp/script.py`` instead of ``python3 -c``; ``curl -d @/tmp/body.json`` instead of multi-line ``-d '{...}'``). # Pre-commit polish (architect/dev/test review) Surfaced during a chief-architect / principal-developer / senior-test-engineer code review of the uncommitted change: - Schema DDL deduplication (described above under F2). - ``finish_cycle`` INSERT-fallback now preserves ``started_at`` / ``driver_name`` when caller provides them; otherwise stamps a ``synthetic_started_at: true`` flag in the raw blob so cycle-time analytics can exclude rows whose duration was synthesised. - ``bytes=`` → ``chars=`` in the embedded-diff header. The value is ``len(diff_text)`` after ``decode("utf-8")`` — a UTF-8 character count, not a byte count. Off-by-multibyte for non-ASCII patches. - ``scripts/opencode-builder.sh`` mode 644 → 755. - ``.gitignore`` entries for ``.parked-prs.json`` (runtime state for ``tools/park_other_prs.py --restore``) and ``.dispatcher-logs/`` (append-only local pipeline log directory). # Tests (381 passed, 1 skipped) - ``test_opencode_worker.py``: 3 new tests for ``on_poll`` cadence, error swallowing, and backwards-compatible default. - ``test_dispatch_runtime.py``: 7 new tests for ``begin_cycle`` / ``finish_cycle`` semantics, the v4→v5 migration with row preservation, the crash-safe try/finally path, the ``dispatch_one`` → ``run_session_blocking`` ``on_poll`` wiring, and the new ``started_at`` / ``driver_name`` plumbing through the INSERT-fallback branch. - ``test_telemetry_server.py``: 4 new tests for ``in_flight_cycle`` in ``/api/health``, the elapsed-seconds computation, and the ``running_long_worker`` flag. - ``test_telemetry_schema.py``: assertion bumped from v4 → v5 and a new test confirming the cycle tables now allow ``ended_at IS NULL``. - ``test_dispatch_review.py`` (new file): 14 tests for diff fetch (happy path, truncation, HTTP/URL errors, END_PR_DIFF redaction, Forgejo auth scheme), ``_build_diff_section`` (dry-run, env toggle, embedding, fallback), and end-to-end prompt embedding. Co-authored-by: Cursor <cursoragent@cursor.com>
353 lines
12 KiB
JSON
353 lines
12 KiB
JSON
{
|
|
"$schema": "https://opencode.ai/config.json",
|
|
"permission": "allow",
|
|
"instructions": [
|
|
".opencode/instructions/*.md"
|
|
],
|
|
"mcp": {
|
|
"context7": {
|
|
"type": "local",
|
|
"command": [
|
|
"npx",
|
|
"-y",
|
|
"@upstash/context7-mcp"
|
|
],
|
|
"enabled": true
|
|
},
|
|
"sequential-thinking": {
|
|
"type": "local",
|
|
"command": [
|
|
"npx",
|
|
"@modelcontextprotocol/server-sequential-thinking"
|
|
],
|
|
"enabled": false
|
|
}
|
|
},
|
|
"provider": {
|
|
"anthropic": {
|
|
"models": {
|
|
"anthropic/claude-opus-4-6": {
|
|
"name": "Claude OPUS 4.6",
|
|
"limit": {
|
|
"context": 200000,
|
|
"output": 32768
|
|
}
|
|
},
|
|
"anthropic/claude-haiku-4-5": {
|
|
"name": "Claude Haiku 4.5",
|
|
"limit": {
|
|
"context": 200000,
|
|
"output": 32768
|
|
}
|
|
},
|
|
"anthropic/claude-sonnet-4-6": {
|
|
"name": "Claude Sonnet 4.6",
|
|
"limit": {
|
|
"context": 200000,
|
|
"output": 32768
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"CleverThis": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://ke5ntcikhnj2clcp.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-6-35B-A3B-GGUF-BF16": {
|
|
"name": "Qwen 3.6 35b A3B GGUF BF16 (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-2": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://bqdgwv2uzjgianhe.us-east-2.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"MiniMax-M2-7-GGUF-BF16": {
|
|
"name": "MiniMax M2.7 GGUF BF16 (Thinking)",
|
|
"limit": {
|
|
"context": 196608,
|
|
"output": 65536
|
|
},
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-3": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://jve0bsgx6csdzln9.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Devstral-Small-2-24B-Instruct-GGUF": {
|
|
"name": "Devstral Small 2 24B GGUF (Instruct)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-4": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://ntgtpdbn2vuag4yb.us-east-2.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Kimi-K2-6-GGUF-Q2-K-XL": {
|
|
"name": "Kimi K2.6 GGUF Q2_K_XL (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-5": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://fhe4kwehnm1rb275.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-Coder-Next-GGUF-BF16": {
|
|
"name": "Qwen3 Coder Next GGUF BF16",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-6": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://f0oelboag4bzw1zp.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Nemotron-3-Nano-30B-GGUF-Q8-K-XL": {
|
|
"name": "Nemotron 3 Nano 30B GGUF Q8_K_XL (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-7": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://dfwwzwtekzhnh4fl.us-east-2.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"DavidAU/Qwen3.6-40B-Claude-4.6-Opus-Deckard-Heretic-Uncensored-Thinking": {
|
|
"name": "Qwen 3.6 40B Uncensored BF16 (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-8": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://ki19d58snp1d3qa4.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-Coder-Next-GGUF-Q4-0": {
|
|
"name": "Qwen3 Coder Next GGUF Q4_0",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-9": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://m56026p5kxvout0d.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-6-35B-A3B-GGUF-MXFP4-MOE": {
|
|
"name": "Qwen 3.6 35b A3B GGUF MXFP4_MOE (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-10": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://dirv1cnem5wqzoax.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-235B-A22B-INST-GGUF-Q8-0": {
|
|
"name": "Qwen3 235b A22B GGUF Q8_0 (Instruct)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-11": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://hueiyrdbly1ff4oo.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-235B-A22B-INST-GGUF-UD-Q2-K-XL": {
|
|
"name": "Qwen3 235b A22B GGUF UD-Q2_K_XL (Instruct)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-12": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://atocunu78hjdnu3f.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-30B-A3B-INST-GGUF-UD-Q8-K-XL": {
|
|
"name": "Qwen3 30b A3B GGUF UD-Q8_K_XL (Instruct)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-13": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://dpe3orf5en4581im.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-30B-A3B-INST-GGUF-UD-Q5-K-XL": {
|
|
"name": "Qwen3 30b A3B GGUF UD-Q5_K_XL (Instruct)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-14": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://jamyigykfzm39bcu.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"MiniMax-M2-7-GGUF-UD-Q4-K-XL": {
|
|
"name": "MiniMax M2.7 GGUF UD-Q4_K_XL (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-15": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://n4u4h8h0fgintms4.us-east-1.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-6-35B-A3B-GGUF-UD-Q3-K-XL": {
|
|
"name": "Qwen 3.6 35b A3B GGUF UD-Q3_K_XL (Thinking)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"CleverThis-16": {
|
|
"npm": "@ai-sdk/openai-compatible",
|
|
"options": {
|
|
"baseURL": "https://kcgeda25msp4dkwm.us-east-2.aws.endpoints.huggingface.cloud/v1",
|
|
"apiKey": "{env:HF_TOKEN}",
|
|
"headers": {
|
|
"X-HF-Bill-To": "CleverThis",
|
|
"Authorization": "Bearer {env:HF_TOKEN}"
|
|
}
|
|
},
|
|
"models": {
|
|
"Qwen3-code-480B-A35B-INST-GGUF-1M-UD-Q3-K-XL": {
|
|
"name": "Qwen3 Code 480b A35B GGUF UD-Q3_K_XL (Instruct, 1M Context)",
|
|
"tools": true
|
|
}
|
|
}
|
|
},
|
|
"openai": {
|
|
"models": {
|
|
"gpt-5-codex": {
|
|
"name": "GPT-5 Codex"
|
|
},
|
|
"gpt-5-mini": {
|
|
"name": "GPT-5 Mini"
|
|
},
|
|
"gpt-5-nano": {
|
|
"name": "GPT-5 Nano"
|
|
},
|
|
"o4-mini": {
|
|
"name": "o4-mini"
|
|
}
|
|
}
|
|
},
|
|
"google": {
|
|
"models": {
|
|
"gemini-2.5-pro": {
|
|
"name": "Gemini 2.5 Pro"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|