Skip to content

Tools: add PR batch merge ledger#3996

Merged
justin808 merged 46 commits into
mainfrom
jg-codex/3907-pr-merge-ledger
Jun 15, 2026
Merged

Tools: add PR batch merge ledger#3996
justin808 merged 46 commits into
mainfrom
jg-codex/3907-pr-merge-ledger

Conversation

@justin808

@justin808 justin808 commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds script/pr-merge-ledger plus script/pr-merge-ledger.schema.json to produce a machine-checkable PR closeout ledger from GitHub PR state.
  • Requires the strict ledger in .agents/workflows/pr-processing.md and $pr-batch closeout guidance before merge readiness or batch handoff.
  • Adds fixture/RSpec coverage for merged PR ci(rspack): decouple per-bundler build + integration jobs so one bundler's build failure doesn't skip the other (#3593) #3613 and wires root RuboCop coverage for the new script.
  • Hardens the ledger after review: resolved-thread priority findings are scanned, disposition evidence is validated, review ordering ties are stable, multi-PR disposition reuse is scoped, explicit UNKNOWN changelog classifications are rejected, gh subprocess timeouts avoid Ruby Timeout.timeout around Open3.capture3, and rate-limit/secondary-limit responses stay on the retry path.

Closes #3907.

Validation

Current head SHA: fba964e48f09ca7a61258103cdad77f001087f7a

  • ruby -c script/pr-merge-ledger -> Syntax OK.
  • cd react_on_rails && bundle exec rspec spec/react_on_rails/pr_merge_ledger_script_spec.rb -> 105 examples, 0 failures.
  • cd react_on_rails && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop ../script/pr-merge-ledger spec/react_on_rails/pr_merge_ledger_script_spec.rb -> no offenses.
  • git diff --check -> passed.
  • pnpm start format.listDifferent -> passed with the repo's existing Node dependency bin path.
  • Pre-commit hooks passed scoped Ruby autofix/RuboCop and trailing-newline checks.
  • Pre-push hooks passed branch RuboCop and online Markdown link checks.
  • script/ci-changes-detector origin/main -> full suite recommended; no benchmarks indicated.

Acceptance dry run:

  • script/pr-merge-ledger --fixture react_on_rails/spec/react_on_rails/fixtures/pr_merge_ledger/pr_3613.json --changelog-classification not_user_visible --strict --pretty > /tmp/pr-3613-merge-ledger-fba964e48.json -> strict exit 1.
  • Result reproduces the rc.2 miss: complete_allowed=false with review_decision_changes_requested, unresolved_current_head_review_thread, and unknown_priority_finding_disposition violations.

Self-ledger:

  • Finding dispositions artifact: /tmp/pr-3996-finding-dispositions-after-fba964e48-complete.json.
  • script/pr-merge-ledger 3996 --repo shakacode/react_on_rails --changelog-classification not_user_visible --finding-dispositions /tmp/pr-3996-finding-dispositions-after-fba964e48-complete.json --strict --pretty > /tmp/pr-3996-merge-ledger-final-fba964e48.json -> strict exit 1 only for unknown_review_decision.
  • Result: complete_allowed=false; unresolved current-head review threads: 0; unknown priority dispositions: 0; unused disposition keys: 0.

Current GitHub checks snapshot:

  • All current-head checks are complete: 44 pass and 7 expected skips.
  • Passing/skipped/advisory in the latest snapshot: claude-review passed, CodeRabbit skipped, Cursor Bugbot neutral/skipping, benchmark confirmation jobs skipped.
  • No failed current-head checks observed in the final snapshot.

Codex Decision Log

  • Resolved: Current-head ledger hardening after review.
    • Decision: Fixed resolved-thread priority scanning, disposition validation, review-order tie handling, multi-PR disposition reuse, explicit UNKNOWN changelog classification handling, 4xx retry handling, 429 retry handling, 403 secondary/rate-limit retry handling, and subprocess timeout cleanup.
    • Evidence: 105-example focused RSpec suite passes; strict self-ledger has zero unresolved current-head threads, zero unknown priority dispositions, and zero unused disposition keys.
  • Non-blocking: Schema URI currently points at main.
    • Decision: Keep the current $schema URI and rely on emitted schema_version: pr-merge-ledger/v1 for this initial internal gate.
    • Why: Tag-pinning or publishing schema URLs is a compatibility policy decision and is not required to close the machine-checkable PR-batch closeout: machine-checkable per-PR merge ledger (extends #3851) #3907 closeout gap.
    • Review later: Choose a stable schema hosting/tagging policy before external consumers depend on archived ledger artifacts.
  • Non-blocking: conservative_non_outdated evidence is a string.
    • Decision: Keep the v1 schema stable; operators still get the PR head SHA and each thread comment's comment_head_sha in the same ledger thread object.
    • Review later: Consider structured evidence if real closeouts show ambiguity.
  • Non-blocking: Broad .lock suffix detection.
    • Decision: Keep the conservative lockfile detector for this initial gate.
    • Review later: Narrow package-lock detection if false positives appear in real batch ledgers.

Agent Merge Confidence

Mode: accelerated-rc
Current head SHA: fba964e48f09ca7a61258103cdad77f001087f7a
Score: 8/10 for maintainer-delegated human merge
Auto-merge recommendation: no; the independent accelerated-RC finalizer is not present, so this is not an unattended auto-merge path
Maintainer-delegated merge recommendation: yes, per current-session maintainer instruction: "You can merge if you are confident. Just document your criteria in the pr description."
Affected areas: CI/process tooling, root Ruby script, JSON schema, RSpec coverage
CI detector: script/ci-changes-detector origin/main -> full suite recommended; labels remain full-ci, full-ci-no-benchmarks
Validation run:

  • ruby -c script/pr-merge-ledger -> Syntax OK
  • cd react_on_rails && bundle exec rspec spec/react_on_rails/pr_merge_ledger_script_spec.rb -> 105 examples, 0 failures
  • cd react_on_rails && BUNDLE_GEMFILE=../Gemfile bundle exec rubocop ../script/pr-merge-ledger spec/react_on_rails/pr_merge_ledger_script_spec.rb -> no offenses
  • pnpm start format.listDifferent -> passed with the repo's existing Node dependency bin path
  • git diff --check -> passed
  • Pre-commit and pre-push hooks -> passed scoped RuboCop, markdown, and link checks
  • ci(rspack): decouple per-bundler build + integration jobs so one bundler's build failure doesn't skip the other (#3593) #3613 fixture ledger -> strict exit 1 with the expected historical ci(rspack): decouple per-bundler build + integration jobs so one bundler's build failure doesn't skip the other (#3593) #3613 ledger violations
  • Strict self-ledger -> exit 1 only for GitHub reviewDecision UNKNOWN; unresolved current-head review threads, unknown priority dispositions, and unused disposition keys are 0
    Review/check gate:
  • GitHub checks: complete for current head fba964e48f09ca7a61258103cdad77f001087f7a; 44 pass and 7 expected skips
  • Review threads: current strict ledger/GraphQL unresolved count is 0
  • Current-head reviewer verdicts: claude-review completed successfully; Cursor Bugbot neutral/skipping; CodeRabbit skipped
  • Review-object safety: no current-head CHANGES_REQUESTED review object remains; all actionable current-head findings were fixed, replied to, or dispositioned
  • Stale reviewer verdicts, advisory only: prior Claude/Codex/Cursor/CodeRabbit feedback on older heads was triaged or resolved, not used as a current-head gate
    Known residual risk: GitHub aggregate reviewDecision is blank/UNKNOWN; this is explicitly waived for this maintainer-delegated human merge because concrete review-thread, review-object, finding-disposition, and check evidence is otherwise complete for the current head
    Finalized by: maintainer-delegated merge authority from justin808 in the Codex session on 2026-06-15; not an independent accelerated-RC auto-merge finalizer

Merge Criteria

Current head SHA: fba964e48f09ca7a61258103cdad77f001087f7a

Release mode is accelerated-rc from #3823. This PR is mergeable only through the maintainer-delegated human merge path, not unattended accelerated-RC auto-merge. I am comfortable merging when all of these are true immediately before merge:

  • Current head is still fba964e48f09ca7a61258103cdad77f001087f7a.
  • Full current-head GitHub check list is complete; every check is successful or skipped with selector/waiver evidence allowed by AGENTS.md.
  • claude-review has completed for the current head without a confirmed blocker.
  • A fresh GraphQL/ledger review-thread fetch reports zero unresolved current-head review threads.
  • The strict self-ledger reports zero unresolved current-head threads, zero unknown priority dispositions, and zero unused disposition keys.
  • The only strict self-ledger violation is unknown_review_decision, caused by GitHub returning a blank aggregate reviewDecision while concrete review-object/thread evidence is clean.
  • The blank aggregate reviewDecision is explicitly waived for this maintainer-delegated human merge because no current-head CHANGES_REQUESTED review object remains, all current-head threads are resolved, all priority findings are dispositioned, and all checks are green or expected skips.
  • The merge actor re-fetches live PR state after this PR description update and before merge.

Current merge decision: mergeable by maintainer-delegated human path after one final live re-fetch. This remains not an unattended accelerated-RC auto-merge because no independent finalizer is present.

Labels: full-ci, full-ci-no-benchmarks. This PR changes merge-gate/process tooling and specs, so it needs broad CI coverage but no runtime benchmark signal.


Note

Medium Risk
Changes merge-gate and batch-closeout policy for agents; incorrect ledger logic could block merges or miss real blockers, though scope is tooling/docs plus extensive fixture tests rather than runtime app code.

Overview
Introduces a machine-checkable merge closeout gate via script/pr-merge-ledger (with script/pr-merge-ledger.schema.json): it pulls GitHub PR state (reviews, threads, comments, lockfiles), emits versioned JSON with complete_allowed, violations, and UNKNOWN fields, and exits non-zero under --strict when changelog classification or P0/P1/P2/Must-Fix dispositions are missing or merge blockers remain.

Agent workflow docs (pr-processing.md, pr-batch skill) now require running the strict ledger at merge readiness and coordinator closeout, forbid marking targets complete while the ledger has UNKNOWN or complete_allowed: false, and expect ledger JSON in batch handoffs.

Repo tooling: root RuboCop covers script/pr-merge-ledger; a large RSpec suite plus a #3613 fixture exercise review-decision/thread/finding edge cases and live gh behavior (fixtures, pagination, retries).

Reviewed by Cursor Bugbot for commit fba964e. Bugbot is set up for automated code reviews on this repo. Configure here.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Introduces script/pr-merge-ledger, a standalone Ruby CLI that generates a JSON merge-readiness ledger for pull requests by fetching or loading GitHub data (review threads, reviews, issue comments, files), computing unresolved threads, finding dispositions, changelog classification, lockfile presence, and violations. Adds a JSON Schema, a comprehensive RSpec suite with fixtures, and updates agent workflow and skill docs to enforce the ledger as a required closeout gate.

Changes

PR Merge Ledger Tool and Workflow Enforcement

Layer / File(s) Summary
Workflow and skill documentation enforcement
.agents/workflows/pr-processing.md, .agents/skills/pr-batch/SKILL.md
Updates agent workflow and skill docs to mandate script/pr-merge-ledger <PR> --strict with explicit changelog classification and finding/disposition evidence at merge-readiness gates and in the coordinator closeout lane. Blocks complete reporting when ledger has UNKNOWN fields, unresolved current-head threads, CHANGES_REQUESTED review objects, or complete_allowed: false.
JSON Schema output contract
script/pr-merge-ledger.schema.json
Defines strict JSON Schema for ledger report, covering top-level metadata, pull_request_ledger definition with all required sections (review threads, reviews, findings, changelog, lockfile, and ledger controls), and violation/unknown_field object shapes.
CLI entrypoint, constants, option parsing, and report assembly
script/pr-merge-ledger (lines 1–220)
Implements PrMergeLedger class entrypoints and constants for finding regex patterns, option parsing for --strict, --pretty, --schema, --fixture, --finding-dispositions, and --changelog-classification, fixture loading vs live fetching dispatch, repository detection via gh repo view, disposition JSON loading, and report envelope construction with metadata.
GitHubCollector: paginated GraphQL data fetching
script/pr-merge-ledger (lines 824–1140)
Adds PrMergeLedger::GitHubCollector with fetch(pr_number) for paginated gh api graphql calls to fetch PR files, review threads (with nested comment pagination), reviews, and issue comments. Includes error handling, cursor advancement validation, maximum page limits, GraphQL error detection, and timeout enforcement via GITHUB_API_TIMEOUT.
Ledger normalization, analysis, findings, violations, and report emission
script/pr-merge-ledger (lines 223–823)
Implements full per-PR ledger generation: PR/review-thread/review/comment normalization with pagination detection, unresolved-thread and current-head derivation, evidence scanning for P0/P1/P2/MUST_FIX markers with summary suppression, disposition resolution with string/object overrides and validation, changelog classification and lockfile diff detection, unknown_fields generation for truncation and UNKNOWN values, and violation assembly for all completeness checks.
Test fixture and comprehensive RSpec coverage
react_on_rails/spec/react_on_rails/fixtures/pr_merge_ledger/pr_3613.json, react_on_rails/spec/react_on_rails/pr_merge_ledger_script_spec.rb
Adds pr_3613.json fixture with realistic PR metadata, files, review threads, and reviews. Covers comprehensive RSpec suite: smoke test against fixture, strict-mode failure/success for reviewDecision/changelog/headRefOid edge cases, draft/closed PR handling, superseded CHANGES_REQUESTED handling, finding extraction (P0/P1/P2, negative summaries, multi-finding, issue comments), pagination truncation, disposition maps (string and object), live-source integration, GraphQL error propagation, and --schema output validation.
Build configuration linting
Rakefile
Adds script/pr-merge-ledger to the RuboCop root path whitelist, ensuring the script is checked by linting tasks.

Sequence Diagram(s)

sequenceDiagram
  participant Coordinator as Coordinator Agent
  participant Script as script/pr-merge-ledger
  participant GH as gh api graphql
  participant Fixture as Fixture JSON

  Coordinator->>Script: run --strict --changelog-classification X --finding-dispositions Y <PR>
  alt fixture mode
    Script->>Fixture: load JSON
    Fixture-->>Script: raw PR data
  else live mode
    Script->>GH: GraphQL: PR files
    GH-->>Script: files nodes
    Script->>GH: GraphQL: review threads (paginated)
    GH-->>Script: thread nodes + comments
    Script->>GH: GraphQL: reviews (paginated)
    GH-->>Script: review nodes
    Script->>GH: GraphQL: issue comments (paginated)
    GH-->>Script: comment nodes
  end
  Script->>Script: normalize + analyze (threads, reviews, findings, changelog, lockfile)
  Script->>Script: assemble violations + unknown_fields
  Script-->>Coordinator: JSON ledger (complete_allowed, violations, unknown_fields)
  alt complete_allowed: false or UNKNOWN fields
    Coordinator->>Coordinator: block "complete" reporting
  else complete_allowed: true, no violations
    Coordinator->>Coordinator: proceed to merge/handoff
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #3907 (PR-batch closeout: machine-checkable per-PR merge ledger): This PR directly implements the ledger script, JSON schema, tests, and workflow/skill documentation enforcement described in that issue's acceptance criteria, including reproducing PR #3613 as a ledger violation in the fixture test.

Possibly related PRs

  • shakacode/react_on_rails#3824: Both modify the agent batch "Coordinator Closeout Lane" and handoff formatting in .agents/workflows/pr-processing.md and .agents/skills/pr-batch/SKILL.md, with this PR further tightening closeout to run the new script/pr-merge-ledger <PR> --strict.
  • shakacode/react_on_rails#3691: Both update the agent .agents/workflows/pr-processing.md merge-readiness and closeout workflow logic, with this PR specifically adding strict script/pr-merge-ledger <PR> --strict gating and ledger artifact evidence.
  • shakacode/react_on_rails#3757: Both update .agents/workflows/pr-processing.md's merge/qualification closeout guidance, with this PR tightening it via script/pr-merge-ledger --strict and the retrieved PR adjusting advisory-tier merge gating and workflow-scope handling in the same sections.

Suggested labels

enhancement, documentation, codex

Suggested reviewers

  • alexeyr-ci2
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Tools: add PR batch merge ledger' accurately summarizes the main change: introduction of a PR merge ledger tool for batch closeout validation.
Linked Issues check ✅ Passed The PR fully implements the acceptance criteria from #3907: introduces a machine-checkable ledger script with schema, updates pr-processing.md and pr-batch guidance to require it, and includes comprehensive RSpec coverage demonstrating violations.
Out of Scope Changes check ✅ Passed All changes directly support the #3907 objective of creating a PR merge ledger system; RuboCop whitelist update is a necessary housekeeping change for the new script file.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jg-codex/3907-pr-merge-ledger

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread script/pr-merge-ledger
@claude

claude Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #3996: Tools: add PR batch merge ledger

Overview

Adds script/pr-merge-ledger, a Ruby CLI that queries live GitHub GraphQL data (or a fixture) and emits a structured JSON closeout ledger covering: review decision, unresolved current-head threads, superseded reviews, P0/P1/P2/Must-Fix findings, changelog classification, and lockfile touches. --strict gates on complete_allowed: true. Agent workflow docs are updated to require the ledger at merge-readiness time, and RSpec coverage is provided via fixture PR #3613.


Strengths

  • Clean class decomposition: PrMergeLedger / GitHubCollector split is clear and testable.
  • Pagination correctness: all top-level GraphQL connections (files, reviewThreads, reviews, comments) are fully paginated; the inner comments(first:100) limit is explicitly detected and surfaced as UNKNOWN.
  • Superseded-review logic is correct: latest_by_reviewer correctly dismisses old CHANGES_REQUESTED reviews when the same reviewer later approves, and all_changes_requested still records them for audit.
  • Fixture-based testing: avoids live API calls and makes the 12-example suite fast and deterministic.
  • Error handling: Error, JSON::ParserError, and Errno::ENOENT all produce clean exit-2 messages; option validation funnels through OptionParser::InvalidArgument.

Issues

Bug / accuracy gap — multiple same-severity findings from one source are collapsed to one entry

unique_findings.uniq { |f| [f.fetch("url"), f.fetch("severity")] } deduplicates on (source_url, severity). If a reviewer leaves two separate **P1** lines in one review body, only one finding is recorded. The violation still fires (UNKNOWN disposition blocks), but the ledger underreports the count and the audit trail is incomplete for multi-finding reviews.

The fix is to include a per-line index or hash in the dedup key, or to emit one finding per review/comment source rather than per line:

# Option A: dedup by (url, severity, line_index) to preserve distinct findings
unique_findings = findings.each_with_index.uniq { |(finding, idx), _| [finding.fetch("url"), finding.fetch("severity"), idx] }.map(&:first)

# Option B: simpler — one finding per (source id, severity) which is what the current
# disposition model already assumes (disposition is per source, not per line)
unique_findings = findings.uniq { |f| [f.fetch("id"), f.fetch("severity")] }

Option B aligns with the disposition model (you waive a review/comment, not an individual line) and avoids double-counting.


Missing test cases

  1. changelog_present and deferred_to_update_changelog as non-violating states: only not_user_visible and changelog_missing are explicitly exercised; regressions in the other two valid non-blocking classifications would go undetected.
  2. Clean/happy-path PR: no test directly asserts complete_allowed: true for a straightforward APPROVED PR with no threads or findings. It's covered indirectly (tests 5, 10, 11) but an explicit case would be self-documenting.
  3. head_sha propagation when headRefOid is absent: normalize_pull_request sets head_sha to nil when headRefOid is missing; downstream comparisons (comment.dig("commit","oid") == pull_request.fetch("head_sha")) silently return false, marking all threads as non-current. Worth a guard or test.

Minor concerns

  • lockfile_path? matches any .lock suffix (basename.end_with?(".lock")). Files like advisory.lock or deploy.lock would register as lockfile changes. Consider restricting to the explicit LOCKFILE_BASENAMES list unless the broader catch is intentional.
  • current_thread? implicit assumption: the method returns true for any non-outdated thread regardless of its commit SHA — this is correct per GitHub semantics (non-outdated = diff still applies) but the assumption isn't documented and may surprise maintainers who expect an explicit commit-SHA comparison.
  • source_summary records the absolute fixture path when run with --fixture. If ledger artifacts are shared or committed, this leaks system-specific paths. Consider recording only the basename.
  • No timeout on gh api graphql calls: for batch use against slow API, a per-call timeout would prevent indefinite hangs. Low priority for a CLI tool but worth noting.

Summary

The overall implementation quality is high. The core concern worth addressing before merge is the finding deduplication key ([url, severity] → should be [id, severity] to match the per-source disposition model) and adding the two missing classification tests. Everything else is polish or documentation.

Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger
@justin808 justin808 force-pushed the jg-codex/3907-pr-merge-ledger branch from f90bd3e to 7a8e5e2 Compare June 13, 2026 23:10
Comment thread script/pr-merge-ledger Outdated
@justin808 justin808 added full-ci full-ci-no-benchmarks Full CI test matrix but hard-suppress all benchmark suites (overrides detection + benchmark label) labels Jun 13, 2026
@justin808

Copy link
Copy Markdown
Member Author

Addressed current-head review feedback in 7a8e5e2: distinct same-severity findings from one source now remain separate via source line/excerpt, missing headRefOid is an UNKNOWN gate, and specs now cover clean approved PRs plus non-blocking changelog classifications. Merge criteria are now listed in the PR description.

@justin808 justin808 marked this pull request as ready for review June 13, 2026 23:12

@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: 7a8e5e2464

ℹ️ 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 script/pr-merge-ledger
@claude

claude Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR Batch Merge Ledger

Summary

This PR introduces script/pr-merge-ledger, a Ruby tool that queries GitHub GraphQL to produce a machine-verifiable JSON ledger of PR merge readiness: review decisions, unresolved threads, P0/P1/P2/Must-Fix finding dispositions, changelog classification, and lockfile diffs. The script is wired into the agent batch workflow docs and backed by 16 RSpec examples using fixtures and tempfiles.

Overall quality is high. The class-based architecture, clean GitHubCollector/PrMergeLedger separation, explicit UNKNOWN propagation, and paginated live-fetch with truncation detection are all well-executed. The test coverage is thorough for the happy and failure paths it covers. Issues below are mostly correctness edge-cases and one schema design point — none are blockers, but a few are worth addressing.


Issues

1. KeyError not rescued in run (script/pr-merge-ledger:68)
rescue Error, JSON::ParserError, Errno::ENOENT leaves KeyError (raised by .fetch on missing hash keys in a malformed fixture) unhandled. A fixture missing "pull_request" produces a Ruby backtrace instead of a clean pr-merge-ledger: … message. Adding KeyError to the rescue list would make the UX consistent.

2. head_sha nil vs UNKNOWN asymmetry (script/pr-merge-ledger:251)
review_decision defaults to UNKNOWN when absent (|| UNKNOWN), but head_sha stays nil from raw_pr["headRefOid"]. The JSON output then has "head_sha": null even though the schema declares head_sha: {type: "string"}. The check on line 501 (.to_s.empty?) correctly detects this, but the emitted value violates the schema contract. Defaulting to || UNKNOWN (or an empty string) would make it consistent with review_decision.

3. current_thread? silently treats isOutdated: nil as non-outdated (script/pr-merge-ledger:344)
return true unless thread["isOutdated"] returns true for both false and nil (missing field in fixture). This means a fixture thread without an explicit isOutdated key is always considered current-head — the safe default, but an implicit assumption. A brief comment here would prevent future confusion.

4. Cross-source finding deduplication gap (script/pr-merge-ledger:377–387)
The uniqueness key is [id, url, severity, source_line, text_excerpt]. Because id and url are per-source, the same [P1] text appearing in both a review summary body and a top-level PR comment (different id/url, same text) produces two separate findings, each needing its own disposition. This seems intentional given that both URLs are resolvable, but it could surprise users who dispose one URL and still see a violation for the other. Worth a comment or a note in the --help text.

5. Schema pull_request_ledger uses additionalProperties: true (script/pr-merge-ledger.schema.json)
The root object has "additionalProperties": false (strict), but the pull_request_ledger $def uses "additionalProperties": true. Extra unknown keys in per-PR ledgers would pass schema validation silently. If the intent is forward-compatibility, that is fine — but consider tightening it now and relaxing later, since unvalidated extra fields in agent handoffs are a common source of silent mismatches.


Minor notes

  • Inner thread comment pagination: The GraphQL query fetches inner thread comments with first:100 and no cursor. The script correctly flags truncated pages as UNKNOWN. In the rare case of a >100-comment thread this blocks merge until manually resolved — documenting that limitation in --help or the script header would be useful.
  • source_summary in fixture mode exposes the full local filesystem path of the fixture in the JSON artifact. Fine for dev/CI use but worth noting if ledger artifacts are shared or stored.
  • body_text strip: The review.except("body_text") on line 188 is a clean pattern — keeping full text for scanning while dropping it from output. A brief comment there would make the intent clear to future readers.
  • Test coverage for not_user_visible: It is tested implicitly via the clean-PR spec but not explicitly in the classification test (which covers changelog_present and deferred_to_update_changelog). An explicit assertion in that block would close the gap.

Verdict

Approve after addressing items 1–2 (small, clear fixes) and optionally items 3–5. The tool is well-designed and the test corpus is production-quality.

Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger.schema.json
@greptile-apps

greptile-apps Bot commented Jun 13, 2026

Copy link
Copy Markdown

Greptile Summary

This PR introduces script/pr-merge-ledger, a new Ruby CLI tool (~835 lines) that queries GitHub GraphQL for PR review decisions, review threads, reviews, and comments to produce a machine-checkable JSON closeout ledger. It integrates the tool into the $pr-batch agent skill and coordinator workflow documentation as a required pre-merge gate.

  • New script: script/pr-merge-ledger collects review data from live GitHub or a fixture, evaluates violations (unresolved threads, CHANGES_REQUESTED reviews, missing changelog classification, unresolved P0/P1/P2/Must-Fix findings), and emits a validated JSON report; --strict exits non-zero when complete_allowed is false.
  • Tests & fixture: 16 RSpec examples cover the main acceptance paths; a real historical fixture for PR ci(rspack): decouple per-bundler build + integration jobs so one bundler's build failure doesn't skip the other (#3593) #3613 reproduces the known ledger violation; the root Rakefile now includes the script in RuboCop's lint pass.
  • Workflow docs: .agents/workflows/pr-processing.md and .agents/skills/pr-batch/SKILL.md are updated to require the ledger at merge readiness and batch closeout.

Confidence Score: 4/5

Safe to merge; purely internal process tooling with no runtime or package changes, and all four comments are non-blocking quality improvements.

The script is well-structured and thoroughly tested. The disposition-per-source issue is the most substantive concern: a single disposition entry silently clears every finding in a multi-finding review body, which could allow a ledger to report complete_allowed: true while genuine findings remain open. The remaining comments cover a missing KeyError rescue, inconsistent body storage for issue comments vs reviews, and the false-positive risk from negation sentences that start with a severity label. None of these block the intended workflow.

script/pr-merge-ledger — the disposition lookup and finding-pattern sections warrant a second look before the tool is used in production batch workflows.

Important Files Changed

Filename Overview
script/pr-merge-ledger New 835-line Ruby CLI; core logic is correct and well-tested. Minor issues: KeyError from malformed fixture keys not caught by the rescue clause; full issue-comment body stored in ledger while review body_text is stripped; leading-severity text in negation context can produce false findings; disposition lookup covers the whole source (id/url), not individual finding lines.
react_on_rails/spec/react_on_rails/pr_merge_ledger_script_spec.rb 16 comprehensive RSpec examples; covers all major violation codes, fixture, disposition, and pagination-truncation paths.
react_on_rails/spec/react_on_rails/fixtures/pr_merge_ledger/pr_3613.json Historical fixture for merged PR #3613; reviewDecision CHANGES_REQUESTED + one unresolved thread accurately reproduce the documented historical violation.
script/pr-merge-ledger.schema.json JSON Schema 2020-12 for the ledger output; all required fields and $defs defined; pull_request_ledger uses additionalProperties: true intentionally.
Rakefile One-line addition wires script/pr-merge-ledger into the root RuboCop task.
.agents/workflows/pr-processing.md Coordinator closeout lane updated to require the ledger tool at step 5; final handoff and merge-readiness gate updated.
.agents/skills/pr-batch/SKILL.md Three coherent doc additions: merge readiness ledger requirement, batch handoff ledger summary requirement, and coordinator ownership of ledger run.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A([script/pr-merge-ledger]) --> B{--fixture?}
    B -- yes --> C[load_fixture JSON]
    B -- no --> D[GitHubCollector.fetch via GraphQL]
    D --> D1[metadata + files paginated]
    D --> D2[reviewThreads paginated]
    D --> D3[reviews paginated]
    D --> D4[comments paginated]
    C --> E[ledger_for dataset]
    D1 & D2 & D3 & D4 --> E
    E --> F[normalize_pull_request]
    E --> G[normalize_review_threads]
    E --> H[normalize_reviews]
    E --> I[normalize_issue_comments]
    F & G & H & I --> J[finding_dispositions\nextract P0/P1/P2/MUST-FIX lines]
    J --> K[disposition_for URL or ID lookup]
    F --> L[unknown_fields_for]
    G --> L
    J --> L
    L --> M[violations_for]
    M --> N[build_report JSON]
    N --> O{--strict AND\ncomplete_allowed: false?}
    O -- yes --> P([exit 1\nstderr: ledger violations])
    O -- no --> Q([exit 0\nstdout: JSON report])
Loading

Comments Outside Diff (4)

  1. script/pr-merge-ledger, line 1073-1075 (link)

    P2 Unhandled KeyError leaks Ruby backtraces on malformed fixtures

    run rescues Error, JSON::ParserError, and Errno::ENOENT, but Hash#fetch raises KeyError (a subclass of IndexError < StandardError, not of PrMergeLedger::Error). Any fixture that is missing a required key — e.g. "number", "id" on a thread, or "id" on a comment — will crash with a raw backtrace instead of a clean error message. Adding KeyError to the rescue line would make the tool self-consistent for malformed input.

  2. script/pr-merge-ledger, line 1330-1339 (link)

    P2 issue_comments retains full body while reviews strip body_text

    output_reviews explicitly calls .except("body_text") before writing to the ledger, but issue_comments is written verbatim with the full "body" field from normalize_issue_comment. This means the complete text of every top-level PR comment ends up in the output JSON, while review bodies are deliberately truncated to body_excerpt. Consider using the same excerpt-only pattern for issue comments to keep the output consistent and bounded in size.

    Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

  3. script/pr-merge-ledger, line 1035-1040 (link)

    P2 FINDING_PATTERNS can fire on negation or reference sentences that start with a severity label

    The patterns are anchored at \A on each line from each_line, so any line whose first non-whitespace token is P0, P1, P2, or MUST-FIX matches — even if the sentence is semantically negative (e.g. "P1 issues: none found", "P2 items were waived in the previous review"). The test "ignores negative P1/P2 summary text" only verifies "No P1/P2 findings.", which doesn't start with a severity marker. Reviews that summarise prior findings in the form "P2 finding from reviewer X has been resolved" would be extracted as new disposable findings. This is worth a documented caveat or a narrower pattern (e.g. requiring a colon or a bracketed form).

  4. script/pr-merge-ledger, line 1416-1432 (link)

    P2 Disposition lookup is per-source, so a single entry clears all findings on that review or comment

    disposition_for looks up by URL then by ID of the entire source object (review body or comment). When a review body contains multiple severity findings — as tested in "keeps same-severity findings from distinct lines" — providing one disposition entry for the review's URL marks every finding from that review as disposed, not just one. A user who supplies { "https://…/review-id" => "fixed" } intending to clear only the first P1 inadvertently clears the second P1 as well. This could produce a complete_allowed: true ledger with genuine open findings silently masked. Consider keying dispositions by "<url>#L<source_line>" or adding a line field to the disposition map format.

Reviews (1): Last reviewed commit: "Add PR batch merge ledger" | Re-trigger Greptile

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@script/pr-merge-ledger`:
- Around line 242-254: The `normalize_pull_request` function at lines 242-254 in
script/pr-merge-ledger assigns nil to "head_sha" when raw_pr["headRefOid"] is
missing, which violates the schema requirement that pr.head_sha must be a
required string. Apply the same fallback pattern used for "review_decision"
(with the || UNKNOWN operator) to the "head_sha" assignment so that when
headRefOid is omitted, it defaults to UNKNOWN instead of nil, keeping the output
schema-valid. This same fix must also be applied to the corresponding code at
lines 496-503 in the same file.
- Around line 165-168: The `load_dispositions` function accepts any JSON root
type (arrays, scalars, etc.) but the `disposition_for` method later assumes it
can index the result with string keys. If the JSON file contains an array or
scalar value instead of a JSON object, this causes an unhandled TypeError. Add
validation in `load_dispositions` after `JSON.parse(File.read(path))` to ensure
the parsed result is a Hash/object; if it is not, either return an empty hash or
raise an appropriate error so that the exception handling in the `run` method
can properly catch and handle it.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cd394096-3111-43e1-a1fe-a45e731df33f

📥 Commits

Reviewing files that changed from the base of the PR and between 809e306 and 7a8e5e2.

📒 Files selected for processing (7)
  • .agents/skills/pr-batch/SKILL.md
  • .agents/workflows/pr-processing.md
  • Rakefile
  • react_on_rails/spec/react_on_rails/fixtures/pr_merge_ledger/pr_3613.json
  • react_on_rails/spec/react_on_rails/pr_merge_ledger_script_spec.rb
  • script/pr-merge-ledger
  • script/pr-merge-ledger.schema.json

Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger
@claude

claude Bot commented Jun 13, 2026

Copy link
Copy Markdown
Contributor

Code Review

Overall: Well-structured addition of a machine-checkable merge-readiness ledger. The architecture is clean, the fixture-based test harness is solid, and the documentation integration is consistent across both workflow files. The 16 RSpec examples cover the important edge cases. A few issues worth addressing before merge.


Bug: KeyError escapes the rescue clause

run at line 68 rescues Error, JSON::ParserError, Errno::ENOENT but not KeyError. Ruby's Hash#fetch raises KeyError < IndexError < StandardError, which is not in that list. Several places in the parsing pipeline call .fetch on externally-supplied data — notably dataset.fetch("pull_request") in ledger_for, connection.fetch("nodes") in fetch_paginated_connection, and file_connection.fetch("pageInfo") in fetch_metadata_and_files. If any API response or fixture is missing an expected key, the user will see a raw Ruby backtrace and exit code 1 (the OS default) instead of the clean "pr-merge-ledger: …" message and exit code 2 the rescue is designed to produce.

Fix: add KeyError to the rescue list at line 68:

rescue Error, JSON::ParserError, Errno::ENOENT, KeyError => e

load_fixture only validates repository, not pull_request

load_fixture (line 159–162) raises a clean Error if repository is missing, but silently proceeds if pull_request is missing. The KeyError then surfaces later (see above) without a helpful message pointing at the fixture. Adding the check here gives operators an actionable error:

def load_fixture(path)
  JSON.parse(File.read(path)).tap do |dataset|
    raise Error, "fixture is missing repository" unless dataset["repository"]
    raise Error, "fixture is missing pull_request" unless dataset["pull_request"]
  end
end

datasets.first.fetch("repository") silently uses the first PR's repo for the whole report

build_report line 128 always takes the top-level repository value from datasets.first. In live mode every dataset already has the same repo string injected in fetch_live_pull_requests, so today this is fine. But there is no guard or comment making that invariant explicit. If the code ever evolves to support cross-repo batches, this will silently produce a misleading report. A comment or assertion would make the intent clear:

# All datasets share the same repository because fetch_live_pull_requests injects it.
"repository" => datasets.first.fetch("repository"),

Inner review-thread comment pagination is one-shot (acknowledged, but worth documenting)

review_threads_query fetches comments(first:100) without a nested cursor. Threads with >100 comments are correctly detected via pageInfo.hasNextPage and marked UNKNOWN — that logic is right. However, the limitation isn't documented anywhere in the script itself. A single inline comment above the query noting that inner-comment pagination isn't followed (and that truncation detection handles it) would help future maintainers.


Minor: .lock extension in lockfile_path? is slightly broader than necessary

basename.end_with?(".lock") would flag repo-level lock files used by system tooling (e.g. a bundler.lock, sidekiq.lock, advisory lock file, etc.) as package lockfile diffs. For this repo's typical file surface this is harmless, but a stray .lock file in a CI script or similar could produce a spurious has_lockfile_diff: true. The existing LOCKFILE_BASENAMES list already covers all conventional JS/Ruby lockfiles; matching on .lock extension alone feels overly broad. Consider removing it and instead expanding LOCKFILE_BASENAMES with any additional known names (e.g. composer.lock, Pipfile.lock, poetry.lock).


Nit: exit 0 inside add_help_option is untestable

Using Kernel#exit inside the OptionParser callback means --help can't be exercised by the RSpec integration tests without trapping SystemExit. This is a common Ruby CLI pattern and not a correctness issue, but it's worth noting if test coverage of --help output is ever desired.


Positive callouts

  • Clean separation between PrMergeLedger (orchestration) and GitHubCollector (I/O).
  • No shell injection risk — Open3.capture3("gh", *args) is correctly used in array form throughout.
  • The finding-deduplication key [id, url, severity, source_line, text_excerpt] correctly preserves distinct same-severity findings from the same source.
  • latest_reviews_by_reviewer using ISO 8601 string comparison to pick the most-recent review per reviewer is correct (ISO 8601 sorts lexicographically).
  • review_thread_comments_complete? correctly handles both array-form (legacy fixtures) and {nodes, pageInfo} object-form comments.
  • Fixture for ci(rspack): decouple per-bundler build + integration jobs so one bundler's build failure doesn't skip the other (#3593) #3613 is a realistic regression anchor.

Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
@justin808 justin808 force-pushed the jg-codex/3907-pr-merge-ledger branch from 7a8e5e2 to a71f6af Compare June 13, 2026 23:31
@justin808 justin808 force-pushed the jg-codex/3907-pr-merge-ledger branch from a71f6af to 050baca Compare June 13, 2026 23:33
Comment thread script/pr-merge-ledger Outdated
@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Code Review

Good PR overall — the test coverage is thorough and the ledger design is well thought-out. Three findings worth addressing before merge:


P1 — 429 is incorrectly marked as non-retryable

non_retryable_gh_error? includes HTTP 429 (Too Many Requests) in the set of errors that skip retries. 429 is a transient rate-limit signal — exactly what the exponential backoff loop exists to handle. Failing immediately on 429 means a rate-limited batch run aborts the entire collection rather than waiting and retrying.

The pattern GITHUB_API_RETRY_BASE_DELAY_SECONDS * (2**(attempt - 1)) gives delays of 0.25 s → 0.5 s before giving up. Those are short enough to clear most secondary rate-limit windows. Remove 429 from the non-retryable set; a dedicated Retry-After header reader would be ideal but the existing backoff is already better than immediate failure.

401/403/404 are genuinely non-retryable (auth errors, missing resource) and should stay.


P2 — CHANGELOG_CLASSIFICATIONS includes "UNKNOWN" in the OptionParser accepted-values array

The constant is passed directly to opts.on("--changelog-classification CLASS", CHANGELOG_CLASSIFICATIONS), which makes OptionParser treat every element as a user-facing valid value. "UNKNOWN" is the internal sentinel for "not supplied", not a value an operator should be able to pass explicitly.

Whether OptionParser accepts or rejects --changelog-classification UNKNOWN depends on Ruby's case-folding/abbreviation-matching rules for array arguments — behaviour that can vary across Ruby versions. The test "rejects explicit UNKNOWN changelog classification" expects exit 2 with invalid argument, which is only produced if OptionParser raises InvalidArgument. If a future Ruby version changes that matching behaviour, UNKNOWN would silently be accepted as a valid classification and bypass the guard entirely.

Fix: remove UNKNOWN from CHANGELOG_CLASSIFICATIONS and reject it explicitly in validate_options with a clear error message. That makes the contract version-proof.


P2 — detect_repo duplicates the Timeout.timeout / Open3.capture3 pattern outside capture_gh

capture_gh has a caveat comment explaining why Timeout.timeout around Open3.capture3 is risky, and it says "do not extend this pattern". detect_repo extends exactly that pattern independently (lines 270–276) without going through capture_gh and without the warning comment. It also never retries on transient failures.

Routing detect_repo through a capture_gh-style helper (or at minimum adding the same comment and a single retry) would keep the risky pattern in one place and give it the same durability as the GraphQL calls.


Nit — multi-PR mode silently drops unused-disposition warnings

When @argv.length > 1, enforce_unused_dispositions is false, so stale or misspelled disposition keys are swallowed without any message. A warning to stderr in that code path (mirroring what warn_unused_dispositions already does for single-PR mode) would surface typos without blocking the run.

Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger
@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Code Review — PR #3996: script/pr-merge-ledger

Overall this is a well-structured tool: the JSON schema is thorough, the fixture/RSpec coverage is unusually comprehensive for a CLI script, and the input validation (repo pattern, PR-number coercion, GraphQL variable sanitisation) is solid. A few issues worth addressing before merge:

Blocking / high-confidence

1. HTTP 429 treated as non-retryable (inline comment on non_retryable_gh_error?, line 1332)
GitHub rate-limiting returns 429 with a Retry-After hint. Including 429 in the "give up immediately" list means a CI run that happens to hit the rate limit produces a confusing failure rather than waiting and retrying. The three-attempt backoff loop already exists; 429 should go through it (or a dedicated sleep path) rather than bypassing it.

2. detect_repo bypasses retry and lacks the Timeout disclaimer (inline comment, lines 267–277)
Every other gh call goes through capture_ghcapture_gh_with_retries. detect_repo calls Open3.capture3 directly, so a single transient failure aborts the entire run. The capture_gh method also carries a documented disclaimer about Timeout.timeout interrupting mid-IO; detect_repo uses the same pattern without that note.
Side note: the PR description states "gh subprocess timeouts no longer use Ruby Timeout.timeout around Open3.capture3" — both detect_repo (line 269) and capture_gh (line 1339) still use it, so the description is inaccurate on this point.

Minor / non-blocking

3. Redundant unknown_fields.empty? in complete_allowed (inline comment, lines 240 and 356)
violations_for converts every unknown-field entry into a violation via unknown_field_violations, so violations.empty? already implies unknown_fields.empty?. The second clause is dead code. Harmless but worth cleaning up to avoid misleading future readers.

4. GITHUB_REPO_NAME_PATTERN is more permissive than GitHub's actual owner format
/\A[A-Za-z0-9._-]+\z/ allows dots and underscores in the owner segment. GitHub usernames and org names can't contain dots. This won't cause a security issue (args are passed as array elements to Open3.capture3, never through a shell), but the pattern gives a false sense of strict validation for the owner half of OWNER/REPO.

5. Schema URI points at the mutable main branch (acknowledged in PR description as non-blocking)
SCHEMA_URI resolves to whatever is currently on main, so a schema-aware validator could see a different schema than the one the script was written against. Acceptable for now per the decision log, but worth a follow-up tracking issue.

@claude

claude Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Review: PR 3996 - Add PR batch merge ledger

Well-structured internal tooling with thorough test coverage (104 RSpec examples). The ledger logic, disposition scanning, review-thread heuristics, and JSON schema are solid. One P1 bug and a few smaller notes below (also posted as inline comments).

Comment thread script/pr-merge-ledger Outdated
Comment thread script/pr-merge-ledger
Comment thread script/pr-merge-ledger

@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: 96bd0cf4f1

ℹ️ 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 script/pr-merge-ledger Outdated
@justin808

Copy link
Copy Markdown
Member Author

Merge Gate Block

Current head: fba964e48f09ca7a61258103cdad77f001087f7a

Local and GitHub validation are otherwise clean:

  • GitHub checks: 44 pass, 7 expected skips, no failures.
  • GraphQL unresolved review threads: 0.
  • Strict self-ledger artifact: /tmp/pr-3996-merge-ledger-blocked-audit.json.
  • Strict self-ledger result: complete_allowed=false only because GitHub reviewDecision is blank / UNKNOWN; unresolved current-head review threads are 0, priority finding dispositions are known, and unused disposition keys are 0.
  • PR body confidence block still says Finalized by: pending independent finalizer.

Blocked action: this PR needs a qualifying independent finalizer/approval for the current head, or an explicit maintainer-approved path for handling the blank GitHub aggregate review decision before it can be merged under the accelerated-RC rules.

@justin808

Copy link
Copy Markdown
Member Author

Maintainer-Delegated Merge Acknowledgement

Current head: fba964e48f09ca7a61258103cdad77f001087f7a

Final waiver set for this maintainer-delegated human merge path:

  • GitHub aggregate reviewDecision is blank / UNKNOWN.
  • No independent accelerated-RC auto-merge finalizer is present.

Immediate merge decision: proceed only if the final live re-fetch still shows all checks pass or expected-skip, unresolved review threads are 0, no current-head CHANGES_REQUESTED review object remains, and the strict self-ledger has no violations other than unknown_review_decision.

This is not an unattended accelerated-RC auto-merge; it is a maintainer-authorized merge path with criteria documented in the PR description.

@justin808 justin808 merged commit a1ea0d8 into main Jun 15, 2026
52 checks passed
@justin808 justin808 deleted the jg-codex/3907-pr-merge-ledger branch June 15, 2026 03:17
justin808 added a commit that referenced this pull request Jun 15, 2026
* origin/main:
  Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933)
  Add post-merge audit scope resolver (#4029)
  Document continuous agent-run evaluation loop (#4028)
  Tools: add PR batch merge ledger (#3996)
  Add RSC FOUC acceptance coverage (#4033)
  Keep plan-pr-batch goal prompts under 4000 chars (#4025)
  docs(skills): file-collision check + goal-prompt size discipline for plan-pr-batch (#3914)
  Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3938)

# Conflicts:
#	llms-full.txt
justin808 added a commit that referenced this pull request Jun 15, 2026
* origin/main:
  Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933)
  Add post-merge audit scope resolver (#4029)
  Document continuous agent-run evaluation loop (#4028)
  Tools: add PR batch merge ledger (#3996)
  Add RSC FOUC acceptance coverage (#4033)
  Keep plan-pr-batch goal prompts under 4000 chars (#4025)
  docs(skills): file-collision check + goal-prompt size discipline for plan-pr-batch (#3914)
  Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3938)

# Conflicts:
#	CHANGELOG.md
#	llms-full.txt
justin808 added a commit that referenced this pull request Jun 15, 2026
…ter-slice

* origin/main:
  Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933)
  Add post-merge audit scope resolver (#4029)
  Document continuous agent-run evaluation loop (#4028)
  Tools: add PR batch merge ledger (#3996)
  Add RSC FOUC acceptance coverage (#4033)
  Keep plan-pr-batch goal prompts under 4000 chars (#4025)
  docs(skills): file-collision check + goal-prompt size discipline for plan-pr-batch (#3914)
  Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3938)

# Conflicts:
#	llms-full.txt
justin808 added a commit that referenced this pull request Jun 15, 2026
…e-demo-cost-docs

* origin/main:
  Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933)
  Add post-merge audit scope resolver (#4029)
  Document continuous agent-run evaluation loop (#4028)
  Tools: add PR batch merge ledger (#3996)

# Conflicts:
#	llms-full.txt
justin808 added a commit that referenced this pull request Jun 15, 2026
…ce-maps

* origin/main:
  Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933)
  Add post-merge audit scope resolver (#4029)
  Document continuous agent-run evaluation loop (#4028)
  Tools: add PR batch merge ledger (#3996)
  Add RSC FOUC acceptance coverage (#4033)
  Keep plan-pr-batch goal prompts under 4000 chars (#4025)
  docs(skills): file-collision check + goal-prompt size discipline for plan-pr-batch (#3914)
  Verify React 19.2 <Activity> with react_component (CSR + SSR-hydrate) + docs (#3938)

# Conflicts:
#	llms-full.txt
justin808 added a commit that referenced this pull request Jun 15, 2026
…c-streaming

* origin/main:
  Expose React 19 root error callbacks (rootErrorHandlers) + hydration-mismatch debugging guide (#3933)
  Add post-merge audit scope resolver (#4029)
  Document continuous agent-run evaluation loop (#4028)
  Tools: add PR batch merge ledger (#3996)
  Add RSC FOUC acceptance coverage (#4033)

# Conflicts:
#	llms-full.txt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

full-ci full-ci-no-benchmarks Full CI test matrix but hard-suppress all benchmark suites (overrides detection + benchmark label)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

PR-batch closeout: machine-checkable per-PR merge ledger (extends #3851)

1 participant