fix(host-service): bypass user git hooks for scaffolding initial commits

Merged
KI
3mo ago
+110-3

Problem

Creating a session (workspaces.createSession → initEmptyRepo) initializes a fresh repo under the sessions root and makes a scaffolding "Initial commit". If the user has global git hooks configured (e.g. a core.hooksPath commit-msg hook enforcing a commit-message policy), the hook runs against our scaffolding commit and rejects it — session creation deterministically fails as INTERNAL_SERVER_ERROR for any user with such a hook (2 events on 1.20.0).

Root cause

The scaffolding "Initial commit" invocations in resolve-repo.ts (initEmptyRepo, initLocalRepoInPlace, cloneTemplateInto) run plain git commit, so the user's machine-wide hooks apply to commits we author in repos we just created. asInitialCommitTrpcError types git-user-not-configured but a hook rejection falls through to a 500.

Fix

Run all three scaffolding commits with hooks bypassed: -c core.hooksPath= commit --no-verify. Both parts are needed — --no-verify skips pre-commit/commit-msg, and the empty core.hooksPath override covers hooks --no-verify doesn't (verified empirically: a failing prepare-commit-msg still aborts a --no-verify commit). createUserSimpleGit already enables allowUnsafeHooksPath, so simple-git permits the override.

Decision on the cloneTemplateInto snapshot path (commits the user's template files into a repo we just initialized): bypass there too. The user does not author that commit invocation either, and a rejecting global hook fails template creation identically — same failure mode, same fix.

asInitialCommitTrpcError classification is kept unchanged for what can still fail (git identity not configured, disk errors). No hook-rejection classification was added since the bypass covers every scaffolding path — there is no remaining path where a hook can reject these commits.

Precedent: #6168 (hook tolerance in workspace setup).

Also fixed a latent test-fixture hazard found along the way: simple-git only fails a raw task on non-zero exit when stderr is non-empty, so the regression tests' rejecting hooks print to stderr like real policy hooks do (a silent exit 1 hook is swallowed by simple-git — noted in the test comment).

Verification

bunx biome check on both changed files:

Checked 2 files in 14ms. No fixes applied.

cd packages/host-service && bun run typecheck:

$ tsc --noEmit --emitDeclarationOnly false
(clean exit)

bun test packages/host-service/src/trpc/router/project/utils/resolve-repo.test.ts:

 34 pass
 0 fail
 65 expect() calls
Ran 34 tests across 1 file. [7.00s]

Mutation check — with the bypass reverted to plain git commit, the three new regression tests fail; restored, all 34 pass:

(fail) scaffolding commits bypass user git hooks > initLocalRepoInPlace commits despite rejecting global hooks
(fail) scaffolding commits bypass user git hooks > initEmptyRepo commits despite rejecting global hooks
(fail) scaffolding commits bypass user git hooks > cloneTemplateInto snapshot commit despite rejecting global hooks
 31 pass / 3 fail

Refs HOST-SERVICE-13

https://claude.ai/code/session_1a228bdc-29c4-4d4e-8e63-7bb4acf54aaf


Summary by cubic

Bypass user git hooks for scaffolding “Initial commit” during session repo setup to prevent 500s when global hooks reject commits. Aligns with HOST-SERVICE-13 by ensuring session creation doesn’t depend on user git hook policies.

  • Bug Fixes
    • Run scaffolding commits with -c core.hooksPath= and --no-verify in initEmptyRepo, initLocalRepoInPlace, and cloneTemplateInto.
    • Keep existing error mapping; hooks can no longer block these commits.
    • Add regression tests that simulate rejecting global hooks and ensure simple-git surfaces failures via stderr.

Summary by CodeRabbit

  • Bug Fixes
    • Repository scaffolding now succeeds even when global Git hooks would otherwise reject initial commits.
    • Standard user commits continue to respect and enforce configured Git hooks.
    • Applied consistent hook handling when creating repositories in place, initializing empty repositories, or cloning templates.