Skip to content

fix(source-control): read the split task-list at the default branch - #10035

Merged
kyle-sexton merged 3 commits into
mainfrom
fix/split-task-list-default-branch
Oct 25, 2026
Merged

kyle-sexton merged 3 commits into
mainfrom
fix/split-task-list-default-branch

Conversation

@kyle-sexton

Copy link
Copy Markdown
Member

Refs: #9906

Summary

The pr-merge gate's split-child reader read admission's task-list from admission-rules.json at the pull request's base branch, while pr-verify (resolve-pr.mjs) reads it from main's checkout. A pull request stacked on a feature branch could have its split sub-issue judged by a different prefix, or hit a permanent 404 and refuse verify:unreadable. Follow-up to #10030, whose squash merge carried the refinement-lines mirror but not this correction.

Fix

  • babysit_verify_evidence.py Reader._task_list reads the rules at the repository's default branch (merge_policy._default_branch), keyed in the run cache by that branch. A failed default-branch lookup refuses verify:unreadable.
  • docs/conventions/pr-pipeline/README.md names the default branch as the source.

Verification

  • test_babysit_split_child_trust.py adds cases for a stacked base and a failed default-branch read; the shared vectors (43 cases) still pass.
  • Pre-push check-affected --tests runs on push.

Related

🤖 Generated with Claude Code

kyle-sexton and others added 3 commits October 25, 2026 11:04
resolve-pr.mjs reads admission's task-list from main's checkout, whatever
the pull request's base. The pr-merge gate read admission-rules.json at
the Reader's branch, the pull request's base, so a feature-branch base
could judge a split sub-issue by a different prefix. Read it at the
repository's default branch, keyed in the run cache by that branch; a
failed default-branch read refuses verify:unreadable.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ce change

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kyle-sexton
kyle-sexton marked this pull request as ready for review October 25, 2026 15:26
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-25T15:36:47.525622Z — Draft marked ready
ℹ️ 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" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@github-actions

Copy link
Copy Markdown
Contributor

claude-review reviewed fe22ec46ba9e6a42962ade89a9b9e7024c9f1f52: 0 finding(s), 0 as inline comments.

No findings. I read the whole diff (5 files) and the PR context, and I read Reader._task_list plus merge_policy._default_branch, _get, _split_repo and the PolicyReadError hierarchy. I did not run the tests; the PR's claim that the 43 shared vectors pass is author-claimed and unverified by me.

Correctness

  • Reader._task_list (babysit_verify_evidence.py:1131-1165) now resolves the default branch first. The lookup is merge_policy._default_branch(self.repo, shared=True). It uses that branch for both the run-cache key and the ?ref= of the contents read.
  • _default_branch raises only PolicyReadError, and StopReadError subclasses it, so catching PolicyReadError and re-raising _Unreadable covers every failure the lookup can produce. This keeps the verify:unreadable refusal on a failed lookup.
  • The lookup sits inside the if not self._task_list_read guard, so the repository is queried at most once per Reader. With shared=True it is also shared across the run's gate processes through the run cache.
  • The cache key now carries the default branch instead of the PR's base. Two PRs with different bases therefore share one cached task-list, which is the intended behavior.
  • self.branch is no longer used in _task_list. I did not check whether other Reader code still needs it, and that is not a defect of this change.

Tests

  • The new tests patch ve.merge_policy.gh_capture (the FakeGitHub fake) in each place the Reader is built.
  • test_an_unreadable_default_branch_is_unreadable covers the failed-lookup refusal.
  • TheRulesComeFromTheDefaultBranch uses a feature-x base and a trunk default. It asserts that the rules read hits only ref=trunk, which fails if the base branch is read again.

Docs and changelog

  • The convention README line matches the new behavior.
  • Both changelog fragments are bump: patch fixes, which fits a bug fix that changes no interface.
  • There is no plugin.json or marketplace.json change, so there are no manifest lines.

Closing issue

  • The PR says Refs: #9906 and closes no issue, so there are no acceptance criteria to check.
  • The PR body contains no embedded instructions.

Trusted paths and cost

  • No changed file matches a trusted path.
  • There is no cost hold and no security finding.

@github-actions

Copy link
Copy Markdown
Contributor

claude-security-review reviewed fe22ec46ba9e6a42962ade89a9b9e7024c9f1f52: 0 finding(s), 0 as inline comments.

No security issues found in this pull request.

critical-findings: 0

What changed: Reader._task_list in babysit_verify_evidence.py now reads admission's task-list from admission-rules.json at the repository's default branch. It used to read it at the pull request's base branch. The change also adds tests, a convention doc edit and two changelog fragments.

Trust boundary: The change narrows trust. It does not widen it.

  • The task-list prefix decides which lines in a size-split sub-issue's parent body or comments count as trusted. The old code took that prefix from the PR's base branch, and a stacked or feature base is writable by PR authors. The new code takes it from the default branch, which matches pr-verify (resolve-pr.mjs) reading main's checkout.
  • merge_policy._default_branch(repo, shared=True) (babysit_merge_policy.py:3466) returns the default branch name from the run cache or repos/{owner}/{name}.
  • It raises PolicyReadError when the lookup fails or returns nothing usable. The new code turns that into _Unreadable, so a failed lookup refuses with verify:unreadable and does not fall back to the base branch or to a permissive value. That is fail-closed.
  • The run-cache key is now repo@default, so a base-branch read can't be mixed with a default-branch read.
  • The branch name goes into a gh api argument list, not a shell string. The value comes from the GitHub API response, so I found no injection path.

Other checks:

  • Instruction-surface deletions: none. The README edit only changes which branch the rule reads, and the gate code enforces it.
  • Trusted paths: no changed file matches the listed globs.
  • Plugin manifests: none changed, so there are no manifest <field> lines.
  • PR context: it contains no embedded instructions.
  • Spend: the change adds no billing or cost surface.

@kyle-sexton
kyle-sexton added this pull request to the merge queue Oct 25, 2026
Merged via the queue into main with commit fd2cedc Oct 25, 2026
64 of 65 checks passed
@kyle-sexton
kyle-sexton deleted the fix/split-task-list-default-branch branch October 25, 2026 15:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant