# Files changed · PR \#23876 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/23876/diffs)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

[feat: add a setting to turn off Git integration](https://git.cafe/pingdotgg/t3code/pull/23876.md)

Version: 5

Head commit: f0bfb38a27052c0b85a4eb605ea88018647e8e78

Base commit: 2c8bdf6177159d8fcd12a897d7c9311f6eb93237

Comparison base: 2c8bdf6177159d8fcd12a897d7c9311f6eb93237

Showing up to 20 entries from this revision. If the pull request changes, reopen the first page before continuing.

## apps/mobile/src/features/settings/SettingsServerControlsRouteScreen.tsx

Status: modified; +10; −0

```diff
@@ -356,6 +356,16 @@
 
               {props.page === "source-control" ? (
                 <>
+                  <SettingsSection title="Git">
+                    <SettingsSwitchRow
+                      icon="arrow.triangle.branch"
+                      label="Git integration"
+                      subtitle="When off, T3 Code treats every folder as having no Git repository: no checkpoints, status checks, diffs, or branch and pull request controls."
+                      value={uniform("sourceControlEnabled")}
+                      disabled={disabledFor("sourceControlEnabled")}
+                      onValueChange={(value) => write({ sourceControlEnabled: value })}
+                    />
+                  </SettingsSection>
                   <BranchNamingSettings
                     key={targetsKey}
                     mode={uniform("branchNamingMode")}
```

## apps/server/scripts/evaluate-thread-titles.ts

Status: modified; +1; −1

```diff
@@ -143,8 +143,8 @@
         ProcessRunner.layer,
         SourceControlProviderRegistry.layer.pipe(
           Layer.provide(SourceControlBuiltInDrivers.layer),
-          Layer.provide(ServerSettings.layerTest()),
           Layer.provide(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProjectConfig.layer))),
+          Layer.provide(ServerSettings.layerTest()),
           Layer.provide(GitVcsDriver.layer),
           Layer.provide(VcsProcess.layer),
           Layer.provide(FetchHttpClient.layer),
```

## apps/server/src/checkpointing/CheckpointDiffQuery.test.ts

Status: modified; +2; −0

```diff
@@ -11,6 +11,7 @@
 import { OrchestratorProjectionError } from "../orchestration-v2/Orchestrator.ts";
 import type { ProjectionCheckpointContext } from "../orchestration-v2/ProjectionStore.ts";
 import * as ThreadManagement from "../orchestration-v2/ThreadManagementService.ts";
+import * as ServerSettings from "../serverSettings.ts";
 import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
 import * as VcsProcess from "../vcs/VcsProcess.ts";
 import * as CheckpointDiffQuery from "./CheckpointDiffQuery.ts";
@@ -193,6 +194,7 @@
 const layerVcsProcess = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer));
 const layerStore = CheckpointStore.layer.pipe(
   Layer.provide(VcsDriverRegistry.layer.pipe(Layer.provide(layerVcsProcess))),
+  Layer.provide(ServerSettings.layerTest()),
   Layer.provide(NodeServices.layer),
 );
 const layerTest = Layer.merge(layerStore, layerVcsProcess).pipe(
```

## apps/server/src/checkpointing/CheckpointStore.test.ts

Status: modified; +34; −1

```diff
@@ -18,12 +18,16 @@
 import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
 import * as VcsProcess from "../vcs/VcsProcess.ts";
 import * as ServerConfig from "../config.ts";
+import * as ServerSettings from "../serverSettings.ts";
 
 const layerServerConfig = ServerConfig.ServerConfig.layerTest(process.cwd(), {
   prefix: "t3-checkpoint-store-test-",
 });
 const layerVcsProcessTest = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer));
+const layerVcsDriverTest = VcsDriverRegistry.layer.pipe(
+  Layer.provide(layerVcsProcessTest),
+  Layer.provide(ServerSettings.layerTest()),
+);
-const layerVcsDriverTest = VcsDriverRegistry.layer.pipe(Layer.provide(layerVcsProcessTest));
 const layerCheckpointStoreTest = CheckpointStore.layer.pipe(
   Layer.provideMerge(layerVcsDriverTest),
   Layer.provideMerge(NodeServices.layer),
@@ -101,6 +105,35 @@
     .concat("\n");
 }
 
+it.effect("captures no checkpoint while source control is off", () =>
+  Effect.gen(function* () {
+    const tmp = yield* makeTmpDir();
+    yield* initRepoWithCommit(tmp);
+    const checkpointStore = yield* CheckpointStore.CheckpointStore;
+    const checkpointRef = checkpointRefForThreadTurn(ThreadId.make("thread-source-control-off"), 1);
+
+    expect(yield* checkpointStore.isGitRepository(tmp)).toBe(false);
+    const error = yield* checkpointStore
+      .captureCheckpoint({ cwd: tmp, checkpointRef })
+      .pipe(Effect.flip);
+    expect(error._tag).toBe("VcsUnsupportedOperationError");
+    expect(yield* git(tmp, ["for-each-ref", "refs/t3"])).toBe("");
+  }).pipe(
+    Effect.provide(
+      CheckpointStore.layer.pipe(
+        Layer.provide(
+          VcsDriverRegistry.layer.pipe(
+            Layer.provide(layerVcsProcessTest),
+            Layer.provide(ServerSettings.layerTest({ sourceControlEnabled: false })),
+          ),
+        ),
+        Layer.provideMerge(layerVcsProcessTest),
+        Layer.provideMerge(NodeServices.layer),
+      ),
+    ),
+  ),
+);
+
 it.layer(layerTest)("CheckpointStore.layer", (it) => {
   it.effect("detects a nested workspace without its own .git entry", () =>
     Effect.gen(function* () {
```

## apps/server/src/git/GitManager.test.ts

Status: modified; +17; −0

```diff
@@ -1226,6 +1226,22 @@
     }),
   );
 
+  it.effect("branch PR lookup finds nothing and runs no git while source control is off", () =>
+    Effect.gen(function* () {
+      const repoDir = yield* makeTempDir("t3code-git-manager-");
+      yield* initRepo(repoDir);
+      const gitCommands: string[] = [];
+      const { manager, ghCalls } = yield* makeManager({
+        gitCommands,
+        serverSettings: { sourceControlEnabled: false },
+      });
+
+      expect(yield* manager.branchPullRequest({ cwd: repoDir, branch: "main" })).toBeNull();
+      expect(gitCommands).toEqual([]);
+      expect(ghCalls).toEqual([]);
+    }),
+  );
+
   it.effect("branch PR lookup uses the saved name after the local branch is deleted", () =>
     Effect.gen(function* () {
       const repoDir = yield* makeTempDir("t3code-git-manager-");
@@ -4065,6 +4081,7 @@
           VcsDriverRegistry.layer.pipe(
             Layer.provide(VcsProjectConfig.layer),
             Layer.provide(VcsProcess.layer),
+            Layer.provide(ServerSettings.layerTest()),
           ),
         );
         const handle = yield* Context.get(registry, VcsDriverRegistry.VcsDriverRegistry).resolve({
```

## apps/server/src/git/GitManager.ts

Status: modified; +2; −0

```diff
@@ -2683,6 +2683,8 @@
   const branchPullRequest: GitManager["Service"]["branchPullRequest"] = Effect.fn(
     "branchPullRequest",
   )(function* ({ cwd, branch }, options) {
+    // Settlement and discovery sweeps ask about every thread's branch directly.
+    if (!(yield* ServerSettings.sourceControlEnabled(serverSettingsService))) return null;
     const cacheCwd = yield* normalizeStatusCacheKey(cwd);
     const refresh = options?.refresh === true;
     const freshGitState = refresh || options?.freshGitState === true;
```

## apps/server/src/orchestration-v2/testkit/ProviderReplayHarness.ts

Status: modified; +1; −0

```diff
@@ -334,6 +334,7 @@
   const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe(
     Layer.provide(VcsProcess.layer),
     Layer.provide(layerServerConfig),
+    Layer.provide(layerServerSettings),
     Layer.provide(NodeServices.layer),
   );
   const layerCheckpointStore = CheckpointStore.layer.pipe(
```

## apps/server/src/project/RepositoryIdentityResolver.test.ts

Status: modified; +31; −1

```diff
@@ -4,9 +4,11 @@
 import { expect, it } from "@effect/vitest";
 import * as Effect from "effect/Effect";
 import * as FileSystem from "effect/FileSystem";
+import * as Layer from "effect/Layer";
 import * as Path from "effect/Path";
 
 import * as ProcessRunner from "../processRunner.ts";
+import * as ServerSettings from "../serverSettings.ts";
 import * as RepositoryIdentityResolver from "./RepositoryIdentityResolver.ts";
 
 const normalizePathSeparators = (value: string) => value.replaceAll("\\", "/");
@@ -21,7 +23,9 @@
     });
   }).pipe(Effect.provide(ProcessRunner.layer));
 
-it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
+const layerTest = Layer.merge(NodeServices.layer, ServerSettings.layerTest());
+
+it.layer(layerTest)("RepositoryIdentityResolverLive", (it) => {
   it.effect("normalizes equivalent GitHub remotes into a stable repository identity", () =>
     Effect.gen(function* () {
       const fileSystem = yield* FileSystem.FileSystem;
@@ -287,4 +291,30 @@
       expect(identity?.name).toBe("t3code");
     }).pipe(Effect.provide(RepositoryIdentityResolver.layer)),
   );
+
+  it.effect("resolves no repository and runs no git while source control is off", () =>
+    Effect.gen(function* () {
+      const fileSystem = yield* FileSystem.FileSystem;
+      const cwd = yield* fileSystem.makeTempDirectoryScoped({
+        prefix: "t3-repository-identity-source-control-off-test-",
+      });
+      yield* git(cwd, ["init"]);
+      yield* git(cwd, ["remote", "add", "origin", "git@github.com:T3Tools/t3code.git"]);
+
+      const commands: Array<string> = [];
+      const resolver = yield* RepositoryIdentityResolver.make().pipe(
+        Effect.provideService(ProcessRunner.ProcessRunner, {
+          run: (input) => {
+            commands.push(input.command);
+            return Effect.die("source control is off, so nothing may run");
+          },
+        }),
+        Effect.provide(ServerSettings.layerTest({ sourceControlEnabled: false })),
+      );
+
+      expect(yield* resolver.resolve(cwd)).toBeNull();
+      expect(yield* resolver.resolve(cwd, { refresh: true })).toBeNull();
+      expect(commands).toEqual([]);
+    }),
+  );
 });
```

## apps/server/src/project/RepositoryIdentityResolver.ts

Status: modified; +3; −0

```diff
@@ -13,6 +13,7 @@
 import * as Layer from "effect/Layer";
 
 import * as ProcessRunner from "../processRunner.ts";
+import * as ServerSettings from "../serverSettings.ts";
 import { explicitBareRepositoryEnv } from "../vcs/VcsProcess.ts";
 import * as HostProcess from "@t3tools/shared/HostProcess";
 
@@ -233,6 +234,7 @@
   options: RepositoryIdentityResolverOptions = {},
 ) {
   const processRunner = yield* ProcessRunner.ProcessRunner;
+  const serverSettings = yield* ServerSettings.ServerSettingsService;
   const cacheCapacity = options.cacheCapacity ?? DEFAULT_REPOSITORY_IDENTITY_CACHE_CAPACITY;
   const refine = options.refine ?? Effect.succeed;
   const timeToLive = (exit: Exit.Exit<unknown>) =>
@@ -269,6 +271,7 @@
 
   const resolve: RepositoryIdentityResolver["Service"]["resolve"] = Effect.fnUntraced(
     function* (cwd, options) {
+      if (!(yield* ServerSettings.sourceControlEnabled(serverSettings))) return null;
       if (options?.refresh) yield* Cache.invalidate(repositoryRootCache, cwd);
       const cacheKey = yield* Cache.get(repositoryRootCache, cwd);
       if (cacheKey === null) return null;
```

## apps/server/src/server.ts

Status: modified; +13; −3

```diff
@@ -315,7 +315,10 @@
 
 const layerPersistence = Layer.empty.pipe(Layer.provideMerge(SqlitePersistence.layerConfig));
 
-const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe(Layer.provide(VcsProjectConfig.layer));
+const layerVcsDriverRegistry = VcsDriverRegistry.layer.pipe(
+  Layer.provide(VcsProjectConfig.layer),
+  Layer.provide(layerServerSettings),
+);
 
 const layerSourceControlProviderRegistry = SourceControlProviderRegistry.layer.pipe(
   Layer.provideMerge(SourceControlBuiltInDrivers.layer),
@@ -345,7 +348,11 @@
       }),
     });
   }),
-).pipe(Layer.provide(layerSourceControlProviderRegistry), Layer.provide(ProcessRunner.layer));
+).pipe(
+  Layer.provide(layerSourceControlProviderRegistry),
+  Layer.provide(ProcessRunner.layer),
+  Layer.provide(layerServerSettings),
+);
 
 const layerPullRequestService = PullRequestService.layer.pipe(
   Layer.provide(PullRequestProviderRegistry.layer),
@@ -437,7 +444,10 @@
   Layer.provide(NetService.layer),
 );
 
-const layerWorkspaceEntries = WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer));
+const layerWorkspaceEntries = WorkspaceEntries.layer.pipe(
+  Layer.provide(WorkspacePaths.layer),
+  Layer.provide(layerServerSettings),
+);
 
 const layerWorkspaceFileSystem = WorkspaceFileSystem.layer.pipe(
   Layer.provide(WorkspacePaths.layer),
```

## apps/server/src/serverRuntimeStartup.test.ts

Status: modified; +19; −0

```diff
@@ -369,3 +369,22 @@
     assert.deepStrictEqual(pulled, ["/inherited"]);
   }),
 );
+
+it.effect("automatic pull runs no git while source control is off", () =>
+  Effect.gen(function* () {
+    const calls: string[] = [];
+    const record = (cwd: string) =>
+      Effect.sync(() => calls.push(cwd)).pipe(Effect.andThen(Effect.die("git must not run")));
+    const git = {
+      statusDetails: record,
+      pullCurrentBranch: record,
+    } as unknown as GitVcsDriver.GitVcsDriver["Service"];
+
+    yield* ServerRuntimeStartup.autoPullProjects(
+      [{ id: ProjectId.make("/project"), workspaceRoot: "/project" } as never],
+      { ...DEFAULT_SERVER_SETTINGS, defaultAutoPull: true, sourceControlEnabled: false },
+    ).pipe(Effect.provideService(GitVcsDriver.GitVcsDriver, git));
+
+    assert.deepStrictEqual(calls, []);
+  }),
+);
```

## apps/server/src/serverRuntimeStartup.ts

Status: modified; +1; −0

```diff
@@ -192,6 +192,7 @@
   projects: ReadonlyArray<Pick<Project, "id" | "workspaceRoot" | "autoPull">>,
   settings = DEFAULT_SERVER_SETTINGS,
 ) {
+  if (!settings.sourceControlEnabled) return;
   const git = yield* GitVcsDriver.GitVcsDriver;
   const workspaceRoots = [
     ...new Set(
```

## apps/server/src/serverSettings.ts

Status: modified; +10; −0

```diff
@@ -263,6 +263,16 @@
 export const layerTest = (overrides: DeepPartial<ServerSettings> = {}) =>
   Layer.effect(ServerSettingsService, makeTest(overrides));
 
+/**
+ * Whether the server may run Git in project folders. Off, every folder reads as
+ * having no repository. A failed read keeps the default.
+ */
+export const sourceControlEnabled = (settings: ServerSettingsService["Service"]) =>
+  settings.getSettings.pipe(
+    Effect.map((current) => current.sourceControlEnabled),
+    Effect.orElseSucceed(() => true),
+  );
+
 // Migrate saved token delivery without accepting it in settings writes or
 // letting one retired value reset the rest of the environment's settings.
 const PersistedResponseStreamingMode = Schema.Union([
```

## apps/server/src/storageCleanup.test.ts

Status: modified; +27; −0

```diff
@@ -1329,6 +1329,33 @@
       }),
     ),
   );
+  it.live("leaves worktrees untouched while source control is off", () =>
+    runCleanupTest(
+      Effect.gen(function* () {
+        const { service, fs, worktree, setSettings } = yield* cleanupFixture();
+        setSettings({ sourceControlEnabled: false });
+        const report = yield* service.runNow;
+        expect(report.entries.some((entry) => entry.kind === "worktree")).toBe(false);
+        expect(yield* fs.exists(worktree)).toBe(true);
+      }),
+    ),
+  );
+  it.live("keeps a worktree when source control turns off during the sweep", () =>
+    runCleanupTest(
+      Effect.gen(function* () {
+        const fixture = yield* pausedMeasurementFixture();
+        const run = yield* fixture.service.runNow.pipe(Effect.forkChild);
+        yield* Deferred.await(fixture.measuring);
+        fixture.setSettings({ sourceControlEnabled: false });
+        yield* Deferred.succeed(fixture.resume, undefined);
+        expect((yield* Fiber.join(run)).entries[0]).toMatchObject({
+          outcome: "kept",
+          reason: "Settings changed since check",
+        });
+        expect(yield* fixture.fs.exists(fixture.worktree)).toBe(true);
+      }),
+    ),
+  );
   it.live("still removes a worktree when measuring its size fails", () =>
     runCleanupTest(
       Effect.gen(function* () {
```

## apps/server/src/storageCleanup.ts

Status: modified; +7; −4

```diff
@@ -718,6 +718,8 @@
     now: number,
     entries: StorageCleanupReportEntry[],
   ) {
+    // Deciding whether a checkout is safe to remove takes Git.
+    if (!serverSettings.sourceControlEnabled) return;
     if (!anyWorktreePolicy(serverSettings, worktreeCleanupEnabled)) return;
     const roots: Array<string> = [];
     for (const directory of managedWorktreesDirectories(
@@ -919,6 +921,8 @@
         // the removal. Read only this path's threads: a full snapshot per
         // worktree takes seconds on large databases.
         const blockedSinceCheck = Effect.fnUntraced(function* () {
+          if (!(yield* settingsService.getSettings).sourceControlEnabled)
+            return "settings changed since check";
           const latestProjects = yield* projectStore.listShells();
           if (yield* containsProjectRoot(worktreePath, [project, ...latestProjects]))
             return "contains a project checkout";
@@ -1000,11 +1004,10 @@
           return keep("commit changed since check");
         const finalChanges = yield* localChangesReason(worktreePath, settings);
         if (finalChanges !== null) return keep(finalChanges);
+        const currentSettings = yield* settingsService.getSettings;
+        const current = resolveWorktreeCleanup(currentSettings, thread.projectId);
-        const current = resolveWorktreeCleanup(
-          yield* settingsService.getSettings,
-          thread.projectId,
-        );
         if (
+          !currentSettings.sourceControlEnabled ||
           Object.keys(settings).some(
             (key) =>
               current[key as keyof typeof settings] !== settings[key as keyof typeof settings],
```

## apps/server/src/vcs/VcsDriverRegistry.test.ts

Status: modified; +27; −0

```diff
@@ -6,6 +6,7 @@
 import * as Path from "effect/Path";
 import { ChildProcessSpawner } from "effect/process";
 
+import * as ServerSettings from "../serverSettings.ts";
 import * as VcsProcess from "./VcsProcess.ts";
 import * as VcsProjectConfig from "./VcsProjectConfig.ts";
 import * as VcsDriverRegistry from "./VcsDriverRegistry.ts";
@@ -24,6 +25,7 @@
 describe("VcsDriverRegistry", () => {
   const makeDiskBackedLayer = (rootDir: string, calls: string[]) =>
     Layer.effect(VcsDriverRegistry.VcsDriverRegistry, VcsDriverRegistry.make).pipe(
+      Layer.provideMerge(ServerSettings.layerTest()),
       Layer.provide(NodeServices.layer),
       Layer.provide(
         Layer.mock(VcsProjectConfig.VcsProjectConfig)({
@@ -122,4 +124,29 @@
       }).pipe(Effect.provide(makeDiskBackedLayer(repoDir, calls)));
     }).pipe(Effect.provide(NodeServices.layer)),
   );
+
+  it.effect("reads every folder as having no repository while source control is off", () =>
+    Effect.gen(function* () {
+      const fs = yield* FileSystem.FileSystem;
+      const path = yield* Path.Path;
+      const repoDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-vcs-registry-" });
+      yield* fs.makeDirectory(path.join(repoDir, ".git"));
+      const calls: string[] = [];
+
+      yield* Effect.gen(function* () {
+        const registry = yield* VcsDriverRegistry.VcsDriverRegistry;
+        const settings = yield* ServerSettings.ServerSettingsService;
+        yield* settings.updateSettings({ sourceControlEnabled: false });
+
+        assert.equal(yield* registry.detect({ cwd: repoDir }), null);
+        assert.equal(yield* registry.detect({ cwd: repoDir, requestedKind: "git" }), null);
+        const error = yield* registry.resolve({ cwd: repoDir }).pipe(Effect.flip);
+        assert.equal(error._tag, "VcsUnsupportedOperationError");
+        assert.deepEqual(calls, []);
+
+        yield* settings.updateSettings({ sourceControlEnabled: true });
+        assert.equal((yield* registry.detect({ cwd: repoDir }))?.repository.rootPath, repoDir);
+      }).pipe(Effect.provide(makeDiskBackedLayer(repoDir, calls)));
+    }).pipe(Effect.provide(NodeServices.layer)),
+  );
 });
```

## apps/server/src/vcs/VcsDriverRegistry.ts

Status: modified; +10; −2

```diff
@@ -9,6 +9,7 @@
 
 import type { VcsDriverKind, VcsError, VcsRepositoryIdentity } from "@t3tools/contracts";
 import { VcsUnsupportedOperationError } from "@t3tools/contracts";
+import * as ServerSettings from "../serverSettings.ts";
 import * as GitVcsDriver from "./GitVcsDriver.ts";
 import * as VcsProjectConfig from "./VcsProjectConfig.ts";
 import * as VcsDriver from "./VcsDriver.ts";
@@ -69,6 +70,7 @@
   const fileSystem = yield* FileSystem.FileSystem;
   const path = yield* Path.Path;
   const projectConfig = yield* VcsProjectConfig.VcsProjectConfig;
+  const serverSettings = yield* ServerSettings.ServerSettingsService;
   const git = yield* GitVcsDriver.makeVcsDriver;
   const drivers: Partial<Record<VcsDriverKind, VcsDriver.VcsDriver["Service"]>> = {
     git,
@@ -162,9 +164,14 @@
       }),
     },
   );
+
+  // Off reads every folder as having no repository, so checkpoints, status,
+  // and diffs degrade as they do outside Git and no Git process starts.
+  const sourceControlEnabled = ServerSettings.sourceControlEnabled(serverSettings);
 
   const detect: VcsDriverRegistry["Service"]["detect"] = Effect.fn("VcsDriverRegistry.detect")(
     function* (input) {
+      if (!(yield* sourceControlEnabled)) return null;
       const requestedKind = yield* projectConfig.resolveKind(input);
       const key = detectionCacheKey({ cwd: input.cwd, requestedKind });
       const cached = yield* Cache.get(detectionCache, key);
@@ -193,8 +200,9 @@
       return yield* new VcsUnsupportedOperationError({
         operation: "VcsDriverRegistry.resolve",
         kind: requestedKind === "auto" ? "unknown" : requestedKind,
-        detail:
-          requestedKind === "auto"
+        detail: !(yield* sourceControlEnabled)
+          ? "Source control is turned off in this environment's settings."
+          : requestedKind === "auto"
             ? `No supported VCS repository was detected at ${input.cwd}.`
             : `No ${requestedKind} repository was detected at ${input.cwd}.`,
       });
```

## apps/server/src/workspace/WorkspaceEntries.test.ts

Status: modified; +31; −0

```diff
@@ -9,6 +9,7 @@
 import { vi } from "vite-plus/test";
 
 import * as ServerConfig from "../config.ts";
+import * as ServerSettings from "../serverSettings.ts";
 import * as HostProcess from "@t3tools/shared/HostProcess";
 import * as VcsProcess from "../vcs/VcsProcess.ts";
 import * as WorkspaceEntries from "./WorkspaceEntries.ts";
@@ -23,6 +24,7 @@
   Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))),
   Layer.provideMerge(WorkspacePaths.layer),
   Layer.provideMerge(VcsProcess.layer),
+  Layer.provideMerge(ServerSettings.layerTest()),
   Layer.provide(
     ServerConfig.ServerConfig.layerTest(process.cwd(), {
       prefix: "t3-workspace-entries-test-",
@@ -132,6 +134,35 @@
       }),
     );
 
+    it.effect("leaves entries unlabelled while source control is off", () =>
+      Effect.gen(function* () {
+        const cwd = yield* makeTempDir({ git: true });
+        yield* writeTextFile(cwd, ".gitignore", "node_modules/\n.env\n");
+        yield* writeTextFile(cwd, ".env", "secret=value");
+        yield* writeTextFile(cwd, "node_modules/pkg/index.js");
+        const settings = yield* ServerSettings.ServerSettingsService;
+        const workspaceEntries = yield* WorkspaceEntries.WorkspaceEntries;
+
+        yield* Effect.gen(function* () {
+          yield* settings.updateSettings({ sourceControlEnabled: false });
+          const root = yield* workspaceEntries.list({ cwd, directoryPath: "" });
+          expect(root.entries).toEqual(
+            expect.arrayContaining([
+              { path: ".env", kind: "file" },
+              { path: "node_modules", kind: "directory" },
+            ]),
+          );
+          expect(root.entries.some((entry) => entry.ignored)).toBe(false);
+          expect(yield* workspaceEntries.listIgnoredFiles({ cwd })).toEqual({
+            files: [],
+            truncated: false,
+          });
+        }).pipe(
+          Effect.ensuring(Effect.ignore(settings.updateSettings({ sourceControlEnabled: true }))),
+        );
+      }),
+    );
+
     it.effect("labels the children of wide folders exactly as git does for each child", () =>
       Effect.gen(function* () {
         // Above 1,000 children, listing first asks whether the whole folder is ignored.
```

## apps/server/src/workspace/WorkspaceEntries.ts

Status: modified; +8; −0

```diff
@@ -26,6 +26,7 @@
 import { normalizeSearchQuery } from "@t3tools/shared/searchRanking";
 
 import { expandHomePath } from "@t3tools/provider-core/server/pathExpansion";
+import * as ServerSettings from "../serverSettings.ts";
 import * as VcsProcess from "../vcs/VcsProcess.ts";
 import * as WorkspacePaths from "./WorkspacePaths.ts";
 import * as WorkspaceSearchIndex from "./WorkspaceSearchIndex.ts";
@@ -151,6 +152,7 @@
   const workspacePaths = yield* WorkspacePaths.WorkspacePaths;
   const workspaceSearchIndexes = yield* WorkspaceSearchIndex.WorkspaceSearchIndexMap;
   const vcsProcess = yield* VcsProcess.VcsProcess;
+  const serverSettings = yield* ServerSettings.ServerSettingsService;
 
   const normalizeWorkspaceRoot = Effect.fn("WorkspaceEntries.normalizeWorkspaceRoot")(function* (
     cwd: string,
@@ -334,6 +336,9 @@
           },
           catch: toError,
         });
+        if (!(yield* ServerSettings.sourceControlEnabled(serverSettings))) {
+          return { entries, truncated: false };
+        }
         // Use stdin so large directories cannot exceed the command-line argument limit.
         // Ignore classification is optional in non-git workspaces or when git is unavailable.
         const checkIgnore = (paths: ReadonlyArray<string>) =>
@@ -400,6 +405,9 @@
     "WorkspaceEntries.listIgnoredFiles",
   )(function* (input) {
     const normalizedCwd = yield* normalizeWorkspaceRoot(input.cwd);
+    if (!(yield* ServerSettings.sourceControlEnabled(serverSettings))) {
+      return { files: [], truncated: false };
+    }
     // `--directory` reports a folder holding nothing but ignored files as one
     // `name/` entry, which is how `node_modules` and build output drop out.
     const result = yield* vcsProcess
```

## apps/server/src/workspace/WorkspaceFileSystem.test.ts

Status: modified; +2; −0

```diff
@@ -11,6 +11,7 @@
 import * as Path from "effect/Path";
 
 import * as ServerConfig from "../config.ts";
+import * as ServerSettings from "../serverSettings.ts";
 import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
 import * as VcsProcess from "../vcs/VcsProcess.ts";
 import * as KnownWorkspaces from "./KnownWorkspaces.ts";
@@ -46,6 +47,7 @@
   Layer.provideMerge(WorkspaceEntries.layer.pipe(Layer.provide(WorkspacePaths.layer))),
   Layer.provideMerge(WorkspacePaths.layer),
   Layer.provideMerge(VcsDriverRegistry.layer.pipe(Layer.provide(VcsProcess.layer))),
+  Layer.provide(ServerSettings.layerTest()),
   Layer.provide(
     ServerConfig.ServerConfig.layerTest(process.cwd(), {
       prefix: "t3-workspace-files-test-",
```

[Next page](https://git.cafe/pingdotgg/t3code/pull/23876/diffs?after=ImFwcHMvc2VydmVyL3NyYy93b3Jrc3BhY2UvV29ya3NwYWNlRmlsZVN5c3RlbS50ZXN0LnRzIg&expectedVersion=5&format=markdown)
