a21466add232d59cdec1604e09d58ca05659a623
7 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2cbe62a70c |
fix(auto-agents): F1+F2+F3 telemetry liveness, reviewer perf (A+C), bash rules
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> |
||
|
|
593d142f6f |
feat(auto-agents): Tier 2 deterministic review/implementer dispatchers
Replaces the long-running pr-review-supervisor / implementation-
supervisor LLM polling loops with host-level Python dispatchers that
own queueing, claim ownership, watchdogs, and SQLite telemetry. The
LLM workers retain sole responsibility for review judgment and code
generation; Python owns only orchestration. The hard merge invariant
is unaffected — these dispatchers do not touch master.
Driver surface
- _opencode_worker.run_session_blocking — outcome-agnostic OpenCode
session lifecycle (completed / timeout / transport-error). Used
directly by reviewer / implementer dispatchers whose workers do
not emit the conflict-driver JSON exit schema.
run_worker_blocking is now a thin wrapper that adds the
conflict-specific JSON-outcome classification.
- _dispatch_runtime — shared Python runtime (work-group polling,
claim helpers, dispatch loop, telemetry). Pre-checks issue labels
before claiming and refuses when any auto/claimed-* is already
present, distinguishing already-claimed vs labels-fetch-failed
vs claim-failed terminal states. Frozen DispatchConfig.
- dispatch_review.py / dispatch_implementer.py — per-pipeline work
groups, prompts, and CLIs. Each refuses startup when a competing
AUTO-REV-SUP / AUTO-IMP-SUP legacy supervisor is live on the
same OpenCode server (override:
{REVIEW,IMPLEMENTER}_DISPATCHER_ALLOW_SUPERVISOR_COEXIST=1).
- _loader.py — shared sibling-module loader; replaces the three
duplicated copies in the dispatcher entry points.
Operational hardening
- run_outer_loop tracks consecutive cycle exceptions against
cycle_failure_budget (default 5, env-tunable per driver) and
exits 2 on exhaustion for supervisor-driven restart.
- _sanitize_release_detail strips control bytes and neutralises
triple-backtick fences before quoting worker raw_response in
Forgejo claim-release comments.
- scripts/opencode-builder.sh: OPENCODE_BUILDER_SERVER_ONLY=1 keeps
only the OpenCode HTTP API up so the Python dispatchers own
queue orchestration without auto-agents running concurrently.
Telemetry
- _forgejo_cache.py schema v4: dispatch_review_cycles,
dispatch_implementer_cycles. One row per cycle with cycle_id,
driver, candidates_count, claims_acquired, swept_count,
processed_count, terminal_state, worker_outcome, session_id,
worker_wallclock_seconds, raw.
- .opencode/telemetry/server.py wires the new tables into
/api/cycles?driver=dispatch_review|dispatch_implementer and
surfaces a composite terminal_state/worker_outcome 24h breakdown
so dashboards can distinguish session-level vs work-level
outcomes.
Tests
- 31 new tests in tests/auto_agents/test_dispatch_runtime.py
covering: candidate priority/dedup, claim/release labels,
foreign-claim refusal, same-kind-claim refusal,
labels-fetch-failed terminal state, sanitization, supervisor
coexistence guard (pass/refuse/override/unreachable-server),
session timeout / transport-error propagation,
JSON-vs-no-JSON worker exits, cycle failure budget exit and
reset, heartbeat cadence, end-to-end --once --dry-run /
--status CLI smoke, and full prompt-snapshot tests for
_review_prompt and _implementation_prompt (PR-fix + issue-impl).
- test_telemetry_schema.py asserts schema v4 and the presence of
the two new dispatcher cycle tables.
338 auto_agents tests pass (was 322 before Tier 2). Conflict driver
regression suite unchanged. Dispatchers run cleanly under
--status / --once --dry-run with no Forgejo or OpenCode HTTP traffic.
Co-authored-by: Cursor <cursoragent@cursor.com>
|
||
|
|
97ea5a26d8 |
script: reinforced auto-agent scripts
CI / status-check (push) Blocked by required conditions
CI / benchmark-publish (push) Waiting to run
CI / push-validation (push) Successful in 40s
CI / helm (push) Successful in 53s
CI / build (push) Successful in 1m7s
CI / quality (push) Successful in 1m31s
CI / lint (push) Successful in 1m37s
CI / typecheck (push) Successful in 1m39s
CI / security (push) Successful in 1m46s
CI / integration_tests (push) Successful in 4m40s
CI / e2e_tests (push) Successful in 5m10s
CI / unit_tests (push) Successful in 7m10s
CI / docker (push) Waiting to run
CI / coverage (push) Has started running
|
||
|
|
514d61c63c |
build: fixed script to work with agent changes
CI / helm (push) Successful in 36s
CI / push-validation (push) Successful in 27s
CI / benchmark-publish (push) Failing after 59s
CI / lint (push) Successful in 57s
CI / build (push) Successful in 54s
CI / quality (push) Successful in 1m21s
CI / security (push) Successful in 1m37s
CI / typecheck (push) Successful in 1m37s
CI / integration_tests (push) Failing after 3m37s
CI / e2e_tests (push) Successful in 3m45s
CI / unit_tests (push) Failing after 4m48s
CI / docker (push) Has been skipped
CI / coverage (push) Successful in 11m2s
CI / status-check (push) Failing after 3s
CI / benchmark-publish (pull_request) Has been skipped
CI / push-validation (pull_request) Successful in 39s
CI / helm (pull_request) Successful in 42s
CI / build (pull_request) Successful in 1m1s
CI / lint (pull_request) Successful in 1m34s
CI / security (pull_request) Successful in 1m43s
CI / typecheck (pull_request) Successful in 1m46s
CI / quality (pull_request) Successful in 1m45s
CI / integration_tests (pull_request) Failing after 3m53s
CI / e2e_tests (pull_request) Successful in 4m46s
CI / unit_tests (pull_request) Failing after 4m58s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Successful in 12m32s
CI / status-check (pull_request) Failing after 3s
|
||
|
|
fe30961990 |
build: Updated script to match the new agent code
CI / docker (push) Blocked by required conditions
CI / helm (push) Waiting to run
CI / push-validation (push) Waiting to run
CI / status-check (push) Blocked by required conditions
CI / unit_tests (push) Has started running
CI / benchmark-publish (push) Failing after 39s
CI / lint (push) Successful in 1m6s
CI / typecheck (push) Successful in 1m17s
CI / quality (push) Successful in 1m45s
CI / security (push) Successful in 2m27s
CI / coverage (push) Waiting to run
CI / build (push) Successful in 36s
CI / integration_tests (push) Failing after 7m7s
CI / e2e_tests (push) Successful in 8m2s
|
||
|
|
f2fc8f771e |
feat: add port checking to opencode-builder script
CI / build (push) Successful in 18s
CI / push-validation (push) Successful in 19s
CI / lint (push) Failing after 24s
CI / helm (push) Successful in 23s
CI / quality (push) Successful in 32s
CI / security (push) Successful in 59s
CI / typecheck (push) Successful in 1m7s
CI / coverage (push) Has been skipped
CI / benchmark-regression (push) Has been skipped
CI / e2e_tests (push) Successful in 3m4s
CI / integration_tests (push) Failing after 4m4s
CI / unit_tests (push) Successful in 5m9s
CI / docker (push) Has been skipped
CI / status-check (push) Failing after 1s
CI / benchmark-publish (push) Has been cancelled
- Add check_opencode_running() function to detect existing OpenCode server - Skip server startup if OpenCode already running on target port - Display prominent warning when connecting to existing server - Only manage server lifecycle when script starts its own server - Add OWN_SERVER flag to track server ownership - Update cleanup logic to leave existing servers running This enables multiple developers and automation scripts to safely run in parallel without conflicting server management. |
||
|
|
73afe58cfe
|
build: runner for opencode project-builder
CI / benchmark-publish (push) Waiting to run
CI / lint (push) Successful in 34s
CI / quality (push) Successful in 41s
CI / typecheck (push) Successful in 48s
CI / security (push) Successful in 1m5s
CI / benchmark-regression (push) Waiting to run
CI / build (push) Successful in 24s
CI / helm (push) Successful in 22s
CI / unit_tests (push) Successful in 7m6s
CI / docker (push) Successful in 20s
CI / coverage (push) Successful in 10m0s
CI / e2e_tests (push) Successful in 16m13s
CI / integration_tests (push) Successful in 22m44s
CI / status-check (push) Successful in 1s
|