Skip to content

Cover the inbound symptom of a restricted fetch refspec in Edge cases - #177

Open
dmccoystephenson wants to merge 2 commits into
mainfrom
fix/restricted-refspec-checkout
Open

dmccoystephenson wants to merge 2 commits into
mainfrom
fix/restricted-refspec-checkout

Conversation

@dmccoystephenson

Copy link
Copy Markdown
Member

Summary

The restricted-fetch-refspec entry in the generated-skill template's Edge cases section is extended to cover the inbound symptom reported in #130. The entry is retitled so that it can be found from either error message.

  • Outbound (unchanged): a branch that has just been pushed fails gh pr create auto-detection, and the fix is --head <branch>.
  • Inbound (new): git fetch origin <branch> writes only FETCH_HEAD, so git checkout <branch> fails with error: pathspec '<branch>' did not match any file(s) known to git for a branch that does exist on the remote. git fetch origin +<branch>:<branch> is prescribed from the default branch.
  • Beyond the issue's suggestion: a second hazard was observed in this clone. Local branches from earlier runs are kept (ten were present at triage), so a plain git checkout <branch> can succeed silently onto a stale local copy. Two checks were added: git rev-parse <branch> is compared with the PR's headRefOid, and git log FETCH_HEAD..<branch> must be empty before the forced update, so that unpushed work is not discarded. The + was added to the issue's suggested <branch>:<branch> form for this reason.
  • The entry is kept as one line, matching its siblings. A dash list would have caused the following {{#if REVIEWER}} entry to be read as a lazy continuation of the last list item.

Closes #130

gardener interface: Steps 3, 5, and 6 are untouched, so no breaking change is introduced for gardener.

Retrofit: the Edge cases section is copied into every generated skill, so a retrofit pass is likely needed for every existing <slug>-dev-loop skill that carries the old gh pr create-only entry. create-dev-loop-dev-loop is one of them.

Research grounding

No RESEARCH.md finding applies. This is plain Git refspec behavior, as stated in #130, so no Implementations entry is added.

Doc sync check

  • README.md's "What it does" Step list still matches create-dev-loop.md's Steps 1:1 (no Step was changed)
  • No {{placeholder}} was added. The existing {{DEFAULT_BRANCH}} is not used in the new text.

Test plan

  • python3 scripts/check_docs.py: "Doc consistency check passed."
  • python3 -m unittest discover -s tests: 42 tests were run, OK.
  • The inbound symptom was reproduced in this dispatch's clone (refspec +refs/heads/main:refs/remotes/origin/main, git 2.25.1). git fetch origin fix/negated-closing-keywords wrote only FETCH_HEAD, and the checkout failed with the quoted pathspec error.
  • The stale-branch case was observed: while a local fix/negated-closing-keywords was present, git checkout succeeded without any tracking message. git fetch origin +fix/negated-closing-keywords:fix/negated-closing-keywords was then run against a local branch reset to 855a4b7, and it reported (forced update) to ec84115, the remote tip. The scratch branch was deleted afterward.
  • UNVERIFIED (behavioral): no /create-dev-loop run was performed, because a GitHub repo is created in Step 6 and files outside the checkout are written in Steps 3 and 5, and neither is authorized in a headless dispatch ([self-audit] The repo's own validation anchor is structurally unrunnable in a headless dispatch, so template changes can never merge autonomously #109). None of the five CLAUDE.md checklist items was exercised, so this PR is left for a maintainer to merge.

Issues not picked this cycle

This PR description was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).

🤖 Generated with Claude Code


drafted by Claude on behalf of Daniel Stephenson

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dmccoystephenson
dmccoystephenson requested a balanced review from Copilot November 11, 2026 02:18
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Self-review rubric (an inline review was performed in a headless dispatch, with no independent reviewer):

  • Scope: PASS. One line of create-dev-loop.md (the restricted-refspec Edge case) was changed, and nothing else.
  • Tests-new: PASS (not applicable). No function was added.
  • Tests-fix: PASS (not applicable as a code regression test). The issue describes missing guidance, not a code defect. Its symptom was reproduced in this clone, and the prescribed +<branch>:<branch> form was run and observed to produce (forced update) to the remote tip.
  • Sibling structure: PASS. The entry is kept as one bold-titled line, like its siblings. A dash list was tried and removed, because the next {{#if REVIEWER}} line would have been absorbed into it as a lazy continuation.
  • Sibling renames: PASS. The entry title was changed, and a repo-wide search for must first push and pathspec found no other references.
  • Docs: PASS. The README, CONTRIBUTING, SECURITY, and template docs do not describe Edge-case entries individually.
  • Issue resolution: PASS. Both changes suggested in A restricted fetch refspec also blocks checking out an existing remote branch, with an error that reads as 'branch does not exist' #130 are present: the inbound symptom is added to the existing entry, and the entry is retitled so it can be found from either error message.
  • CI: PASS. doc-consistency passed on the PR head.
  • Placeholder parity / README Step parity / fence escaping / phase numbering / version comments: PASS. No placeholders, Steps, fences, phases, or header lines were touched (check_docs passed).
  • Research grounding: PASS. The PR body states that no finding applies.
  • No back-ported specifics: PASS. The text is generic to restricted clones. "gardener-managed" was already in the entry.
  • gardener interface: PASS. Steps 3, 5, and 6 are untouched, and this is stated in the PR body.
  • Retrofit note: PASS. The PR body names the existing skills that likely need a retrofit pass.
  • Behavioral validation: FAIL (UNVERIFIED). No /create-dev-loop run could be performed headlessly ([self-audit] The repo's own validation anchor is structurally unrunnable in a headless dispatch, so template changes can never merge autonomously #109).

Judgment calls flagged for a reviewer:

Because of the UNVERIFIED behavioral item, this PR is not merged autonomously and is left for a maintainer.

This review was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

Copilot AI 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.

🟡 Changes recommended

The unconditional git log check fails when the local branch is absent, blocking the intended recovery; it must run only for existing branches and succeed with no commits before allowing a forced update.

1 open issue
What changed in this PR

Extends the generated skill’s restricted-fetch-refspec guidance to cover inbound branch checkout failures, alongside the existing PR-creation workaround.

Changes:

  • Retitles the Edge cases entry to make both error messages discoverable.
  • Adds explicit fetch instructions and checks for stale local branches and unpushed commits.
File Description
create-dev-loop.md Expands restricted-refspec troubleshooting in the generated skill template.

Review effort: Balanced

Comment thread create-dev-loop.md Outdated
**A review comment is a false positive:** reply with evidence, do not apply the change.
**A `gh` command fails with a transient network/transport error (`http2: client conn could not be established`, `unexpected EOF`):** retry once or twice before treating it as blocked — do not treat the first failure as terminal and silently drop a self-review, commit, or comment.
**`gh pr create` fails with "you must first push the current branch to a remote" despite a successful `git push -u`:** the clone's fetch refspec may be restricted to the default branch only (common in gardener-managed or otherwise restricted dedicated checkouts) — confirm with `git config --get-all remote.origin.fetch`. A restricted refspec means `git fetch` never populates a remote-tracking ref for feature branches, so `gh pr create`'s and `git branch --set-upstream-to`'s auto-detection both fail even though the push and upstream config succeeded. Pass `--head <branch>` explicitly to `gh pr create` to bypass the remote-tracking-ref lookup rather than retrying the push.
**Restricted fetch refspec — `gh pr create` fails with "you must first push the current branch to a remote" despite a successful `git push -u`, or `git checkout <branch>` fails with `error: pathspec '<branch>' did not match any file(s) known to git` for a branch that exists on the remote:** the clone's fetch refspec may be restricted to the default branch only (common in gardener-managed or otherwise restricted dedicated checkouts) — confirm with `git config --get-all remote.origin.fetch`. A restricted refspec means `git fetch` never populates a remote-tracking ref for feature branches, and it has two symptoms. *Outbound (a branch this clone just pushed):* `gh pr create`'s and `git branch --set-upstream-to`'s auto-detection both fail even though the push and upstream config succeeded. Pass `--head <branch>` explicitly to `gh pr create` to bypass the remote-tracking-ref lookup rather than retrying the push. *Inbound (an existing remote branch, e.g. a carried-over PR's branch in Phase 1):* `git fetch origin <branch>` succeeds but writes only `FETCH_HEAD`, so the checkout error reads as "the branch does not exist" — it does exist; only the mapping into a local ref is missing. Do not re-fetch the same way or start fresh work. From the default branch, run `git fetch origin +<branch>:<branch>`, which creates the local branch directly, then check it out. The `+` matters because such clones are often reused across runs and can keep a **stale local branch of the same name** — in that case a plain `git checkout <branch>` succeeds silently onto the old commit, so compare `git rev-parse <branch>` against `gh pr view <number> --json headRefOid` before trusting it. The forced update discards local-only commits on that branch, so first run `git fetch origin <branch>` and confirm `git log FETCH_HEAD..<branch>` is empty (no unpushed work).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@dmccoystephenson

Copy link
Copy Markdown
Member Author

Copilot's finding ("Skip local-commit check when the branch is absent") was confirmed and addressed in 98bfb83.

  • The failure was reproduced: with no local branch, git log FETCH_HEAD..<branch> exits with fatal: ambiguous argument ... unknown revision.
  • The unpushed-work check is now prescribed only when git rev-parse --verify --quiet <branch> prints a SHA. That probe was observed to print nothing for an absent branch and a SHA for main. When no local branch exists, the forced fetch is run directly.
  • python3 scripts/check_docs.py passed, and the unit tests passed (OK).

The behavioral /create-dev-loop validation remains UNVERIFIED (#109), so the PR is still left for a maintainer to merge.

This comment was drafted during a Gardener session (https://github.com/Stephenson-Software/gardener).


drafted by Claude on behalf of Daniel Stephenson

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.

A restricted fetch refspec also blocks checking out an existing remote branch, with an error that reads as 'branch does not exist'

2 participants