Commit Graph

10 Commits

Author SHA1 Message Date
drew 0bc734c020 style: ruff format the controller-state-machine branch (288 files)
Applies `ruff format` to the accumulated formatting debt on this branch.
Formatting-only — no behavioral changes. Required for CI/lint's format
gate (`nox -s format -- --check`), which the branch was failing on 288
tracked files that drifted from ruff's canonical style.

In-progress WIP files are intentionally excluded so this commit stays a
clean formatting-only diff.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 00:09:17 -04:00
drew 3f12e4140c fix(auto-agents): R3.4 — cycle-cap signature + implementer sees full reviewer record
Two related fixes surfaced by the 2026-05-17 run-5 live observation:

1. **Cycle-cap signature now includes total_reviews count** so the
   reviewer's COMMENT-only path actually moves the signature. Before:
   the cap signature was ``sha + (approvals + has_active_RC +
   has_unaddressed_RC)`` — all three boolean axes ignore COMMENT
   reviews entirely. After: ``+ total_reviews`` term bumps on
   EVERY review submission. The ``data_complete=False → COMMENT
   downgrade`` path the reviewer takes in low-context cycles now
   reflects in the signature, so the cap stops firing falsely.

   Without this, run-5 observed 4 fresh PRs hitting count=5 in
   ~10 minutes — the reviewer was successfully posting reviews
   every cycle but the cap saw "no change" because COMMENT-only
   reviews don't bump approvals_count, has_active_RC, or
   has_unaddressed_RC.

2. **Implementer now sees ALL reviewer feedback**, not just active
   REQUEST_CHANGES. Before: ``fetch_pr_fix_context`` passed
   ``include_active_reviews=False`` so failing-CI PRs reached the
   implementer with zero reviewer data. ``fetch_request_changes_pr_context``
   only included active RC reviews — COMMENT-only feedback was
   invisible in both code paths.

   After: ``fetch_pr_fix_context`` also includes reviews, AND the
   fetcher now partitions reviews into TWO buckets — the existing
   ``request_changes_reviews`` (active blocking RC, unchanged
   semantic) and a new ``comment_reviews`` field carrying every
   non-dismissed non-RC review (COMMENT / APPROVE). Both are
   persisted in the PR-context sentinel and exposed via
   ``implementer_pr_context.py``'s ``comment_reviews`` field.

   New prompt section ``## Pre-fetched reviewer comments and
   approvals`` renders the comment_reviews bucket with author /
   event / commit / body / inline comments + a postscript marking
   them as ADVISORY (not blocking, unlike the existing RC section).

   This closes the architectural gap where the reviewer and
   implementer pools could work in silos on the same PR — the
   reviewer's substantive prose feedback now reaches the
   implementer regardless of which work-group routed it.

Files touched:
- tools/_pr_classification_cache.py — total_reviews in classify_pr +
  reactivity composite; docstring updated.
- tools/_implementer_prefetch.py — new comment_reviews +
  comment_reviews_completed fields; fetcher partitions reviews
  once; fetch_pr_fix_context now includes reviews.
- tools/_implementer_prompt.py — _build_comment_reviews_section;
  wired into prompt assembly between RC and PR-comments sections.
- tools/_pr_context_sentinel.py — comment_reviews in _to_dict.
- tools/implementer_pr_context.py — comment_reviews accessor for
  the worker's handoff read path.
- tests/auto_agents/test_pr_context_sentinel.py — expected_value_keys
  + fixture + round-trip test updated.
- .opencode/agents/estimator-implementation.md — restored canonical
  section header levels (####) for downstream test compatibility
  after R3.1 rewrite.

Full auto_agents suite: 2303 passing (+12 from this and adjacent
work, none broken).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 17:00:02 -04:00
drew 2658deee94 feat(auto-agents): PR State Warmer substrate + supporting infra
Adds a long-lived sidecar (pr_state_warmer.py) that polls Forgejo's
/pulls endpoint every 30s and writes the full PR snapshot to a
shared SQLite store, eliminating the dispatcher's per-cycle
cold-cache stalls (24-30s rebuilds on flaky cycles) and the silent
50-PR pagination cap on the legacy single-page fetch.

Substrate
- tools/_pr_state_cache.py  — SQLite store with (owner, repo) PK,
  WAL mode, additive v2→v3 migration (comments_refreshed_updated_at),
  bounded fcntl.flock migration lock, threading.Lock for per-process
  init, @_with_reheal decorator (catches OperationalError no-such-
  table + DatabaseError corruption with file quarantine), atomic
  TEMP-table chunking for >32k seen-set, _normalize_updated_at to
  canonicalize Forgejo tz-marker drift
- tools/pr_state_warmer.py — poll/upsert/vanish/comments-refresh
  loop with fcntl.flock singleton (rejects second warmer), bounded
  comments-refresh cap, persistent deferral via SQL pending query,
  PermissionError-tolerant lock setup, cold-start log suppression
- tools/_pr_classification_cache.py — three-layer fall-through
  (warmer cache → list cache → live fetch) with staleness gate
  (PR_STATE_WARMER_STALE_AFTER_S floored at 30s in prod)

Comments cache hardening
- Bot-filter at write time drops bot status/claim/release/sentinel
  while preserving **Implementation Attempt** markers (94.6%
  reduction on bot-heavy PRs like #30's 19k-comment thread)
- _normalize_since_cursor strips microsecond precision before
  building ?since= query (fixes the live-observed Forgejo HTTP 422
  bug on PRs #25 + #28); handles uppercase Z, lowercase z, ±HH:MM
  offsets (including non-zero like +05:30), naive ISO
- Lazy migration of legacy null-key by_author entries on _read_cache
- _newest_cursor walks tail-back skipping malformed entries

Supporting infrastructure (cumulative dmpipeline-v2 work)
- Telemetry server: SSE live tail, run-sessions enumeration,
  cost/token tracking, app.js UI rewrite with collapsible sections
- MCP servers (mcp_ci_server, mcp_forgejo_server, mcp_git_server,
  mcp_handoff_server, mcp_graphify_server) for opencode worker
  context access
- Live log writer (tools/live_log_writer.py) — SSE-streaming
  dispatcher event log
- Tier-dispatcher escalation flow with prompts trimmed for budget
- Shared bot-logins resolver (tools/_bot_logins.py) replacing two
  drift-prone copies
- token_usage_audit.py for opencode cost analysis

Tests
- 2259 passing across 65 changed/new files
- New suites: test_pr_state_cache, test_pr_state_warmer,
  test_pr_state_warmer_integration, test_pr_classification_cache,
  test_pr_list_cache_backoff, test_mcp_* (5 servers),
  test_live_log_writer_sse, test_telemetry_run_sessions,
  test_review_post_ready_label
- Test_pr_comments_cache expanded with bot-filter coverage,
  cursor-normalization regression pins, format-drift, atomicity,
  failed-comments-not-stamped (silent-data-loss class)
- Parametrized @_with_reheal coverage across 7 wrapped APIs
- Real fault-inject atomicity test for chunked mark_vanished path
  via Connection wrapper class
- Subprocess-based singleton flock test (cross-process contract)
- Event-driven SIGTERM-mid-poll test (no fixed-sleep flake)

Architecture notes
- Schema v3 migration is additive (ALTER ADD COLUMN); v0/v1 still
  need destructive rebuild because pre-v2 column shape lacks
  owner/repo. Cross-process drop-table-ping-pong prevented by the
  fcntl migration lock + per-process _initialized flag.
- Comments-refresh deferral is persistent via
  comments_refreshed_updated_at column — survives warmer restart,
  picks up next cycle even if PR didn't change again. Replaces
  in-memory changed_numbers list.
- Rollback path: PR_STATE_WARMER_PREFER=0 bypasses the warmer
  cache and reverts to live-fetch behavior. PR_STATE_CACHE_DISABLE=1
  short-circuits the warmer process at startup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-17 10:02:53 -04:00
drew dc4e9368f2 feat(auto-agents): block-store substrate + DRY refactors
Externalise large prompt sections (PR diff, comments, CI failure
logs, reviews, linked issues) into a cross-process SQLite-backed
block store so the worker can recover original content when an
intermediate tier-* agent's summarisation strips inline sections.

New substrate
-------------

- ``tools/_block_store.py`` — SQLite WAL, per-row 1MB cap, 1h TTL,
  janitor (startup + opportunistic per-hour in register()),
  threading.Lock around the periodic-sweep gate.
- ``tools/_block_prompt.py`` — registration glue + ``## Available
  blocks`` Markdown table renderer.
- ``tools/_prefetch_section.py`` — single-source-of-truth section
  registry; collapses the duplicate ``_register_*_blocks`` helpers
  the reviewer and implementer previously kept in lockstep.
- ``tools/mcp_block_store_server.py`` — FastMCP wrapper exposing
  ``block_fetch`` / ``block_list`` / ``block_register`` /
  ``block_invalidate`` to agents. Uses the expanded ``_mcp_common``
  helpers.
- ``tools/_implementer_escalation_helpers.py`` — pure helpers
  extracted from ``dispatch_implementer.py`` (~180 lines off the
  3284-line file); takes ``claim_runtime`` as a kwarg for clean DI.

DRY refactors
-------------

- ``tools/_backoff.py`` — shared ``Backoff`` dataclass collapses the
  three near-identical exponential-backoff state machines in
  ``_pr_comments_cache``, ``_ci_logs``, ``_pr_classification_cache``.
- ``tools/_mcp_common.py`` — expanded with ``bootstrap_loader``,
  ``error_envelope``, ``make_main`` so each MCP server's prelude is
  three lines.
- ``tools/_pr_diff.build_diff_section_full`` — returns a 4-tuple
  including the raw diff body so the reviewer's block-store
  registration reuses the bytes instead of doing a second HTTP fetch.

Wiring
------

- ``_review_prompt.build_review_prompt`` builds a ``PrefetchSection``
  registry via ``_review_sections``, registers them, and renders the
  ``## Available blocks`` table at the end of the prompt.
- ``_implementer_prefetch._fetch_pr_context`` /
  ``fetch_new_issue_context`` build the equivalent registry via
  ``_implementer_sections`` and stamp ``result.block_refs`` for the
  prompt builder to read.
- ``_implementer_prompt`` builders include
  ``_build_available_blocks_section(result)`` in all three flows.
- ``dispatch_review.main`` + ``dispatch_implementer.main`` call
  ``_block_store.janitor()`` at startup; the per-call opportunistic
  janitor in ``register()`` keeps the file bounded between restarts.

Agent contract updates
----------------------

- ``.opencode/agents/task-implementor.md`` +
  ``.opencode/agents/pr-review-worker.md``:
  - ``block_store*`` permission
  - new "Block-store substrate" paragraph explaining
    ``block_fetch`` / ``block_list`` as the summarisation recovery
    path.

Tests
-----

- ``test_block_store.py`` — 48 tests pinning every public contract
  (register/fetch/list/invalidate/janitor, key whitelist, TTL,
  size cap, WAL durability).
- ``test_block_prompt`` — covered transitively via the e2e test.
- ``test_block_store_recovery_e2e.py`` — builds a real prompt via
  ``build_pr_fix_prompt``, applies a heading-bounded summariser stub
  (``_summarise_inline_sections``), asserts inline content is stripped
  yet block keys survive and ``block_fetch`` recovers original content.
  Plus a ``block_list`` fallback test for the worst case where the
  table itself was summarised away.
- ``test_mcp_block_store_server.py`` — 27 wrapper-contract tests.
- ``test_mcp_block_store_transport.py`` — spawns the actual server
  subprocess via ``mcp.client.stdio`` and exercises the JSON-RPC
  transport round-trip in ~1.5s. Catches FastMCP schema /
  serialisation bugs the in-process tests miss.
- ``test_backoff.py`` — 14 behaviour-focused tests of the shared
  ``Backoff`` curve.
- ``test_pr_comments_cache.py`` + ``test_ci_logs.py`` — deleted the
  now-redundant ``TestComputeNextAttemptAfter`` / ``TestBackoffActive``
  / ``TestBackoffHelpers`` classes; ``test_backoff`` covers them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 22:55:52 -04:00
drew 3d30358333 feat(auto-agents): pre-fetch failing-CI job log tails (information-starvation fix)
Workers (reviewer + implementer) historically saw `CI / lint: failure`
in the prompt but had no idea WHY. Three implementer cycles on PR #28
(runs 18-20, 2026-05-16) burned their full 30-min worker budget
largely on:

- ~14 file reads + ~14 bash calls inferring the failure from the diff
- `bash curl … /actions/runs/N/jobs` attempts blocked by the bash allowlist
- `webfetch` against the same Actions URL — also errored
- one ~10-min `ci_run_local_gate` run (a full `nox -s coverage_report`)

This adds a single source-of-truth cache that pre-fetches the LAST N
chars of every failing job's raw CI log:

- **`tools/_ci_logs.py`** — per-(head_sha) on-disk cache; parses Forgejo
  Actions `target_url` (both `/runs/N/jobs/M` and `/runs/N`-only shapes)
  to resolve job_ids; fetches `/repos/{owner}/{repo}/actions/jobs/{id}/logs`
  and keeps the tail. SHAs are immutable so `completed=True` cache
  entries are good forever; partial fetches stamp exponential backoff
  (same shape as `_pr_comments_cache`).
- Dispatcher pre-fetch (reviewer + implementer): each dispatcher's
  prefetch coordinator (`_review_prompt.fetch_review_context` +
  `_implementer_prefetch.prefetch_for_pr_fix`) now calls
  `_ci_logs.fetch_pr_failure_logs` when CI overall != success. Cache
  exceptions WARN and serve None (prompt builds with empty section;
  worker still has `target_url` to follow).
- Prompt sections: new `## Pre-fetched CI failure logs` section in
  both reviewer (`_review_views.build_ci_failure_logs_section` — JSON
  payload) and implementer (`_implementer_prompt._build_ci_failure_logs_section`
  — text blocks). The reviewer's `data_complete` aggregate gates on it.
- Agent prompts updated: `pr-review-worker.md` line 414 (which already
  predicted this feature in prose) and `task-implementor.md` step 1
  ("Read the CI failure picture FIRST") both now point at the new
  section and explicitly tell the worker NOT to use `curl` / `webfetch`
  / `ci_run_local_gate` for log content.

Coverage: 26 new tests in `test_ci_logs.py` (parse_run_job_ids, the
two URL shapes, cache miss/hit/backoff, log truncation, run-only URL
job-id resolution, disabled-mode bypass) + 1 test update in
`test_pr_context_sentinel.py` to include the new completion flag.
Full auto_agents suite: 2046 passing (+36 vs prior commit, all green).

NOTE: the matching `ci_fetch_pr_failure_logs(pr)` MCP wrapper for
agents to call ad-hoc is implemented in `tools/mcp_ci_server.py` +
covered by `tests/auto_agents/test_mcp_ci_fetch_pr_failure_logs.py`,
but both files are currently untracked (Phase 1 work). They'll ride
with Phase 1's commit. The dispatcher pre-fetch path here is
self-contained and doesn't depend on the MCP wrapper.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 15:36:30 -04:00
drew 23ae44e432 feat(auto-agents): loud signal on degraded prompt assembly (W8, default-OFF)
Adds assess_prompt_completeness() to _implementer_prefetch.py — a
pure helper that translates the carrier's per-section completion
flags into a single {degraded, missing_sections, error_kinds} dict.
The aggregate data_complete flag already encoded the AND, but as a
single bool it lost the *which sections* signal an operator needs
to triage a degraded cycle. This helper keeps both together.

dispatch_implementer._prefetch_prompt now calls the helper after
prefetch and stashes the result on the per-item context as
prompt_completeness. When IMPLEMENTER_DEGRADED_PROMPT_LOG_ENABLED=1
(default OFF, per the dmpipeline safety contract) AND the cycle is
degraded, also emits a WARN log line naming the specific missing
sections so an operator tailing the dispatcher log sees the
silent-degradation signal without parsing telemetry JSONL.

The completeness signal is always stashed on the context regardless
of the flag, so future telemetry / status-comment paths can pick it
up without operators flipping any switch. The flag specifically
gates the LOG emission — the harvest plan's primary value
("converts a silent correctness risk into an observable one").

5 unit tests for the helper covering happy / single-fail / multi-
fail / diff-truncation / direct-data_complete-flip paths.

Refs: docs/development/final-working-harvest-plan.md (W8).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 20:27:19 -04:00
drew 19dad571bd feat(auto-agents): bounded comment view + digest, persistent cache, claim-sweep through cache
Three coordinated changes addressing run-8 finding R8-3 ("PR #30's
1440 comments poison the pipeline via prompt size × chain depth"):

Spec #5 — bounded view + deterministic digest. The prompt and
sentinel now embed at most DEFAULT_MAX_PROMPT_COMMENTS=50 verbatim
comments plus a one-paragraph rollup of the older bot attempt
comments (counts by tier / outcome / failing gates / last success).
_build_comments_section takes the most-recent N (comments[-N:]) not
the oldest — a long-standing bug where the worker on heavy PRs saw
ancient history and missed every recent attempt. Bot status / claim
/ sentinel comments are dropped from the view via author-based
classification (HAL9000 / HAL9001 defaults, FORGEJO_USERNAME /
FORGEJO_REVIEWER_USERNAME env overrides) — a content-only
classifier mis-counted them as "humans" and ballooned the view to
1252 items on the real test case (run-10 inspection).

Spec #5 (R8-5) — persistent comment cache fixes. Seed-on-truncation:
a page-cap-truncated fetch now seeds the cache (clipped but valid)
flagged any_partial_fetch=True. since_cursor replaces wall-clock
fetched_at as the ?since= delta cursor so backfill walks forward
from the newest cached comment instead of skipping the un-fetched
middle. _api_get_paginated gains an opt-in return_truncation=True
shape so the cache can distinguish "transient failure" (don't seed)
from "page cap hit" (seed and backfill next cycle).

Spec #6 — claim-sweep routes through the comment cache.
_claim_runtime._find_newest_claim_at used to paginate every page of
issue comments on every cycle (29 sequential round-trips for #30,
~10+ minutes when Forgejo was slow — see run-9 hang diagnosis). It
now reads from _pr_comments_cache.get_pr_comments and reverse-scans
for the marker with early-exit. get_pr_comments grew optional
owner/repo overrides so callers with a narrower RuntimeContext cfg
(no owner/repo attrs) can share the cache. Fail-safe on
completed=False: when the timeline is incomplete and no marker was
found, return datetime.now() so the sweep keeps the claim this
cycle rather than releasing on partial data.

Run-11 verification (PR #30 end-to-end):
- Dispatcher startup -> first cycle log: 12+ min hang -> 9 s
- pr_comments view len in sentinel: 1252 (run-10) -> 50 (run-11)
- pr_comments_digest populated with full tier/outcome/gates rollup
- data_complete=True; 4 implementer sessions ran cleanly

Tests green: 1603 passed / 3 skipped. New test files:
test_attempt_history.py, test_implementer_prefetch.py. New tests
added in test_pr_comments_cache.py, test_claim_runtime.py,
test_implementer_pr_context_cli.py, test_implementer_prompt_snapshot.py,
test_pr_context_sentinel.py.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 01:04:16 -04:00
drew 8ed4b96b1a feat(auto-agents): perf + observability + persistent comment cache
Folds B1-B4 + C2-C5 from the post-live-test plan into one commit:

B1 — npx tsx pre-warm in dispatchers-launcher.sh closes the cold-cache
30s AbortSignal timeout that killed both dispatchers' first cycle.

B2 — per-tier worker timeout
(IMPLEMENTER_DISPATCHER_WORKER_TIMEOUT_TIER_{N}_SECONDS) lets Tier 1
(qwen-large) and Tier 2 (kimi) get more wallclock than gpt-5-mini;
floor 60s.

B3 — _rebuild_prompt_from_cached_result skips the full prefetch on
tier transitions (worktree-reset puts everything back at the
prefetched head_sha, so the prefetch result + det_sections don't
change). Saves ~7 min per tier transition on comment-heavy PRs.

B4 — git-commit-util.md documents the FORBIDDEN naive recovery
pattern (git fetch && git reset --hard) that lost PR #30 attempt
3's real fix in the live test. Two correct paths now spelled out:
--force-with-lease=<branch>:<old-remote-sha> or stash+rebase+pop.

C2 — _pr_clone._refresh_mirror_with_retry adds one retry on git
fetch failure and force-reclones the bare mirror if both attempts
fail. Previously a single exit 128 logged WARN and continued with
stale data forever.

C3 — in-flight turn markers (asterisk suffix on input/output token
counts) in the per-turn log when completed=False. The archived
turn dict's completed field was already there; the log now surfaces
it. Sub-agent timeout archiving was already correct via
_archive_subagent_tree.

C4 — new module _recent_push_cache.py records per-PR push events
(head_sha + timestamp + cycle metadata). Prefetch surfaces in the
sentinel under recent_implementer_push (with --field accessor)
when the cached push matches the PR's current head_sha within
1h. Prevents the "dispatcher re-cycles right after pushing,
worker re-does the same compliance work" failure mode from
PR #28 cycle 2 in the live test.

C5 (replaces C1) — new module _pr_comments_cache.py wraps
_review_fetch.fetch_pr_comments with disk-backed delta-fetch
semantics. PR #30's 1340+ comment fetch (which previously took
~30s and hit the 20-page pagination cap) now becomes a 5-10 item
delta. Cache is per-PR, shared between reviewer + implementer
dispatchers, has 24h staleness bound, kill-switch via
IMPLEMENTER_DISPATCHER_COMMENT_CACHE_DISABLE=1.

Tests: 1484 passed, 3 skipped (+20 from 49a28b5c). New test files
test_pr_comments_cache.py (16 tests) and test_recent_push_cache.py
(7 tests); new TestPerTierWorkerTimeout class (4 tests).

ISSUES CLOSED: #30 #28

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 10:25:10 -04:00
drew ba52135a66 fix(auto-agents): quality-gate env bootstrap + filesystem-handoff hardening rounds 6-7
Two workstreams in one commit:

1. Quality-gate environment bootstrap + new quality-gates skill (round 8).
   The Tier 0 task-implementor was reporting "Failed — nox not available"
   on every PR because the worker's /tmp throwaway clone had no Python
   tooling. tools/local_ci_gate.sh now self-bootstraps nox via a three-
   step resolution chain (system PATH → project venv → uvx fallback)
   and exits 2 with an actionable diagnostic if none resolves. New
   .opencode/skills/quality-gates/SKILL.md gives the implementer worker
   a deterministic recipe + troubleshooting appendix. task-implementor.md
   adds the matching bash allow-rules (bash tools/local_ci_gate.sh *,
   uvx --quiet nox *) plus the skill on its allowlist; step 5 of the
   procedure now references the skill. 9 new unit tests in
   test_local_ci_gate.py pin the three-step resolution chain, the
   bad-shape error paths, and the "system-nox failure does not fall
   through to uvx" invariant. Verified end-to-end against a fresh /tmp
   clone with PATH restricted to system + uvx: both lint and typecheck
   gates ran cleanly via uvx fallback.

2. Filesystem-handoff hardening rounds 6 and 7 (P1/P2 follow-ups from
   the iterative critique loop). Round 6 propagated the round-5
   single-source-of-truth pattern to the sentinel writer
   (_to_dict overlays COMPLETION_FLAG_NAMES) and the worker reader
   (--field metadata introspects payload keys), added the bidirectional
   drift guard test (tuple ↔ dataclass set-equality), and corrected
   round-5 CHANGELOG wording. Round 7 added defence-in-depth: an
   assert-based schema-base collision guard in _to_dict, schema-lock
   tests pinning the writer's full output key set, writer-side typo
   guard, and a reader self-adapts test for unknown future flags.

Test results: 1,170 passing / 3 skipped (+10 vs the round-5 baseline,
+9 in this round). Lint: 11 pre-existing errors across the changed
files, unchanged baseline (corrects the round-6/7 entries' aspirational
"7 pre-existing" claim — empirically the _to_dict refactor cleaned up
zero lints).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 12:29:53 -04:00
drew 355af84fb1 refactor(auto-agents): hard-switch supervisor decommission + implementer parity
Combines the 2026-05-09 hard-switch decommissioning of the LLM
implementation/pr-review supervisors with the Phase 2/3/4/5b
implementer parity work (prefetch + preclone + telemetry + operator-
status comments) and the third/fourth-round critique cleanup.

Removed
- .opencode/agents/implementation-supervisor.md (340 LoC)
- .opencode/agents/pr-review-supervisor.md (348 LoC)
- _dispatch_runtime.assert_no_legacy_supervisor +
  detect_legacy_supervisor_sessions and the SUPERVISOR_TAGS /
  SUPERVISOR_OVERRIDE_ENV plumbing in both dispatchers, along with
  the five supervisor-coexistence tests in test_dispatch_runtime.py
- _watchdog_helpers.parse_truthy_env + watchdog_check.py
  --check-env mode + their dedicated unit tests (the legacy
  DISPATCHERS_RUNNING gate had no callers after the watchdog
  rewrite became unconditional)

Added
- tools/_implementer_prefetch.py — pre-dispatch Forgejo fetches
  (PR/issue body, diff, CI status, comments, reviews, linked
  issues, Epic) per work group
- tools/_implementer_prompt.py — pure-function prompt assembly
  with UNTRUSTED CONTENT fences and shared
  PR_COMPLIANCE_CHECKLIST / OUTPUT_CONTRACT
- tools/_phase4_telemetry.py — extractor + JSONL sink for the
  Phase 4 plan metrics
- tools/_status_comments.py — per-fingerprint operator-status
  comment substrate, namespaced for reviewer + implementer
- _dispatch_runtime.SessionContext dataclass + SIGTERM/SIGINT
  cooperative claim release with synchronous handler
- TestSupervisorAgentsDecommissioned and
  TestAutoAgentsMdIsWatchdogOnly anti-regression lints (glob over
  *supervisor*.md in .opencode/agents/, plus body keyword bans
  and bash allow-list lint)
- pyproject.toml `slow` marker registration for the subprocess
  SIGTERM smoke test
- tests/auto_agents/fixtures/{phase4-acceptance.yaml,
  phase4-session-output-sample.txt}

Rewritten
- .opencode/agents/auto-agents.md from supervisor-fleet manager
  (~545 LoC) to dispatcher heartbeat watchdog (~184 LoC); host
  init system / process manager (systemd / runit / docker) is now
  the explicit restart authority instead of "host-level process
  supervisor"
- AGENTS.md production-launch story (Shells A-D) reflects the
  deterministic-Python orchestration boundary; the bot-identity
  fork-mode paragraph reads from FORGEJO_OWNER / FORGEJO_REPO
  env vars instead of the deleted hard-coded supervisor flags
- tools/launch_fork.sh header documents three host-level entry
  points (dispatchers-launcher.sh, opencode-builder.sh,
  merge_drive.py)
- worker self-descriptions (implementation-worker.md,
  pr-review-worker.md) refer to the dispatcher / merge driver
  instead of the deleted supervisors; session-health-quick-util.md
  and async-agent-util.md treat -SUP-suffixed sessions as
  flag-and-escalate signals

Tests: 1006 passed, 3 skipped, 0 failed under tests/auto_agents/.
Lint: zero new ruff errors on touched files; three pre-existing
errors in tools/_pr_diff.py at lines blamed to 2026-05-07.

Operator note: the only in-process rollback knob for prefetch
issues is IMPLEMENTER_DISPATCHER_PREFETCH=0 (and the matching
IMPLEMENTER_DISPATCHER_PRECLONE=0). Anything beyond that is git
revert of this commit. Residual doc surface in the
auto-agents-system and supervised-workers skill READMEs is
documentation-only; the agent files those READMEs reference no
longer exist.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-10 16:03:43 -04:00