From cab375a6f8a5edd87f6cb4ec9095c3521d733f52 Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Wed, 24 Jun 2026 00:03:14 -0700 Subject: [PATCH] fix(workspace): detect sub-repos when workspace.json is missing The initial fix only checked loadWorkspaceConfig, but the dashboard POST /api/projects and `fn project add` routes never create workspace.json (only registerProjectInteractive does). So re-adding a workspace project through the dashboard still triggered git init because the guard saw no workspace.json. Add detectWorkspaceRepos as a fallback: after loadWorkspaceConfig and isInsideGitWorkTree both miss, probe for git sub-repos. If found, persist workspace.json and return 'existing' without running git init. This covers all registration surfaces. --- .../core/src/__tests__/git-repository.test.ts | 20 +++++++++++++++++++ packages/core/src/git-repository.ts | 16 +++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/packages/core/src/__tests__/git-repository.test.ts b/packages/core/src/__tests__/git-repository.test.ts index 973109b6b4..e61f97f724 100644 --- a/packages/core/src/__tests__/git-repository.test.ts +++ b/packages/core/src/__tests__/git-repository.test.ts @@ -120,4 +120,24 @@ describe("ensureGitRepositoryForProjectPath", () => { // No .git should be created at the workspace root expect(existsSync(join(projectPath, ".git"))).toBe(false); }); + + it("detects workspace sub-repos and skips git init when workspace.json is missing", async () => { + const projectPath = tempDir("fusion-git-workspace-detect-"); + // Create a real git sub-repo inside the project root (but no workspace.json) + const subRepo = join(projectPath, "repo-a"); + mkdirSync(subRepo, { recursive: true }); + await git(subRepo, ["init", "-b", "main"]); + await git(subRepo, ["config", "user.email", "test@test.com"]); + await git(subRepo, ["config", "user.name", "Test"]); + writeFileSync(join(subRepo, "README.md"), "# repo-a\n"); + await git(subRepo, ["add", "README.md"]); + await git(subRepo, ["commit", "-m", "init"]); + + const outcome = await ensureGitRepositoryForProjectPath(projectPath); + + expect(outcome).toBe("existing"); + expect(existsSync(join(projectPath, ".git"))).toBe(false); + // workspace.json should be auto-persisted so future calls hit the fast path + expect(existsSync(join(projectPath, ".fusion", "workspace.json"))).toBe(true); + }); }); diff --git a/packages/core/src/git-repository.ts b/packages/core/src/git-repository.ts index 3bef28e70a..beffccd194 100644 --- a/packages/core/src/git-repository.ts +++ b/packages/core/src/git-repository.ts @@ -59,6 +59,22 @@ export async function ensureGitRepositoryForProjectPath( return "existing"; } + /* + FNXC:Workspace 2026-06-24-14:30: + Fallback workspace detection: when workspace.json is missing (e.g. project added via + dashboard or `fn project add`, which don't run the interactive workspace detection flow), + probe for git sub-repos. If found, persist workspace.json so future calls hit the fast + loadWorkspaceConfig path, and skip git init. This covers all registration surfaces: the + CLI interactive setup writes workspace.json explicitly, but dashboard POST /api/projects + and `fn project add` do not — without this fallback they would create a stray .git at the + workspace root because loadWorkspaceConfig returned null. + */ + const detectedRepos = await detectWorkspaceRepos(projectPath); + if (detectedRepos.length > 0) { + await saveWorkspaceConfig(projectPath, { repos: detectedRepos }); + return "existing"; + } + try { await runner("git", ["-C", projectPath, "init"], { timeout }); return "initialized";