Skip to content

Commit 51d1004

Browse files
tyulyukovclaude
andcommitted
fix(sidebar): prefer origin remote and deep-merge persisted client settings
Forks with both origin and upstream were surfacing the upstream repo name because pickPrimaryRemote prioritized upstream. Flip the order so a user's own fork wins, making the canonicalKey consistent with their working tree. Client settings hydration replaced the default snapshot with raw persisted JSON, so fields added in later versions (e.g. sidebarProjectGroupingMode) stayed undefined on upgrade. Deep-merge with DEFAULT_CLIENT_SETTINGS so missing keys inherit defaults. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent c5f4c91 commit 51d1004

3 files changed

Lines changed: 10 additions & 8 deletions

File tree

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -97,24 +97,24 @@ it.layer(NodeServices.layer)("RepositoryIdentityResolverLive", (it) => {
9797
}).pipe(Effect.provide(RepositoryIdentityResolverLive)),
9898
);
9999

100-
it.effect("prefers upstream over origin when both remotes are configured", () =>
100+
it.effect("prefers origin over upstream so forks surface their own name", () =>
101101
Effect.gen(function* () {
102102
const fileSystem = yield* FileSystem.FileSystem;
103103
const cwd = yield* fileSystem.makeTempDirectoryScoped({
104-
prefix: "marcode-repository-identity-upstream-test-",
104+
prefix: "marcode-repository-identity-origin-priority-test-",
105105
});
106106

107107
yield* git(cwd, ["init"]);
108-
yield* git(cwd, ["remote", "add", "origin", "git@github.com:julius/marcode.git"]);
108+
yield* git(cwd, ["remote", "add", "origin", "git@github.com:tyulyukov/marcode.git"]);
109109
yield* git(cwd, ["remote", "add", "upstream", "git@github.com:MarCodeHQ/marcode.git"]);
110110

111111
const resolver = yield* RepositoryIdentityResolver;
112112
const identity = yield* resolver.resolve(cwd);
113113

114114
expect(identity).not.toBeNull();
115-
expect(identity?.locator.remoteName).toBe("upstream");
116-
expect(identity?.canonicalKey).toBe("github.com/marcodehq/marcode");
117-
expect(identity?.displayName).toBe("marcodehq/marcode");
115+
expect(identity?.locator.remoteName).toBe("origin");
116+
expect(identity?.canonicalKey).toBe("github.com/tyulyukov/marcode");
117+
expect(identity?.displayName).toBe("tyulyukov/marcode");
118118
}).pipe(Effect.provide(RepositoryIdentityResolverLive)),
119119
);
120120

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ function parseRemoteFetchUrls(stdout: string): Map<string, string> {
2727
function pickPrimaryRemote(
2828
remotes: ReadonlyMap<string, string>,
2929
): { readonly remoteName: string; readonly remoteUrl: string } | null {
30-
for (const preferredRemoteName of ["upstream", "origin"] as const) {
30+
for (const preferredRemoteName of ["origin", "upstream"] as const) {
3131
const remoteUrl = remotes.get(preferredRemoteName);
3232
if (remoteUrl) {
3333
return { remoteName: preferredRemoteName, remoteUrl };

apps/web/src/hooks/useSettings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ async function hydrateClientSettings(): Promise<void> {
7878
try {
7979
const persistedSettings = await ensureLocalApi().persistence.getClientSettings();
8080
if (persistedSettings) {
81-
replaceClientSettingsSnapshot(persistedSettings);
81+
replaceClientSettingsSnapshot(
82+
deepMerge(DEFAULT_CLIENT_SETTINGS, persistedSettings) as ClientSettings,
83+
);
8284
}
8385
} catch (error) {
8486
console.error(`${CLIENT_SETTINGS_PERSISTENCE_ERROR_SCOPE} hydrate failed`, error);

0 commit comments

Comments
 (0)