Skip to content

fix(vcs): treat bare repositories as valid worktree sources - #307

Merged
patroza merged 6 commits into
fork/changesfrom
t3code/debug-t3vm-vcs-errors
Aug 4, 2026
Merged

fix(vcs): treat bare repositories as valid worktree sources#307
patroza merged 6 commits into
fork/changesfrom
t3code/debug-t3vm-vcs-errors

Conversation

@patroza

@patroza patroza commented Aug 4, 2026

Copy link
Copy Markdown
Owner

Problem

Every T3 turn against /var/lib/t3/src/t3code on t3vm failed with:

Could not start T3 turn: dispatch failed: Git command failed in
GitWorkflowService.fetchRemote (/var/lib/t3/src/t3code):
Failed to resolve the VCS driver for this Git command.

The repository had core.bare = true. GitVcsDriver.detectRepository gated on
rev-parse --is-inside-work-tree, which conflates "is a Git repository" with
"has a checkout". A bare repository therefore detected as no repository at all, so
VcsDriverRegistry.resolve raised VcsUnsupportedOperationError and every Git route
failed — including the two a thread actually needs, fetchRemote and createWorktree.

Nothing was logged server-side: the error reached Discord but produced zero
t3code-server journal lines, so the host looked healthy.

Why bare should work

Starting a thread never needs the source repository to have a checkout — the thread gets
its own worktree. git worktree add, git fetch, and ref plumbing all work fine
against a bare repository. Only operations that touch a checkout need one.

The lower layer already agreed: resolveRepositoryPaths tolerates a failing
--show-toplevel and models worktreeRoot: null. Only the detection gate rejected bare.

Change

  • Detect bare repositories instead of rejecting them; carry bare on
    VcsRepositoryIdentity. rootPath is the metadata directory when bare.
  • One probe. Detection now runs a single combined
    rev-parse --is-bare-repository --is-inside-work-tree, which separates no repository /
    bare / checkout in one call. Negative detection stays at one git invocation, so
    there is no added load on the status-poll path.
  • Bare-safe routes opt in with allowBare: true: createWorktree, fetchRemote,
    resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when it
    is not also switching.
  • Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
    thread preparation) keep the default and now fail with the real reason — the repository
    has no working tree — instead of a generic routing error.
  • Status polling reports a bare repository as having no workspace rather than erroring
    on every poll.

Tests

  • GitVcsDriver.test.ts — bare repo detects as bare: true; a repo whose config marks an
    existing checkout bare (the exact shape that broke t3vm) detects instead of returning
    null; non-repositories still return null; and a usable worktree is created from a bare
    repository
    , with the resulting checkout detecting as bare: false.
  • GitWorkflowService.test.tscreateWorktree and fetchRemote run against a bare
    handle; switchRef is refused before reaching the driver with an actionable message;
    localStatus degrades to "no workspace".
  • VcsDriverRegistry.test.ts — updated to the new probe, asserting one probe per detect.
  • Contract harness asserts bare: false for ordinary repositories.

Note

The immediate outage was already mitigated on t3vm by git config core.bare false — that
repository has a full checkout and 15 linked worktrees, so its config flag was simply wrong.
This change is about the routing behavior: a repository without a checkout should not take
down thread creation, and when a checkout genuinely is required the error should say so.

🤖 Generated with Claude Code


Also in this PR: unblocking a red fork/changes

fork/changes was red when this branch was cut (Fork CI failing on Check and Test),
so the ship gate could not pass on the VCS fix alone. These commits fix that breakage. They
are unrelated to the VCS change and are separated per commit for review:

  • style: format files drifted on fork/changes — whitespace only, in three files this
    branch does not otherwise touch (ThreadComposer.tsx, ProviderCommandReactor.ts,
    serverRuntimeStartup.ts). This is what CI's Check job was failing on.
  • fix(desktop): add the missing removeCommandLineSwitch test stubElectronApp
    gained removeCommandLineSwitch, but the DesktopUpdates test mock was never updated, so
    @t3tools/desktop failed typecheck.
  • refactor(server): use Effect FileSystem and Schema for the runtime descriptor
    serverRuntimeStartup.ts wrote and cleaned up the runtime descriptor with
    node:fs/promises, node:path, and hand-rolled JSON, which the Effect diagnostics reject
    (nodeBuiltinImport, preferSchemaOverJson). It now uses Effect FileSystem/Path and
    the existing ServerRuntimeDescriptor schema from @t3tools/shared/serverRuntime
    the same schema the desktop client already decodes the file with, so the format now comes
    from one definition instead of two hand-written shapes. mode: 0o600 and the best-effort
    cleanup semantics are preserved, and a test pins the on-disk format against the consumer's
    decode path.

Known flake (not fixed here)

apps/desktop/src/app/DesktopPreReadyPlatform.test.ts > acquires a synchronous pre-ready layer before an asynchronous Clerk-shaped layer fails intermittently. It failed in one local
full-suite run, then passed on re-run, in isolation (3×), and in the full desktop suite
(456 tests) — and CI hit the same test on fork/changes this morning. It is pre-existing and
timing-sensitive; left alone rather than papered over.

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.
Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.
`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.
…scriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.
@patroza
patroza marked this pull request as ready for review August 4, 2026 11:02
@patroza
patroza merged commit 1ccb31b into fork/changes Aug 4, 2026
11 checks passed
patroza added a commit that referenced this pull request Aug 4, 2026
* fix(vcs): treat bare repositories as valid worktree sources

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.

* style: format files drifted on fork/changes

Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.

* fix(desktop): add the missing removeCommandLineSwitch test stub

`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.

* fix(vcs): supply the required worktree path in the bare-repo test input

* refactor(server): use Effect FileSystem and Schema for the runtime descriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.

* test(server): decode the descriptor via the schema JSON codec

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
patroza added a commit that referenced this pull request Aug 4, 2026
* fix(vcs): treat bare repositories as valid worktree sources

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.

* style: format files drifted on fork/changes

Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.

* fix(desktop): add the missing removeCommandLineSwitch test stub

`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.

* fix(vcs): supply the required worktree path in the bare-repo test input

* refactor(server): use Effect FileSystem and Schema for the runtime descriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.

* test(server): decode the descriptor via the schema JSON codec

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
patroza added a commit that referenced this pull request Aug 4, 2026
* fix(vcs): treat bare repositories as valid worktree sources

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.

* style: format files drifted on fork/changes

Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.

* fix(desktop): add the missing removeCommandLineSwitch test stub

`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.

* fix(vcs): supply the required worktree path in the bare-repo test input

* refactor(server): use Effect FileSystem and Schema for the runtime descriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.

* test(server): decode the descriptor via the schema JSON codec

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
patroza added a commit that referenced this pull request Aug 5, 2026
* fix(vcs): treat bare repositories as valid worktree sources

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.

* style: format files drifted on fork/changes

Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.

* fix(desktop): add the missing removeCommandLineSwitch test stub

`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.

* fix(vcs): supply the required worktree path in the bare-repo test input

* refactor(server): use Effect FileSystem and Schema for the runtime descriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.

* test(server): decode the descriptor via the schema JSON codec

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
patroza added a commit that referenced this pull request Aug 5, 2026
* fix(vcs): treat bare repositories as valid worktree sources

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.

* style: format files drifted on fork/changes

Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.

* fix(desktop): add the missing removeCommandLineSwitch test stub

`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.

* fix(vcs): supply the required worktree path in the bare-repo test input

* refactor(server): use Effect FileSystem and Schema for the runtime descriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.

* test(server): decode the descriptor via the schema JSON codec

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
patroza added a commit that referenced this pull request Aug 5, 2026
* fix(vcs): treat bare repositories as valid worktree sources

A repository with `core.bare=true` was reported as "not a repository" at
detection, because `detectRepository` gated on `rev-parse --is-inside-work-tree`
— which conflates "is a Git repository" with "has a checkout". Every Git route
then failed with "Failed to resolve the VCS driver for this Git command",
including `fetchRemote` and `createWorktree`, so no thread could start against
that repository.

Starting a thread does not need the source repository to have a checkout: the
thread gets its own worktree, and `git worktree add`, `git fetch`, and ref
plumbing all work against a bare repository. Only operations that touch a
checkout need one.

- Detect bare repositories instead of rejecting them, and carry `bare` on the
  repository identity. Detection now uses one combined
  `rev-parse --is-bare-repository --is-inside-work-tree`, which separates
  "no repository" / "bare" / "checkout" in a single call, so negative detection
  stays at one git invocation.
- Let the bare-safe routes through: createWorktree, fetchRemote,
  resolveRemoteTrackingCommit, removeWorktree, renameBranch, and createRef when
  it is not also switching.
- Checkout-dependent routes (switchRef, pullCurrentBranch, stacked actions, PR
  thread preparation) now fail with the actual reason — that the repository has
  no working tree — instead of a routing error.
- Status polling reports a bare repository as having no workspace rather than
  erroring on every poll.

* style: format files drifted on fork/changes

Whitespace only, no behavior change. These three files were already
unformatted on fork/changes and are untouched by this branch's fix, but the
agent ship gate runs a whole-repo `vp check`, so publishing is blocked until
they are formatted.

* fix(desktop): add the missing removeCommandLineSwitch test stub

`ElectronApp` gained `removeCommandLineSwitch`, but the DesktopUpdates test
mock was not updated, so `@t3tools/desktop` failed typecheck on fork/changes
before this branch. Untouched by this branch's fix, but the agent ship gate
runs the workspace typecheck, so publishing is blocked until it compiles.

* fix(vcs): supply the required worktree path in the bare-repo test input

* refactor(server): use Effect FileSystem and Schema for the runtime descriptor

serverRuntimeStartup wrote and cleaned up the runtime descriptor with
`node:fs/promises`, `node:path`, and hand-rolled `JSON.stringify`/`JSON.parse`,
which the Effect diagnostics flag (nodeBuiltinImport, preferSchemaOverJson) and
which failed the workspace typecheck.

- Write and remove the descriptor through Effect `FileSystem` (`mode: 0o600` is
  preserved) and join its path with Effect `Path`.
- Encode and decode with the existing `ServerRuntimeDescriptor` schema from
  `@t3tools/shared/serverRuntime` rather than untyped JSON. That schema was
  already the one the desktop client decodes with, so the file now round-trips
  through a single definition instead of two hand-written shapes.
- Keep prior failure semantics: a descriptor that cannot be written is a defect
  (as it was under `Effect.promise`), and cleanup stays best-effort so a
  missing, unreadable, or malformed file cannot fail shutdown.
- Pin the on-disk format with a test that decodes the written contents the same
  way `DesktopExistingBackend` does, since that shape is a cross-process
  contract.

* test(server): decode the descriptor via the schema JSON codec

---------

Co-authored-by: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com>
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.

1 participant