355af84fb1
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>
194 lines
7.6 KiB
Python
194 lines
7.6 KiB
Python
"""Shared per-fingerprint operator-status comment substrate.
|
|
|
|
Houses the dedup substrate every dispatcher uses to post operator-
|
|
visibility comments on PR / issue timelines without re-implementing
|
|
the fingerprinting logic in two places. The reviewer dispatcher
|
|
(:mod:`_review_post`) and the implementer dispatcher
|
|
(``tools/dispatch_implementer.py`` via :mod:`_review_post`) both
|
|
key off this module; their public posters (``post_operator_status_comment``,
|
|
``post_implementer_status_comment``) are thin per-pipeline wrappers
|
|
that supply the marker family + body template.
|
|
|
|
Public surface:
|
|
|
|
- ``_sanitize_status_detail`` — normalise + cap free-form reason
|
|
strings before they enter the fingerprint or the body.
|
|
- ``_compute_status_fingerprint`` — sha256-derived 12-hex-char
|
|
identifier for ``(action, sanitised(reason))``.
|
|
- ``_has_existing_operator_status`` — generic "marker prefix +
|
|
fingerprint already on this PR" predicate (parameterised on the
|
|
pipeline's marker prefix).
|
|
- ``OPERATOR_STATUS_MARKER_PREFIX`` /
|
|
``IMPLEMENTER_STATUS_MARKER_PREFIX`` (and their suffix
|
|
counterparts) — the on-wire marker namespaces. Two-key namespacing
|
|
protects each pipeline's idempotency from cross-talk if a future
|
|
audit enumerates "every status comment one dispatcher kind posted
|
|
on a given PR".
|
|
- ``operator_status_marker`` / ``implementer_status_marker`` —
|
|
public marker constructors so external callers don't have to
|
|
string-concat the prefix + fingerprint themselves.
|
|
|
|
The module is import-safe and self-contained; no cycle risk against
|
|
:mod:`_review_post` or any dispatcher driver. Extracting it here
|
|
prevents :mod:`_review_post` from drifting into a junk drawer of
|
|
"reviewer-specific posters AND a shared dedup substrate AND the
|
|
implementer's marker family" — the previous shape paid the cohesion
|
|
price for backwards compatibility.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import hashlib
|
|
import re
|
|
from typing import Any
|
|
|
|
# ─── Reviewer-side marker family ────────────────────────────────────────────
|
|
#
|
|
# Format: ``<!-- pr-review-dispatcher:operator-status:fp=ABCDEF123456 -->``
|
|
#
|
|
# Stable wire string: lands in PR comment bodies; mutating it would
|
|
# break fingerprint compatibility with previously-posted comments.
|
|
_OPERATOR_STATUS_MARKER_PREFIX = "<!-- pr-review-dispatcher:operator-status:fp="
|
|
_OPERATOR_STATUS_MARKER_SUFFIX = " -->"
|
|
OPERATOR_STATUS_MARKER_PREFIX = _OPERATOR_STATUS_MARKER_PREFIX
|
|
OPERATOR_STATUS_MARKER_SUFFIX = _OPERATOR_STATUS_MARKER_SUFFIX
|
|
|
|
|
|
def _operator_status_marker(fingerprint: str) -> str:
|
|
"""Build the full HTML-comment marker for ``fingerprint``."""
|
|
return (
|
|
f"{_OPERATOR_STATUS_MARKER_PREFIX}"
|
|
f"{fingerprint}"
|
|
f"{_OPERATOR_STATUS_MARKER_SUFFIX}"
|
|
)
|
|
|
|
|
|
def operator_status_marker(fingerprint: str) -> str:
|
|
"""Public form of :func:`_operator_status_marker`. Tests and any
|
|
external caller that needs to construct or recognise a marker
|
|
should import this rather than the underscore-prefixed helper.
|
|
"""
|
|
return _operator_status_marker(fingerprint)
|
|
|
|
|
|
# ─── Implementer-side marker family (Phase 5b) ──────────────────────────────
|
|
#
|
|
# Distinct prefix from the reviewer namespace so the dedup logic
|
|
# keyed off either prefix cannot mis-match a comment from the other
|
|
# pipeline.
|
|
_IMPLEMENTER_STATUS_MARKER_PREFIX = (
|
|
"<!-- pr-implementer-dispatcher:operator-status:fp="
|
|
)
|
|
_IMPLEMENTER_STATUS_MARKER_SUFFIX = " -->"
|
|
IMPLEMENTER_STATUS_MARKER_PREFIX = _IMPLEMENTER_STATUS_MARKER_PREFIX
|
|
IMPLEMENTER_STATUS_MARKER_SUFFIX = _IMPLEMENTER_STATUS_MARKER_SUFFIX
|
|
|
|
|
|
def implementer_status_marker(fingerprint: str) -> str:
|
|
"""Public form of the implementer-side fingerprint marker. Mirrors
|
|
:func:`operator_status_marker` on the reviewer side.
|
|
"""
|
|
return (
|
|
f"{_IMPLEMENTER_STATUS_MARKER_PREFIX}"
|
|
f"{fingerprint}"
|
|
f"{_IMPLEMENTER_STATUS_MARKER_SUFFIX}"
|
|
)
|
|
|
|
|
|
# ─── Detail sanitisation + fingerprinting ───────────────────────────────────
|
|
|
|
|
|
# Cap on the action_reason / detail string we surface in the
|
|
# operator-status body. Large reasons (a verbose Forgejo error blob,
|
|
# a 5 KB Python traceback excerpt) hurt readability and inflate the
|
|
# fingerprint surface area.
|
|
_STATUS_DETAIL_MAX_CHARS = 600
|
|
|
|
|
|
def _sanitize_status_detail(detail: str) -> str:
|
|
"""Normalise + cap the action_reason string for operator display.
|
|
|
|
- Collapse runs of whitespace (newlines, tabs) into single spaces
|
|
so a multi-line traceback doesn't span the operator comment.
|
|
- Strip control characters (other than tab/newline) for log
|
|
hygiene.
|
|
- Cap at :data:`_STATUS_DETAIL_MAX_CHARS` characters with an
|
|
ellipsis. Without the cap a runaway worker error message
|
|
could blow past the comment-body limit.
|
|
|
|
Used both for fingerprint computation (so stylistic whitespace
|
|
differences don't fork a new fingerprint) and for the rendered
|
|
body. Returns the empty string when ``detail`` is empty.
|
|
"""
|
|
if not detail:
|
|
return ""
|
|
cleaned = "".join(
|
|
ch
|
|
for ch in detail
|
|
if ch == "\n" or ch == "\t" or (0x20 <= ord(ch) < 0x7F) or ord(ch) >= 0xA0
|
|
)
|
|
cleaned = re.sub(r"\s+", " ", cleaned).strip()
|
|
if len(cleaned) > _STATUS_DETAIL_MAX_CHARS:
|
|
cleaned = cleaned[: _STATUS_DETAIL_MAX_CHARS - 1] + "\u2026"
|
|
return cleaned
|
|
|
|
|
|
def _compute_status_fingerprint(action: str, detail: str) -> str:
|
|
"""Compute the 12-hex-char idempotency fingerprint for a status
|
|
comment.
|
|
|
|
Built from ``sha256(f"{action}|{sanitised_detail}")[:12]`` so two
|
|
cycles producing the same outcome surface the same fingerprint
|
|
and skip the duplicate post; distinct outcomes get distinct
|
|
fingerprints and distinct posts. The ``action`` argument is the
|
|
pipeline-specific verb (``failed`` / ``stale`` / ``rebase-failed``
|
|
/ ``timeout`` / etc.); the ``detail`` argument is whatever
|
|
free-form reason string the dispatcher captured.
|
|
"""
|
|
canonical = f"{action}|{_sanitize_status_detail(detail)}"
|
|
return hashlib.sha256(canonical.encode("utf-8", errors="replace")).hexdigest()[:12]
|
|
|
|
|
|
def _has_existing_operator_status(
|
|
pr_comments: list[dict[str, Any]] | None,
|
|
fingerprint: str,
|
|
*,
|
|
marker_prefix: str,
|
|
) -> bool:
|
|
"""Return True iff any prefetched PR comment carries the marker
|
|
for ``fingerprint``. Used to short-circuit the operator-status
|
|
POST so identical-outcome cycles don't double-post.
|
|
|
|
``marker_prefix`` is required (no default) so that a third
|
|
pipeline cannot accidentally key against the reviewer's namespace.
|
|
Reviewer callers pass :data:`_OPERATOR_STATUS_MARKER_PREFIX`;
|
|
implementer callers pass :data:`_IMPLEMENTER_STATUS_MARKER_PREFIX`;
|
|
each pipeline's idempotency stays scoped to its own namespace.
|
|
|
|
Tolerates malformed entries: any comment whose ``body`` is not a
|
|
string is skipped silently.
|
|
"""
|
|
if not pr_comments:
|
|
return False
|
|
needle = f"{marker_prefix}{fingerprint}"
|
|
for comment in pr_comments:
|
|
if not isinstance(comment, dict):
|
|
continue
|
|
body = comment.get("body")
|
|
if isinstance(body, str) and needle in body:
|
|
return True
|
|
return False
|
|
|
|
|
|
__all__ = (
|
|
"IMPLEMENTER_STATUS_MARKER_PREFIX",
|
|
"IMPLEMENTER_STATUS_MARKER_SUFFIX",
|
|
"OPERATOR_STATUS_MARKER_PREFIX",
|
|
"OPERATOR_STATUS_MARKER_SUFFIX",
|
|
"_compute_status_fingerprint",
|
|
"_has_existing_operator_status",
|
|
"_operator_status_marker",
|
|
"_sanitize_status_detail",
|
|
"implementer_status_marker",
|
|
"operator_status_marker",
|
|
)
|