Skip to content

fix(github): repo-doc PR refresh permanently breaks after the loopover/repo-docs branch is closed without deleting it #8310

Description

@JSONbored

Context

openRepoDocPullRequest (src/github/repo-doc-pr.ts) is the single code path that opens/refreshes the automated AGENTS.md/CLAUDE.md (and optional skill-file) pull request for a repo — called from both the scheduled sweep (src/queue/processors.ts) and the on-demand MCP tool loopover_refresh_repo_docs (src/mcp/server.ts, backed by src/github/repo-doc-refresh-runner.ts's performRepoDocRefresh).

Every run always targets the same fixed branch name, loopover/repo-docs (REPO_DOC_BRANCH_NAME, src/github/repo-doc-pr.ts:42) — this is deliberate, per the file's own header comment: "Stable across runs (not per-run unique) so a repeat invocation targets the SAME branch/PR instead of piling up duplicates."

The function only checks for an open PR on that branch before deciding whether to create a new one (src/github/repo-doc-pr.ts:167, state: "open"). If none is found, it always creates a brand-new ref via:

// src/github/repo-doc-pr.ts:227
await octokit.request("POST /repos/{owner}/{repo}/git/refs", { owner, repo, ref: `refs/heads/${REPO_DOC_BRANCH_NAME}`, sha: commitSha });

POST /git/refs fails with a 422 ("Reference already exists") if the ref is already present. This happens whenever a maintainer closes the repo-doc PR without deleting the branch (GitHub's UI offers a "Delete branch" button on a closed PR, but nothing forces it — and a PR closed via the API/a bot doesn't delete the branch at all). At that point:

  • The state: "open" PR lookup at line 167 finds nothing (the PR is closed).
  • The function proceeds through the diff-aware refresh logic and decides a new commit is needed.
  • The POST /git/refs call at line 227 throws (branch already exists).
  • The whole function is wrapped in one outer try { ... } catch (error) { return { opened: false, reason: error.message } } (src/github/repo-doc-pr.ts:141-244), so the failure is swallowed into a generic { opened: false, reason } result — no exception surfaces, no distinct signal from "generation legitimately skipped" (e.g. .loopover.yml disabled it).
  • performRepoDocRefresh (src/github/repo-doc-refresh-runner.ts:54-62) records the attempt regardless of outcome, so the scheduled sweep won't retry sooner — but every future attempt (scheduled or manual) hits the exact same 422 and fails the exact same way, permanently, until an operator manually deletes the orphaned branch out-of-band.

Nothing in this codepath ever checks whether the branch already exists before creating it, and nothing ever deletes/updates it — confirmed via grep -rn "loopover/repo-docs\|REPO_DOC_BRANCH_NAME" src/ returning only the three lines above (no delete, no PATCH-ref call, no existence probe).

Requirements

  • openRepoDocPullRequest must tolerate the target branch (refs/heads/loopover/repo-docs) already existing with no open PR on it (i.e., a previous PR was closed without deleting the branch), and still successfully commit the refreshed content and open a fresh PR.
  • The fix must use GitHub's ref-update primitive (PATCH /repos/{owner}/{repo}/git/refs/{ref} with sha + force: true) to update the existing ref in place when the create call reports the ref already exists, rather than failing the whole operation. Since this branch is exclusively owned by this feature (never shared with contributor work — see the file's own header comment), force-updating it to the freshly built commit is safe.
  • Do not change the existing "only reuse an open PR" behavior (src/github/repo-doc-pr.ts:167) — a closed PR on this branch must still result in a new PR being opened (not silently updating a closed one), only the underlying branch ref must be reusable.
  • Preserve the existing fail-safe contract: any other GitHub API failure during this recovery path must still degrade to { opened: false, reason }, never throw out of openRepoDocPullRequest.

Deliverables

  • src/github/repo-doc-pr.ts: when the POST /git/refs create call fails because the ref already exists, fall back to PATCH /repos/{owner}/{repo}/git/refs/heads/{REPO_DOC_BRANCH_NAME} with { sha: commitSha, force: true } to update it in place, then proceed to open the PR exactly as the create-succeeded path already does.
  • A regression test in test/unit/repo-doc-pr.test.ts that mocks POST /git/refs returning a 422 "Reference already exists" response and asserts openRepoDocPullRequest still returns { opened: true, reused: false, ... } with a real PR opened against the force-updated branch (mirroring the existing test file's mocking style for the other GitHub calls in this path).

Test Coverage Requirements

99%+ Codecov patch coverage, branch-counted, on the changed lines in src/github/repo-doc-pr.ts — both the "create succeeds" (existing, already-covered) branch and the new "create fails with ref-exists, fall back to PATCH" branch must be exercised. Also add a case where the create call fails for a genuinely different reason (e.g. a 500) to confirm the existing fail-safe { opened: false, reason } behavior is untouched for non-"already exists" failures.

Expected Outcome

A repo whose loopover/repo-docs PR was closed without deleting the branch resumes getting successful AGENTS.md/CLAUDE.md refreshes on the next scheduled sweep or manual loopover_refresh_repo_docs call, instead of being permanently stuck failing the same 422 forever.

Links & Resources

Metadata

Metadata

Assignees

No one assigned

    Labels

    gittensor:bugGittensor-scored bug fix — scores a 0.05x multiplier.help wantedExtra attention is needed

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions