Close three docs-review pipeline gaps: framing-drift verdict, turn-cap escalation, SHA-based review freshness - #20577
Merged
Conversation
Collaborator
|
Your site preview for commit f0480b5 is ready! 🎉 http://www-testing-pulumi-docs-origin-pr-20577-f0480b57.s3-website.us-west-2.amazonaws.com |
This was referenced Jul 30, 2026
…p escalation, SHA-based review freshness Gap 1 — no verdict for "value correct, meaning drifted" (PR #20550: three stacked framing distortions on an accurate 66% CNCF figure rendered as verified, with the drift observation buried in evidence prose): - New `framing-drift` (🌀) verdict across verify-claims.py, compose-review.py, validate-pinned.py (schema v18→v19), splicer.py, validator-fix.py, record-claims.py (kept), reverify-claims.py (stale), compose-pr-body.py. - Structured `framing` enum on verify_claim (exact-match / entailed-narrower / overclaim-broader / shifted / none); _finalize_verdict deterministically coerces a verified verdict carrying a drift shape to framing-drift, so the observation can no longer soft-pedal into verified prose. - Framing taxonomy recast in entailment terms ("does the source, as quoted, prove the claim as written?") and reconciled across claim-extraction.md, fact-check.md (whose fallback section previously mapped the same labels to opposite verdicts), and the verifier prompt. - Bucket:⚠️ Low-confidence by default; reviewer promotes to 🚨 when the drifted phrasing rides social.* frontmatter (auto-posted on merge). - New source-discipline rule: the content under review never verifies its own technical claims (the AES256-GCM circular-verification case on PR #20371). Gap 2 — pass1 turn-cap and routing failures reported as unverifiable (PR #20556: six REST-API claims died at the 8-turn cap; PR #20371: a pure CLI-behavior claim routed to web search, which cannot read product source): - pass1 turn cap 8→12; cap exhaustion now auto-escalates once to pass3 instead of returning bare unverifiable. - Symmetric route escalation: pass3 verifiers may emit route_escalation: "pass1" for Pulumi-product-behavior claims that carry no pulumi-shaped token (one hop, ping-pong guarded). - Terminal turn-cap records carry `turn_cap_exhausted: true` and retryable phrasing; compose-review stubs them with an explicit "never call this out of scope" TODO so budget failures and architectural limits can no longer render identically downstream. Gap 3 — stale pinned review kept review:no-blockers (PR #20556: a Copilot conflict-resolution push fired no pull_request:synchronize event, so mark-stale never ran; App pushes like workprentice[bot] do fire it — the suppressed class is Copilot-agent and GITHUB_TOKEN pushes): - compose-review.py stamps a machine-readable `<!-- CLAUDE_REVIEW_HEAD <sha> -->` sentinel under the review header (the workflow already passes --head-sha); update.md requires the re-entrant path to refresh it. - pr-review SKILL Step 2 now runs a SHA freshness check before trusting CURRENT labels, with a review-history fallback for pre-sentinel reviews. - New scheduled review-label-reconcile.yml workflow compares each labeled open PR's reviewed SHA against its head and applies the same stale transition mark-stale would have applied — the polling-side net for pushes that suppress webhook events. Tests: new test_framing_drift.py (36 checks: constant sync, coercion, escalation hops, cap marker, stub placement, validator acceptance/violation directions, header sentinel); all existing docs-review and content-review suites pass; make lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KcNQVWHUyvbbe5Go9MGxwA
The first cut folded framing-drift into CONTRADICTION_FAMILY, which feeds the 🔍 trail <details> header and the investigation log's parenthetical — both of which label that number `contradicted`. But framing-drift defaults to⚠️ , not 🚨, so the headline reported contradictions the body didn't contain: a fork run produced `2 verified · 0 unverifiable · 2 contradicted` above a trail with zero ❌ lines, and another read `4 contradicted` for 1 real ❌ plus 3 🌀. CONTRADICTION_FAMILY now means what its name says (contradicted + mismatch) and drift gets its own `· W framing-drift` segment in both places, omitted entirely when the count is zero so ordinary reviews are byte-identical. The per-lane Pass 2/3 V/C/U triple keeps drift in the C column — that triple's shape is pinned by validate-pinned.py's PASS2_OUTCOME_RE/PASS3_OUTCOME_RE, and it's a routing diagnostic rather than a reader-facing count. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CamSoper
force-pushed
the
CamSoper/docs-review-pipeline-gaps-k2ns5p
branch
from
July 30, 2026 21:44
7c1e138 to
f0480b5
Compare
CamSoper
marked this pull request as ready for review
July 30, 2026 21:45
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes
Three gaps in the docs-review pipeline produced clean pinned reviews on PRs with real problems (adjudicated 2026-07-29), making them invisible to the outcome metrics. Each gap's root cause was confirmed against live evidence before changing anything.
Gap 1 — new
framing-drift(🌀) verdict: "value correct, meaning drifted"On PR #20550, the verifier confirmed the 66% CNCF figure, wrote the framing distortion into
evidenceprose, and returnedverified— because the vocabulary offered onlyverified(nothing wrong) orcontradicted(feels false when the number is right), and aframing_notewithout a matching verdict has no bucket effect.framing-driftacross the pipeline:verify-claims.py,compose-review.py,validate-pinned.py(schema v18→v19, following theflaggedv15→v16 precedent),splicer.py,validator-fix.py,record-claims.py,reverify-claims.py,compose-pr-body.py, plusoutput-format.md/fact-check.md/claim-extraction.md/ci.mdand the workflow prompt.framingenum on theverify_claimtool (exact-match | entailed-narrower | overclaim-broader | shifted | none). The harness, not the model, places the observation:_finalize_verdict()coercesverified+ drift-shapedframingtoframing-drift, closing the soft-pedal path mechanically.fact-check.md§Mandatory evidence-line format saidstrengthened → contradicted;claim-extraction.mdand the verifier prompt saidstrengthened → verified).social.*frontmatter (auto-posts on merge — exactly Add "How to Run AI Agents on Kubernetes with Pulumi" blog post #20550's exposure). The validator acceptsframing-driftthe same way it does forflagged.framing-driftis not part of theZ contradictedheadline tally (it defaults to2 contradictedabove a trail with zero ❌ lines). It gets its own· W framing-driftsegment in both places, omitted when the count is zero so ordinary reviews render byte-identically. The per-lane Pass 2/3V/C/Utriple keeps drift in the C column; that triple's shape is pinned by the validator and it's a routing diagnostic, not a headline.pkg/logging/encrypted.gosays encryption is conditional).Gap 2 — turn-cap escalation + a machine-readable "retryable" marker
Six of PR #20556's seven unverifiables read literally "verification did not converge within 8 turns" (the cap's own comment predicted this failure mode); PR #20371's log-rotation claim (pure CLI behavior, no pulumi-shaped token) routed to web search, which cannot read Go source. Both then surfaced downstream as generic unverifiable — narrated by the sweep as "out of scope."
unverifiable(the explicit-escalation valve existed but was unreachable once capped).route_escalation: "pass1"for Pulumi-product-behavior claims, one hop, ping-pong guarded.turn_cap_exhausted: trueplus retryable phrasing; the composer stubs them with an explicit "never call this out of scope" TODO, so a budget failure and an architectural limit can no longer render identically to any consumer.Gap 3 — SHA-based review freshness (labels alone can't be trusted)
PR #20556's Copilot conflict-resolution push at 2026-07-30T00:47Z fired no
pull_request: synchronizeevent (confirmed from the Actions run history), somark-stalenever ran and the PR closed atreview:no-blockerswith a pinned review describing replaced content. Ordinary App pushes (workprentice[bot]) do fire the event — the suppressed class is Copilot-agent andGITHUB_TOKENpushes.compose-review.pystamps a machine-readable<!-- CLAUDE_REVIEW_HEAD <sha> -->sentinel under the review header (the workflow already passes--head-sha);update.mdrequires the re-entrant path to refresh it./pr-reviewStep 2 runs a SHA freshness check before trustingCURRENTlabels, with alast-reviewed-shareview-history fallback for pre-sentinel reviews.review-label-reconcile.yml(every 2h, deterministic, label transitions only): compares each labeled open PR's reviewed SHA against its live head and applies the same transitionmark-stalewould have. Skips when it can't justify a flip.Tests
New
test_framing_drift.py(36 checks: cross-script constant sync, coercion, both escalation hops, the cap marker, stub placement, validator acceptance/violation in both directions, header sentinel). All existing docs-review and content-review suites pass;make lintclean.Out of scope
The pr-review-sweep punt-template wording lives in the work-ops plugin (a personal plugin repo unreachable from this session). The
turn_cap_exhaustedmarker makes the conflation impossible at the source regardless; the template fix should still land separately.Unreleased product version (optional)
N/A — pipeline/tooling only; no product docs content changed.
Related issues (optional)
Follow-up from the 2026-07-29 punted-PR adjudication of #20371, #20550, #20552, #20556.
🤖 Generated with Claude Code
https://claude.ai/code/session_01KcNQVWHUyvbbe5Go9MGxwA
Generated by Claude Code