Repository navigation
Back T6 and T12 with tests; read ruleset-only branch rules - #183
Merged
Merged
Conversation
T6: a job reruns `cmd:` checks on a collaborator head once the person re-attributes and re-approves it (base unchanged), and a fresh merge inspection reads both rule sources again when base and head match. Recording real gh output for T12 showed two bugs in #rules, which made every ruleset-only branch (this repository's main among them) read as "Required branch checks could not be read.": - GitHub omits `integration_id` for a required check any app may report; that now reads as no app pin. - `branches/<b>.protected` is true when only a ruleset applies, and `/protection` then answers 404 "Branch not protected"; that answer now means no classic protection. Other 404s still fail closed. scripts/record-merge-gh.ts records the gateway's real gh calls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test/merge-recorded.test.ts drives the real parser and the merge job with GitHub's recorded answers: zero required checks (PR 104), and PR 183's two required checks pending, then passing at the same head. The job waits and merges, blocks at the time limit, and stops at once against the repository's real non-strict ruleset. Fail-closed tests cover every other 404 on a protected branch. The design plan ticks T6 and T12 with evidence; ci.md says how to record the gh fixtures again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The non-strict ruleset job test answers with pending checks, so a job that waited before reading the guard would fail it. - The hand-written gh 404 answers are built as runWithInput reports them; the "Not Found" case is the answer a token without admin rights gets on a protected branch (checked on cli/cli trunk). - T6 evidence cites the test that shows the review recomputed. - Drop a test that repeated the classic missing app_id case. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…exactly Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Branch reads in the gh recordings keep only name and protected (the gateway reads only protected), dropping commit authors' emails; the recorder does the same. Test titles state each 404 row's outcome, and the rule-refresh fake sends ruleset checks as GitHub does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n token Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g base - A "Branch not protected" 404 counts as no classic protection only when the same branch read shows protection.enabled false. The recordings keep `protection` again, from the original answers. - A new job test fails the command check only on a head holding the moved base's new file, so the base itself breaks it. - The pr merge fakes say what they leave out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…swers Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ed fields Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
🟢 Approval recommended
The narrowly scoped parser fixes preserve fail-closed behavior, have focused regression coverage, and have no unresolved review findings.
No open issues 🎉
What changed in this PR
Adds regression evidence for T6 and T12 and fixes ruleset-only branch handling in the guarded merge gateway.
Changes:
- Accepts omitted ruleset app IDs and confirmed absence of classic protection while preserving fail-closed behavior.
- Adds recorded GitHub responses and tests for refreshed rules, changed heads, check progression, and timeout.
- Updates implementation documentation and task completion evidence; #176 remains open.
| File | Description |
|---|---|
| test/pre-merge.test.ts | Tests breaking-base and collaborator-head check reruns. |
| test/merge.test.ts | Tests rule refresh, review exclusion, and protection parsing. |
| test/merge-recorded.test.ts | Replays recorded responses through inspection and merge jobs. |
| test/fixtures/gh/zero-checks.json | Records a branch without required checks. |
| test/fixtures/gh/recorded.ts | Loads and replays recorded CLI responses. |
| test/fixtures/gh/checks-pending.json | Records pending required checks. |
| test/fixtures/gh/checks-passing.json | Records passing required checks. |
| scripts/record-merge-gh.ts | Captures inspection responses with branch-data filtering. |
| github/merge.ts | Fixes ruleset app IDs and classic-protection absence detection. |
| docs/implementation/guarded-merge.md | Documents parser behavior and permission limitations. |
| docs/implementation/ci.md | Documents fixture recording and replay. |
| docs/designs/codeboost-plan-indexed-review.md | Marks T6 and T12 verified with evidence. |
| docs/architecture.md | Updates merge verification status. |
Review effort: Balanced
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.
Closes #184
Backs design tasks T6 and T12 (
docs/designs/codeboost-plan-indexed-review.md, Implementation Tasks) with tests and ticks both. Recording realghoutput for T12 found two product bugs, which are fixed here.Bugs fixed (
github/merge.ts#rules)On this repository's own
main, codeboost blocked every merge with "Required branch checks could not be read.", for these two reasons:Missing
integration_id. GitHub leavesintegration_idout of a ruleset's required check when any app may report it ({"context":"test"}). codeboost required the key, so it setrulesKnown: false. A missing key now reads asnull(no app pin), as GitHub documents. A malformed value still fails closed, and classicapp_idis still required.Ruleset-only branches.
GET branches/<b>reportsprotected: truewhen only a ruleset applies.GET branches/<b>/protectionthen answers an admin token with 404{"message":"Branch not protected","status":"404"}, and the branch read showsprotection.enabled: false. codeboost threw "Branch protection is present but unreadable". Now, only gh's own 404 with that body, together withprotection.enabled: false, counts as no classic protection. Everything else on a protected branch still fails closed:{"message":"Not Found"}that a token without admin rights gets (checked on cli/clitrunk, gh 2.102.0);protection.enabledtrue or missing.What a non-admin token gets on a ruleset-only branch was not checked. If it is "Not Found", that branch still blocks as unknown rules.
Fix commits and regression tests (#184). History was kept rather than rewritten, so the fix is spread over three commits:
ba80fb0: the first fix of both causes, committed together with the T6 tests.a19dcf4: requiresstatus: "404"in the body.031c64b: also requires the branch read'sprotection.enabled: false.Regression tests that use GitHub's real recorded answers and fail without the fix (checked by reverting
github/merge.tstomain):test/merge-recorded.test.ts"reads the ruleset's required checks as pending, then as passing at the same head";test/merge.test.ts"for GitHub's "Branch not protected" answer with classic protection off, …".Fakes that send what real gh/GitHub does not (audit; not changed here)
test/merge.test.ts:app: { databaseId: 10 }on check runs (lines ~918, ~1215, ~1428). gh'sstatusCheckRollupnever carriesapp; this is App-pinned required checks always read as missing #176.integration_id: nullon ruleset checks (lines ~922, ~1672, andrequiredTestState's default). GitHub omits the key for an any-app check; it was never seen sendingnull. These paths now read both forms the same way.{ protected: true }with noprotectionobject (6 tests). GitHub always sentprotectionin the recordings. The code reads it only after a "Branch not protected" 404, which these tests never reach.{ type: 'merge_queue' }with noparameters(line ~922). GitHub's merge-queue rule carriesparameters; the code does not read them.test/merge-job.test.ts:RemoteMergeState, not gh output. They pairatomicBaseGuard: truewith zero required checks andmergeQueue: false, a state the real gateway never produces: with nothing required, only a merge queue gives the guard. "merges at once when the required set was read and is empty" uses that state.T6
Verify: "a rebase onto a breaking main blocks the merge; a head-only collaborator push recomputes review and reruns checks; matching SHAs still refresh both rule sources".
Breaking main.
test/pre-merge.test.ts"fails the job when the moved base itself breaks the command check on the rewritten head". The check fails only on a head that holds the moved base's new file, so it would pass on the reviewed head.Collaborator push. The design (
merge-handoff.md, Outcomes:review-required) reruns checks on the next job after the person reviews the new head, not automatically on the push. "reruns command checks on a collaborator head after review when the base did not move" walks that path:Matching SHAs.
test/merge.test.ts:In the recorded job test, every inspection reads both rule sources.
T12
Verify: "recorded-output tests for zero checks, pending then passing, and timeout". Title: "Read required checks from branch rules; zero checks passes; ignore reviews".
scripts/record-merge-gh.tsruns one inspection through the production gateway and the realgh. It saves each call's argv, exit status, stdout and stderr, asrunWithInputreports them. A branch read keeps onlyname,protectedandprotection, which drops commit authors' emails. A fresh recording of PR 104 reproduces its fixture exactly.test/fixtures/gh/holds three, all from this repository with gh 2.102.0:zero-checks: PR 104, into a branch that no rule covers.checks-pending: this PR atba80fb0, whiletestandreal-dockerran.checks-passing: the same head, after both checks passed.test/merge-recorded.test.tsreplays the recordings through the real parser and through the merge job. Each test asserts that everyghcall had a recorded answer, apart from the job'sgh pr merge, which is faked. The job waits while the checks are pending and merges once they pass. At the time limit, with the checks still pending, it endsblocked.test/merge-job.test.ts, "ends queued when the merge queue accepted the head").app.databaseId.Follow-ups (not in this PR)
branches/<b>answer already carriesprotection.required_status_checks, which read-only tokens can see. This would remove the admin-only/protectionread.test/review.test.ts"keeps review load available when continuation reconciliation refuses" timed out once at 15 s under full-suite load. It passes alone. This PR does not touch that file.Validation
Each gate is reported separately. Validated head:
eeafaf7.eeafaf7:test/merge-recorded.test.tsandtest/merge.test.ts, 197 passed. Intest/pre-merge.test.ts, the new tests (3 cases) passed.eeafaf7(8 merge and pre-merge files, run locally at load average ~88): 503 passed, 1 failed. The failure was the existing "stops an active command check when the operation-wide deadline expires": its deadline ran out before the check attempt started (attemptundefined). It passes alone, and CItestpassed it. This is recorded as a load failure.npx tsc --noEmitis clean.testis the full-suite gate.eeafaf7:testpassed (the full Vitest suite), andreal-dockerpassed. Mergeable:MERGEABLE,CLEAN.Review record
42abf4a. Rounds 1–17 found issues, all fixed:protection.enabled: false, and added a real breaking-base test.status === "404", and asserting that every call was recorded. They also corrected claims and the notes on what fakes leave out.eeafaf7, base42abf4a): no findings. That clean pass authorizes the Copilot request.eeafaf7): "Approval recommended … No open issues", with no inline comments and 0 review threads.Review-lesson audit
No
AGENTS.mdorCLAUDE.mdchange is needed.🤖 Generated with Claude Code