branch_merged passes head= to a Gh.pr_list that has no such parameter, so no wrap-up has ever deleted its branch — and the fakes' **kwargs hides it nf-qe1 ← Beads

open priority 2 task unassigned

Twinned from https://github.com/cfsh/ninefold/issues/736 by tools/beads/import_github.py's reconcile pass.

Filed unclaimed from nf-dev-session-state, hit live while wrapping up (PR #715, e6471ee).

What happens

./tools/pr/wrap_up.py --remove removes both worktrees, then always ends with:

``` ⚠ kept local branch feat/session-state — could not ask GitHub whether a PR merged it (TypeError: Gh.pr_list() got an unexpected keyword argument 'head') ```

and exits 1, on a wrap-up where every check passed.

Cause: branch_merged calls a parameter that does not exist

nflib/sessions.py:1552:

```python carried = gh.pr_list(["number", "state"], state="all", head=branch, limit=100) ```

nflib/gh.py:171:

```python def pr_list(self, fields, *, state="open", base=None, author=None, limit=100) ```

There is no head. Verified against the real class rather than read off the page:

``` $ python3 -c "...; print(inspect.signature(Gh.pr_list))" (self, fields, *, state='open', base=None, author=None, limit=100) ```

So branch_merged raises TypeError on every invocation. tidy_branch catches it and degrades correctly — the branch is kept rather than force-deleted, which is the safe direction and why this has gone unnoticed — but the consequence is that no session's wrap-up has ever deleted its local branch. They accumulate in the clone, which is #602's shape in the ref namespace instead of the filesystem.

⚠️ It also makes wrap_up.py --remove exit 1 on a completely clean wrap-up, so the one signal a CTO or a script would read as "something went wrong" is permanently on.

⚠️ The test for it cannot fail

test_branch_merged_asks_github_for_that_branch_only passes, and so does every other test touching this path, because every fake overrides with a permissive signature:

```python def pr_list(self, *args: Any, **kwargs: Any): # test_nflib_sessions.py:1276, :1293 def pr_list(self, fields, **kwargs): # test_dispatch.py, five sites ```

**kwargs swallows head= silently. The suite is green and production has never worked — #450's shape, in the guard written for it.

This is the fifth instance of that class I have hit today; four were mine on #715 (C1, C6, C9 and a wake-message test), all caught by Tier-0 review. This one is in shipped nflib. The through-line each time: *the test asserted the shape I had just written, not the thing that would change if it went wrong.*

Shape of a fix — not prescribing, and there is a real choice here
  1. Add head to Gh.pr_list. gh pr list --head <branch> is real, so this is one line plus a test. Simplest, and probably right.
  2. Drop the parameter and filter client-side on headRefName, which sessions.py:1091 already does for open PRs. Costs a wider fetch; no new API surface.

⚠️ Whichever is taken, the fake is the actual fix. A permissive **kwargs fake makes every future signature drift in this class invisible, and Gh is the seam nearly every tool in the tier goes through. Consider having nflib.fakes.FakeGh mirror the real signatures — or asserting inspect.signature compatibility once, in one test, so drift fails somewhere rather than nowhere.

Repro

```bash python3 -c "import sys; sys.path.insert(0,'tools') from nflib.gh import Gh; from nflib import sessions sessions.branch_merged(Gh('cfsh/ninefold'), 'feat/anything')" # TypeError: pr_list() got an unexpected keyword argument 'head' ```

⚠️ tools/nflib/** is governance, so whoever takes this needs @aedanpope's approval — not a Tier-1 auto-merge.

Unclaimed and unlabelled. I am wrapped and not taking it.

Dependencies

None.

Comments

No comments.

Add a comment