Skip to content

fix(verifier): normalize structured provider content - #2887

Merged
stranske merged 2 commits into
mainfrom
codex/fix-verifier-structured-content
Aug 1, 2026
Merged

fix(verifier): normalize structured provider content#2887
stranske merged 2 commits into
mainfrom
codex/fix-verifier-structured-content

Conversation

@stranske

@stranske stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Source: Issue #2878

Closes #2878

Automated Status Summary

Scope

scripts/check_consumer_sync_drift.py:420-456 already records pending_sync_prs, but build_report() still sets top-level status to drift; main() then returns 1 for every non-converged state (scripts/check_consumer_sync_drift.py:841-860). The workflow runs on source pushes and at 05:10 UTC (.github/workflows/health-68-consumer-sync-drift.yml:6-17), before the 05:30 janitor, and comments on the durable issue for every failure (health-68-consumer-sync-drift.yml:81-201). Live evidence showed 249 runs with zero successes in 49 days and 223 comments on #2210. This is a current observability break: expected, covered propagation is reported as failure.

Context for Agent

Related Issues/PRs

Tasks

  • Extend scripts/check_consumer_sync_drift.py:358-495 to parse current sync provenance and classify repo-level remediation states.
  • Add desired plan/hash input from scripts/sync_manifest_compiler.py and require an open PR marker/branch to match it before state becomes covered.
  • Change scripts/check_consumer_sync_drift.py:841-860 so converged and fully covered reports exit zero; blocked, untracked_drift, lookup errors, and expired coverage exit non-zero.
  • Update .github/workflows/health-68-consumer-sync-drift.yml to run after Maint 71 and invoke issue mutation only for actionable failure states.
  • Update .github/scripts/consumer_sync_drift_issue_body.js so body/marker/comment output renders the five states and does not append an unchanged covered-state comment.
  • Extend tests/scripts/test_check_consumer_sync_drift.py with current, superseded, blocked, expired, and lookup-error fixtures.
  • Extend .github/scripts/__tests__/consumer-sync-drift-issue-body.test.js with comment-suppression and actionable-state cases.
  • Update docs/ops/CONSUMER_REPO_MAINTENANCE.md and docs/ops/DURABLE_TRACKING_ISSUES.md with the state and SLO contract.

Acceptance criteria

  • python -m pytest tests/scripts/test_check_consumer_sync_drift.py -q and node --test .github/scripts/__tests__/consumer-sync-drift-issue-body.test.js pass.
  • Drift in every affected repo with a current, unexpired generated PR produces top-level status covered and process exit 0.
  • Drift with no current PR, a superseded hash, a deterministic failed check, or an expired lease produces a non-zero exit and exact actionable repo list.
  • Repeated covered-state runs do not add comments to 🔄 Consumer repo drift detected #2210; a changed actionable fingerprint updates the durable tracker once.
  • Deliberate-break gate: temporarily make the PR hash differ from the compiler plan in the covered fixture; tests/scripts/test_check_consumer_sync_drift.py::test_current_sync_pr_covers_drift must fail or change the report to untracked_drift. Restore the fixture before review.

Copilot AI review requested due to automatic review settings August 1, 2026 17:25
@stranske stranske added agent:codex Agent-created issues from Codex agents:keepalive Use to initiate keepalive functionality with agents autofix Opt-in automated formatting & lint remediation agent:retry Add to trigger agent retry after rate limit or pause labels Aug 1, 2026
@cursor

cursor Bot commented Aug 1, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 25 minutes

Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 662326de-5709-47fa-92b6-e1abd0a25a70

📥 Commits

Reviewing files that changed from the base of the PR and between 2321dbe and c823739.

📒 Files selected for processing (2)
  • scripts/langchain/pr_verifier.py
  • tests/scripts/test_pr_verifier_structured_output.py

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Runner dispatch state for codex on PR #2887. Do not edit.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates the PR verifier’s structured-output parsing so it can reliably handle providers that return structured “content blocks” (e.g., Anthropic-style lists of {type, text} blocks) by coercing them into a single text payload before schema parsing.

Changes:

  • Add _coerce_response_content() to normalize non-string LLM response content into a string for downstream JSON/schema parsing.
  • Broaden _parse_llm_response() to accept content: object, parse the coerced string, and store a normalized raw_content.
  • Add a regression test covering structured (block-based) response content in the comparison runner.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
scripts/langchain/pr_verifier.py Normalizes LLM response content (string vs block lists) before structured parsing and uses normalized raw_content.
tests/scripts/test_pr_verifier_structured_output.py Adds test coverage for block-based structured response content handling.

Comment thread scripts/langchain/pr_verifier.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3496de0cbd

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/langchain/pr_verifier.py
@agents-workflows-bot

Copy link
Copy Markdown
Contributor

🤖 Bot Comment Handler

  • Agent: codex
  • Bot comments to address: 2

The agent has been assigned to this PR to address the bot review comments.

Instructions for agent

  1. Implement suggested fixes that improve the code
  2. Skip suggestions that don't apply (note why in your response)

The bot comment handler workflow has prepared context in the artifacts.

codex-automation and others added 2 commits August 1, 2026 12:44
Concatenate provider text blocks without a separator so a JSON document split
across blocks reassembles faithfully, and coerce schema-repair responses through
the same normalization so a structured repair reply is not rejected as CONCERNS.
@stranske
stranske force-pushed the codex/fix-verifier-structured-content branch from 3496de0 to c823739 Compare August 1, 2026 17:45
@stranske

stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Review response — both findings were valid and are fixed in c8237395

1. copilot-pull-request-reviewer (scripts/langchain/pr_verifier.py:689) — valid, fixed.
_coerce_response_content now uses "".join(text_blocks) instead of "\n".join(...). Confirmed the failure mode is real rather than cosmetic: if a provider splits one JSON document across two text blocks and the split lands inside a string literal, the inserted newline is an unescaped control character and the reassembled payload is no longer valid JSON, so the verifier would burn a schema-repair attempt (or return CONCERNS) on output that was actually fine.

2. chatgpt-codex-connector P1 (scripts/langchain/pr_verifier.py:696) — valid, fixed.
Traced the exact path described: structured_output.build_repair_callback._repair returns getattr(response, "content", None) or str(response), so an Anthropic repair reply hands back a list of thinking/text blocks; _invoke_repair_loop then calls EvaluationPayload.model_validate_json(repaired) on that list, which raises, sets error_stage="repair_validation", and yields CONCERNS even when the repaired payload is correct. _build_verifier_repair_callback now routes the repair reply through the same _coerce_response_content normalization, returning None on an empty/absent reply so the repair_unavailable stage is still distinguishable from repair_validation. The fix is kept verifier-local; scripts/langchain/structured_output.py is unchanged, so issue_optimizer.py (the only other build_repair_callback consumer) is unaffected.

Both new gates are demonstrated load-bearing

Each fix was individually reverted and the matching test was confirmed to fail:

Reverted change Failing test Observed failure
"".join"\n".join test_comparison_runner_concatenates_split_text_blocks payload no longer parses; invoke call count 1 → 2 (spurious repair)
repair-reply normalization removed test_comparison_runner_normalizes_structured_repair_response assert 'CONCERNS' == 'PASS' — exactly the P1's predicted symptom

Source was restored and re-verified clean afterwards.

Validation on pushed head c8237395

  • pytest tests/scripts/test_pr_verifier_structured_output.py tests/test_structured_output.py tests/scripts/test_pr_verifier_compare.py51 passed (structured-output suite went 33 → 35 with the two new regressions)
  • ruff check clean; ruff format --check → already formatted; mypy scripts/langchain/pr_verifier.py → no issues
  • git diff --check clean

Rebased onto current main (2321dbeb, picking up #2888/#2889 — no file overlap with this change) before pushing.

@stranske
stranske enabled auto-merge (squash) August 1, 2026 17:45
@stranske
stranske merged commit 2e3e5b3 into main Aug 1, 2026
44 checks passed
@stranske
stranske deleted the codex/fix-verifier-structured-content branch August 1, 2026 17:49
@stranske stranske added the verify:compare Compare multiple LLM evaluations label Aug 1, 2026
stranske added a commit that referenced this pull request Aug 1, 2026
* fix(verifier): normalize structured provider content

* fix(verifier): preserve split text blocks and normalize repair responses

Concatenate provider text blocks without a separator so a JSON document split
across blocks reassembles faithfully, and coerce schema-repair responses through
the same normalization so a structured repair reply is not rejected as CONCERNS.

---------

Co-authored-by: Codex Automation <codex-automation@users.noreply.github.com>
Co-authored-by: Cursor Automation <cursor-automation@users.noreply.github.com>
@github-actions

github-actions Bot commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Provider Comparison Report

Provider Summary

Provider Model Verdict Confidence Summary
openai gpt-5.6-terra FAIL 99% The code may be a self-contained change for LangChain PR-verifier structured provider content, but it does not implement the documented consumer-sync-drift PR scope or any of its acceptance criteri...
anthropic claude-sonnet-5 FAIL 90% The supplied acceptance criteria and task list describe extensive changes required in scripts/check_consumer_sync_drift.py, the Health 68 workflow, and related test/doc files to fix consumer-sync-d...
📋 Full Provider Details (click to expand)

openai

  • Model: gpt-5.6-terra
  • Verdict: FAIL
  • Confidence: 99%
  • Scores:
    • Correctness: 2.0/10
    • Completeness: 0.0/10
    • Quality: 6.0/10
    • Testing: 2.0/10
    • Risks: 4.0/10
  • Summary: The code may be a self-contained change for LangChain PR-verifier structured provider content, but it does not implement the documented consumer-sync-drift PR scope or any of its acceptance criteria. This is a substantive scope/implementation mismatch, so the acceptance verification fails.
  • Concerns:
    • The merged changes are scoped to scripts/langchain/pr_verifier.py and its structured-output tests, while every documented task and acceptance criterion concerns consumer-sync drift detection, its workflow, durable-issue rendering, tests, and operations documentation.
    • scripts/check_consumer_sync_drift.py was not changed, so the required covered/untracked/blocked/expired/lookup-error classification, compiler plan/hash matching, actionable repository list, and zero exit for fully covered drift are not implemented by this PR.
    • .github/workflows/health-68-consumer-sync-drift.yml was not changed, so the required scheduling after Maint 71 and actionable-state-only issue mutation are not addressed.
    • .github/scripts/consumer_sync_drift_issue_body.js was not changed, so covered-state comment suppression and five-state rendering are not addressed.
    • The required consumer-sync Python and JavaScript tests, deliberate hash-mismatch gate, and operations documentation updates are absent. The added pr_verifier structured-output tests do not verify the stated acceptance criteria.
    • Because the implementation does not modify the system identified in the PR plan, it cannot establish the required behavior for issue 🔄 Consumer repo drift detected #2210 or prevent covered propagation from being reported as failure.

anthropic

  • Model: claude-sonnet-5
  • Verdict: FAIL
  • Confidence: 90%
  • Scores:
    • Correctness: 3.0/10
    • Completeness: 0.0/10
    • Quality: 5.0/10
    • Testing: 3.0/10
    • Risks: 4.0/10
  • Summary: The supplied acceptance criteria and task list describe extensive changes required in scripts/check_consumer_sync_drift.py, the Health 68 workflow, and related test/doc files to fix consumer-sync-drift status reporting. However, the actual PR diff only modifies scripts/langchain/pr_verifier.py (+27/-5) and adds a new test file for structured provider content normalization — a completely unrelated fix to the PR verifier tool itself. None of the required files, logic changes, or test fixtures (current/superseded/blocked/expired/lookup-error states, exit code changes, comment-suppression logic, deliberate-break gate) appear in the diff. As delivered, this PR does not implement or satisfy the stated acceptance criteria for the consumer-sync-drift observability fix, regardless of code quality in the unrelated change it does contain.
  • Concerns:
    • The PR title and diff ('fix(verifier): normalize structured provider content') only touch scripts/langchain/pr_verifier.py and its test file — these changes relate to sanitizing LLM provider response content in the AI PR-verifier tool.
    • None of the required files from the stated scope/tasks were modified: scripts/check_consumer_sync_drift.py, .github/workflows/health-68-consumer-sync-drift.yml, .github/scripts/consumer_sync_drift_issue_body.js, tests/scripts/test_check_consumer_sync_drift.py, .github/scripts/tests/consumer-sync-drift-issue-body.test.js, docs/ops/CONSUMER_REPO_MAINTENANCE.md, docs/ops/DURABLE_TRACKING_ISSUES.md.
    • None of the acceptance criteria (covered/blocked/untracked_drift/expired state classification, exit codes, comment suppression, deliberate-break gate test) can be verified as satisfied because the relevant code was never changed in this diff.
    • The linked issue [Dependency/Sync] Make Health 68 distinguish covered drift from actionable drift #2878 and PR fix(verifier): normalize structured provider content #2887 plan describe an entirely different feature (Health 68 consumer sync drift classification) than what the actual code changes implement.
    • It is unclear whether this PR's actual purpose (normalizing structured provider content in the verifier) was even part of the intended task, or whether the wrong PR/diff was reviewed against this acceptance criteria set.

Agreement

  • Verdict: FAIL (all providers)
  • Correctness: scores within 1 point (avg 2.5/10, range 2.0-3.0)
  • Completeness: scores within 1 point (avg 0.0/10, range 0.0-0.0)
  • Quality: scores within 1 point (avg 5.5/10, range 5.0-6.0)
  • Testing: scores within 1 point (avg 2.5/10, range 2.0-3.0)
  • Risks: scores within 1 point (avg 4.0/10, range 4.0-4.0)

Disagreement

No major disagreements detected.

Unique Insights

  • openai: The merged changes are scoped to scripts/langchain/pr_verifier.py and its structured-output tests, while every documented task and acceptance criterion concerns consumer-sync drift detection, its workflow, durable-issue rendering, tests, and operations documentation.; scripts/check_consumer_sync_drift.py was not changed, so the required covered/untracked/blocked/expired/lookup-error classification, compiler plan/hash matching, actionable repository list, and zero exit for fully covered drift are not implemented by this PR.; .github/workflows/health-68-consumer-sync-drift.yml was not changed, so the required scheduling after Maint 71 and actionable-state-only issue mutation are not addressed.; .github/scripts/consumer_sync_drift_issue_body.js was not changed, so covered-state comment suppression and five-state rendering are not addressed.; The required consumer-sync Python and JavaScript tests, deliberate hash-mismatch gate, and operations documentation updates are absent. The added pr_verifier structured-output tests do not verify the stated acceptance criteria.; Because the implementation does not modify the system identified in the PR plan, it cannot establish the required behavior for issue 🔄 Consumer repo drift detected #2210 or prevent covered propagation from being reported as failure.
  • anthropic: The PR title and diff ('fix(verifier): normalize structured provider content') only touch scripts/langchain/pr_verifier.py and its test file — these changes relate to sanitizing LLM provider response content in the AI PR-verifier tool.; None of the required files from the stated scope/tasks were modified: scripts/check_consumer_sync_drift.py, .github/workflows/health-68-consumer-sync-drift.yml, .github/scripts/consumer_sync_drift_issue_body.js, tests/scripts/test_check_consumer_sync_drift.py, .github/scripts/tests/consumer-sync-drift-issue-body.test.js, docs/ops/CONSUMER_REPO_MAINTENANCE.md, docs/ops/DURABLE_TRACKING_ISSUES.md.; None of the acceptance criteria (covered/blocked/untracked_drift/expired state classification, exit codes, comment suppression, deliberate-break gate test) can be verified as satisfied because the relevant code was never changed in this diff.; The linked issue [Dependency/Sync] Make Health 68 distinguish covered drift from actionable drift #2878 and PR fix(verifier): normalize structured provider content #2887 plan describe an entirely different feature (Health 68 consumer sync drift classification) than what the actual code changes implement.; It is unclear whether this PR's actual purpose (normalizing structured provider content in the verifier) was even part of the intended task, or whether the wrong PR/diff was reviewed against this acceptance criteria set.

🔍 LangSmith Traces

@stranske

stranske commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Closer verifier disposition — the FAIL is a scope mismatch, not a defect

Provider Comparison Report 5152804099 (run 30712423939, 18:30:48Z) returned FAIL / FAIL (openai gpt-5.6-terra 99%, anthropic claude-sonnet-5 90%). Both verdicts say the same thing: this diff "does not implement the documented consumer-sync-drift PR scope or any of its acceptance criteria."

That claim is true about the diff and wrong about which PR should be judged against those criteria. Audited against the merged commits rather than the PR body:

Root cause of the false verdict: the follow-up PR was opened with the standard source-issue preamble (<!-- meta:issue:2878 --> / Closes #2878) and inherited #2878's full task and acceptance-criteria block. verify:compare therefore scored a two-file parser fix against a completely different issue's acceptance criteria. The graders behaved correctly given the inputs they were handed.

The fix is independently confirmed working by this very run. Run 30712423939 completed success and produced a full two-provider comparison with per-dimension scores — i.e. the structured-content path that crashed on #2885 now parses. Local validation at merge time was 33 passed on the verifier structured-output + compare suites, with Ruff check/format clean.

Actions taken this round:

  1. Re-dispatched agents-verifier.yml in compare mode against merged PR fix(sync): classify covered consumer drift #2885 (run 30713009619, 18:39:03Z) so issue [Dependency/Sync] Make Health 68 distinguish covered drift from actionable drift #2878 finally gets a genuine verdict on the diff that actually implements it. That run also serves as the end-to-end regression check for this parser fix.
  2. Closing the [Dependency/Sync] Make Health 68 distinguish covered drift from actionable drift #2878 follow-up chain here (followup_completed); [Dependency/Sync] Make Health 68 distinguish covered drift from actionable drift #2878 stays closed on the strength of fix(sync): classify covered consumer drift #2885.

Carried forward, not dropped: CodeRabbit's 18:33:02Z thread on pr_verifier.py:692 (a repair response of [{"type": "text", "text": ""}] is truthy, so the empty-repair case falls through to json.dumps and the parser treats block metadata as a repair attempt) landed after this PR merged and is a real, if minor, residual edge case in this code. It is recorded against the open review set rather than silently resolved.

Process note for the next agent: when opening a bounded follow-up PR that fixes tooling rather than implementing the source issue, do not carry the source issue's acceptance-criteria block into the PR body, or verify:compare will manufacture a FAIL like this one. Either omit the preamble or replace the criteria with the follow-up's own.

stranske pushed a commit that referenced this pull request Aug 1, 2026
Three CodeRabbit findings arrived at 18:33Z against code that had already
merged (#2887, #2888), so they surfaced on an unrelated open PR's diff
instead of anywhere actionable.

- pr_verifier: a repair reply of only thinking/metadata blocks was truthy,
  so the serialized envelope was returned as repair text and the parser
  failed on JSON the provider never sent. The repair path is now stricter
  than the parse path: no text blocks, or blank text, means no repair.
  The parse path keeps its json.dumps fallback for diagnostics.
- enforce_gate_branch_protection: two of the three snapshot sites hardcoded
  strict: True, so --snapshot --allow-non-strict wrote a health artifact
  claiming strict enforcement that --apply would not create.
- enforce_gate_branch_protection: the --context help still advertised
  'Gate / gate' as the default; DEFAULT_CONTEXTS is 'summary', and #2858
  is specifically about not requiring 'Gate / gate'.

Co-authored-by: Cursor <cursoragent@cursor.com>
stranske added a commit that referenced this pull request Aug 2, 2026
)

* fix: close the review findings that landed after their PRs merged

Three CodeRabbit findings arrived at 18:33Z against code that had already
merged (#2887, #2888), so they surfaced on an unrelated open PR's diff
instead of anywhere actionable.

- pr_verifier: a repair reply of only thinking/metadata blocks was truthy,
  so the serialized envelope was returned as repair text and the parser
  failed on JSON the provider never sent. The repair path is now stricter
  than the parse path: no text blocks, or blank text, means no repair.
  The parse path keeps its json.dumps fallback for diagnostics.
- enforce_gate_branch_protection: two of the three snapshot sites hardcoded
  strict: True, so --snapshot --allow-non-strict wrote a health artifact
  claiming strict enforcement that --apply would not create.
- enforce_gate_branch_protection: the --context help still advertised
  'Gate / gate' as the default; DEFAULT_CONTEXTS is 'summary', and #2858
  is specifically about not requiring 'Gate / gate'.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(gate): keep already-strict snapshot target under allow-non-strict

--allow-non-strict accepts a non-strict floor; an already-strict
policy is still in sync. Snapshot desired.strict now mirrors that
accepted current value so health_summarize does not render
"✅ In sync" beside a True → False transition.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(gate): wire already-strict snapshot target under allow-non-strict

Implements the snapshot_desired_strict path the prior commit's
regression test expects, so health artifacts stay consistent when
an already-strict policy is accepted under --allow-non-strict.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(gate): preserve already-strict on apply and cover review gaps

Pass the effective strict target into update_status_checks under
--allow-non-strict, and add the split-text, fetch-error, and apply
context-drift regressions CodeRabbit flagged on #2890.

Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(autofix): formatting/lint

---------

Co-authored-by: Codex Automation <codex-automation@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

agent:codex Agent-created issues from Codex agent:retry Add to trigger agent retry after rate limit or pause agents:keepalive Use to initiate keepalive functionality with agents autofix Opt-in automated formatting & lint remediation verify:compare Compare multiple LLM evaluations

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Dependency/Sync] Make Health 68 distinguish covered drift from actionable drift

3 participants