refactor(server): share the no-git-repository checkout-root rewrap (#tech-debt) - #1085
Merged
Merged
Conversation
…tech-debt) worktree.ts and pr.ts each declared a file-private resolveRepoRoot that wrapped resolveCheckoutRoot and rethrew the same 404 message, differing only in the domain error class. Replace both (and worktree.ts's pass-through resolveCurrentCheckoutRoot alias) with an exported resolveCheckoutRootOrThrow(cwd, runner, ErrorClass) in git-context.ts, and cover the no-repo path for all four entry points. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
selfcontained
deleted the
agt_97def1efa46c/job-debt-collector-f0a8739c
branch
September 13, 2026 09:11
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.
What
apps/server/src/shared/git/worktree.tsandapps/server/src/shared/github/pr.tseach declared a file-privateresolveRepoRoot(cwd, commandRunner). The two were identical 9-line wrappers:try { return await resolveCheckoutRoot(cwd, commandRunner) } catch { throw new <DomainError>("No git repository found for the provided working directory.", 404) }.This PR adds one exported helper next to
resolveCheckoutRootinshared/git/git-context.ts:It deletes both wrappers, plus
resolveCurrentCheckoutRootin worktree.ts. That function did nothing but call worktree.ts'sresolveRepoRoot, so it only existed to forward to the deleted wrapper. All four call sites now call the helper directly:createGitWorktreeGitWorktreeErrorcleanupGitWorktreeGitWorktreeErrorcreatePrGitHubPrErrorgetPrStatusGitHubPrErrorWhat the included sites have in common: each one runs
git -C <cwd> rev-parse --show-toplevelthrough the injected runner. Any failure is swallowed and replaced with a new domain error that has status 404 and the message above. The sites differ only in the error class. Both classes have the same(message: string, statusCode = 400)constructor, andshared/mcp/tool-error.tsandagents/manager.tsbranch on the class withinstanceof, so each call site passes its own class.Why it's tech debt
resolveRepoRootthat returned the checkout root. That name collides with the exportedgit-context.tsresolveRepoRoot, which returns the common repo root (via--git-common-dir, with a timeout and fallback). worktree.ts then aliased its copy under the accurate nameresolveCurrentCheckoutRoot.resolveRepoRoot.Deliberately left alone
git-context.tsresolveRepoRoot(exported, about 8 importers): it is a different function, and renaming it has a much wider blast radius.worktree.tsresolveCommonRepoRoot: it looks likegit-context.tsresolveRepoRoot, but it has no timeout and no fallback, and it throwsGitWorktreeError(…, 500). Folding it in would change behavior.routes/personas.tsresolveOptionalRepoRoot/resolveOptionalWorktreeRoot: these swallow the failure tonullinstead of rethrowing, so they have a different result type.Shape chosen
The helper takes the error constructor, so call sites stay one-liners and the 404 stays next to the message it belongs with. The alternative was an error factory (
(message) => new GitHubPrError(message, 404)), which moves the status code to each caller at the cost of a lambda per site. I also considered keeping a thin file-private wrapper per file and rejected it: that would keep 2 wrappers for what is now a 4-line call.Validation
pnpm run checkpasses.test/git-worktree.test.ts(create and cleanup) andtest/github-pr.test.ts(createPr and getPrStatus). Previously none of the four sites had an assertion on this path. Each test checks the error class, message and 404.srcfiles (HEAD's git-context/worktree/pr).Errorinstead ofErrorClass, and swapping the class toErrorat each of the 4 call sites one at a time.pnpm run test(server with Postgres env, web, browser-extension) andpnpm run test:e2e: all green — server 187 files passed / 1 skipped (3321 tests), web 130 files / 1924 tests, browser-extension 9 files / 60 tests; E2E 198 passed / 12 skipped (1.8m).Next run
RunJobResultduplication between server and web:apps/server/src/jobs/service.ts:57vsapps/web/src/hooks/use-jobs.ts:22.🤖 Generated with Claude Code