Files
cleveragents-core/tools/_status_comments.py
T
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

191 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}{fingerprint}{_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",
)