Skip to content

fix(reviewer): apply a docs-only rubric so doc PRs aren't over-flagged - #337

Merged
ProtocolWarden merged 2 commits into
mainfrom
fix/reviewer-doc-only-rubric
Jun 19, 2026
Merged

fix(reviewer): apply a docs-only rubric so doc PRs aren't over-flagged#337
ProtocolWarden merged 2 commits into
mainfrom
fix/reviewer-doc-only-rubric

Conversation

@ProtocolWarden

Copy link
Copy Markdown
Owner

The root fix for the over-flagging that looped #334 — the escalation-budget
fix (#335) only bounded the symptom; this stops the reviewer raising the
false-positive in the first place.

When a PR's diff is documentation-only (every changed file is
.md/.markdown/.rst/.txt or under docs/), the self-review prompt now carries a
docs rubric: review for internal consistency, accuracy against the repo, broken
references, and clarity — and do NOT raise concerns that a statement is
"unverifiable in the diff", "lacks CI/test evidence", or "references work outside
this diff". A document legitimately points to work it can't contain (CI runs,
secrets, sibling/other-repo PRs); demanding in-diff proof of those isn't a valid
concern for a docs PR.

Scoped tightly: mixed (doc+code) and config-only diffs (e.g.
.console/reconcile.yaml) still get the full review rubric — only all-docs diffs
are right-sized.

Helpers _is_doc_path / _files_from_diff / _diff_is_docs_only; rubric
_DOC_ONLY_REVIEW_RUBRIC. Tests cover classification, detection, and that
_phase1 injects the rubric for docs-only and omits it for code. Reviewer suite
123 pass; audit clean.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

🤖 Generated with Claude Code

Root fix for the over-flagging that looped #334 (the escalation-budget fix #335
only bounded the symptom). When a PR's diff is documentation-only (every changed
file is .md/.markdown/.rst/.txt or under docs/), the self-review prompt now
carries a docs rubric: review for internal consistency, accuracy against the
repo, broken references, and clarity — and explicitly DO NOT raise concerns that
a statement is "unverifiable in the diff", "lacks CI/test evidence", or
"references work outside this diff". A document legitimately summarizes and points
to work it cannot contain (CI runs, secrets, sibling/other-repo PRs); demanding
in-diff proof of those is not a valid concern for a docs PR.

Mixed (doc+code) and config-only diffs (e.g. .console/reconcile.yaml) still get
the full review rubric — only all-documentation diffs are right-sized.

Helpers: _is_doc_path / _files_from_diff / _diff_is_docs_only; rubric
_DOC_ONLY_REVIEW_RUBRIC. Tests: path classification, docs-only detection, and
that _phase1 injects the rubric for docs-only diffs and omits it for code.
Reviewer suite 123 pass; audit clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@ProtocolWarden

ProtocolWarden commented Jun 19, 2026

Copy link
Copy Markdown
Owner Author

Resolved: superseded by new push — re-review resumed

Self-review concerns — auto-fixing (up to 6 attempts; re-queued if still unresolved):

Critical bug in _phase1(): _pr_files is assigned only in the else branch (line with _files_from_diff) but used unconditionally after the if/else block at docs_only = _diff_is_docs_only(_pr_files). Large diffs will trigger the if branch and cause NameError when _pr_files is undefined. Fix: move _pr_files = _files_from_diff(diff) before the if/else block so it's defined for all code paths.

…paths

The _pr_files variable is used unconditionally at line 2112 to determine if
a diff is documentation-only. Previously it was assigned conditionally:
- In the if branch (large diffs): via gh_client.list_pr_files()
- In the else branch (small diffs): via _files_from_diff()

This fragile approach could break if there was any code path where _pr_files
wasn't assigned. Move the baseline assignment (_files_from_diff) before the
if/else block so _pr_files is always defined, then optionally override it
with the GitHub API call for large diffs.

Resolves self-review concern: ensures _pr_files is defined for all code paths.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@ProtocolWarden
ProtocolWarden merged commit 7578326 into main Jun 19, 2026
21 checks passed
@ProtocolWarden
ProtocolWarden deleted the fix/reviewer-doc-only-rubric branch June 19, 2026 00:23
ProtocolWarden added a commit that referenced this pull request Jun 19, 2026
#335 stopped the fleet's own fix-push from resetting the fix_attempts budget by
matching the recorded last_fix_push_sha. But that SHA is recorded only AFTER the
fix pass returns — so if the watcher is interrupted between the push and the
record (a long fix pass killing the process, as seen on #337), the SHA is lost
and the next poll mistakes our own push for an external one, resetting the budget
and re-opening the #334 infinite-loop risk.

Add a restart-safe fallback using state that survives the pre-fix save: when
there is an active fix cycle (fix_attempts > 0) but the pass outcome was never
recorded (last_fix_pass_pushed absent — popped at dispatch start, re-set only on
completion), a head move is our interrupted fix's push, not an external one — so
do not reset. A poll never observes this mid-dispatch (the dispatch is
synchronous within one poll), so the fallback only triggers after a restart.
External pushes after a COMPLETED pass still reset for a fresh review.

Tests: restart-mid-fix preserves the budget (→2); the self/external-push tests now
set last_fix_pass_pushed=True to model a completed prior pass. Reviewer suite 124
pass; audit clean.

Co-authored-by: ProtocolWarden <ProtocolWarden@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
ProtocolWarden added a commit that referenced this pull request Jun 21, 2026
)

* feat(eval): Phase 4 scaffolding — self-healing agent-quality guard

Stands up the EVAL machinery (HARNESS_TRUST_HARDENING §4) — everything buildable
ahead of the one irreducibly-human step (the operator answer-key signature):

- corpus.py: append-only, hash-chained case ledger. Editing/deleting any past
  entry breaks the chain → tamper-evident (verified by a required CI check).
- signing.py: Ed25519 operator signatures. A case is *graded* only when its
  signature verifies against the constitution public key; asymmetric so no
  compute inside the trust boundary can forge a label. Private key stays offline.
- replay.py: deterministic BLOCKING gate — replays input.checks through the pure
  code-computed verdict (pr_review_watcher.verdict.compute_verdict) and exact-
  matches the committed answer. No model → zero flakiness; catches a #313-style
  verdict-bypass regression. Only operator-signed cases count toward the gate.
- critic.py: non-blocking, different-model-family N-of-M drift monitor (the model
  extractor is an injected seam; production wires a different-family adapter).
- constitution.py: monotonic baseline floor + report-only→blocking graduation
  (D-EVAL-3). The gate can never block before the answer key is seeded (§0.1).
- verify.py + eval-corpus-integrity.yml: the required check tying chain +
  signatures + floor together. CODEOWNERS pins corpus + constitution + workflow
  to the operator (D-EVAL-2).

Seeds 7 unsigned candidate cases (#313/#337 classes); all pass replay and the
gate is correctly report-only (0/15 signed). The exam/answer-key split: the fleet
may append candidate cases, but only an offline operator signature makes one
count toward the gate.

33 unit tests (hash-chain tamper-evidence, signature non-transfer, replay gating,
graduation, drift majority-vote, end-to-end verify). ruff/ty clean.

Deferred (irreducibly human): operator generates the Ed25519 key offline, commits
the pubkey, signs >=15 seed cases → gate graduates to blocking.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(eval): declare cryptography dep; wire monotonic floor into verify.py

CI surfaced two gaps in the Phase 4 scaffolding:
- cryptography was used by eval.signing but not declared, so the test/ty jobs
  failed to import it (it was only transitively present locally). Added it to
  [project] dependencies.
- the monotonic baseline-floor check lived only in the workflow's inline Python
  heredoc, so constitution.is_monotonic_successor_of was "tested but never called
  in production" (D12). Moved the enforcement into verify.py (--base-floor) and
  simplified the workflow to call it — de-stringifies the check and clears D12.

35 unit tests (added monotonic raise/lower cases); ruff/ty/D12 clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: ProtocolWarden <ProtocolWarden@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant