Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,17 @@ export function serviceHomeMatches(a: string, b: string): boolean {
return normalizePathForCompare(a) === normalizePathForCompare(b);
}

/** Accept the Linux default written by service versions predating WSL home discovery. */
export function serviceCodexHomeMatchesInstall(recordedHome: string, deps: CodexHomeDeps = {}): boolean {
const actualHome = currentCodexHome(deps);
if (serviceHomeMatches(recordedHome, actualHome)) return true;

const env = deps.env ?? process.env;
if (env.CODEX_HOME?.trim() || !isWslRuntime(deps)) return false;
const legacyDefault = join((deps.homedir ?? homedir)(), ".codex");
return serviceHomeMatches(recordedHome, legacyDefault);
Comment on lines +249 to +252

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve explicitly pinned Linux-home ownership

When a WSL service was intentionally installed with CODEX_HOME=/home/$USER/.codex, but that home has no config.toml—for example, because Codex integration was disabled—a later shell with CODEX_HOME unset can discover the sole Windows home. This fallback cannot distinguish that explicit install record from legacy implicit metadata and returns true, so repair silently rewrites the unit and state for the Windows home, while stop/uninstall restore the wrong home. Only accept the compatibility case when the installed launcher/state proves the original home was implicit.

Useful? React with 👍 / 👎.

}

/** Single accessor for backend-sensitive service code — v1/legacy state maps to scheduler. */
export function readServiceBackend(): ServiceBackend {
return readServiceInstallState()?.backend === "native" ? "native" : "scheduler";
Expand Down Expand Up @@ -299,9 +310,7 @@ export function assertServiceEnvironmentMatchesInstall(): void {
const state = readServiceInstallState();
if (!state) return;
const actualCodexHome = currentCodexHome();
const expected = normalizePathForCompare(state.codexHome);
const actual = normalizePathForCompare(actualCodexHome);
if (expected !== actual) {
if (!serviceCodexHomeMatchesInstall(state.codexHome)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compare against one Codex-home snapshot

In WSL, home discovery depends on live config.toml files and candidate directories, but assertServiceEnvironmentMatchesInstall() resolves actualCodexHome and then this call resolves it again inside serviceCodexHomeMatchesInstall(). If candidates change between those reads, the check can reject a matching record while reporting identical installed/current paths, or accept based on a different snapshot. Pass the already-resolved actualCodexHome into the compatibility comparison so ownership and diagnostics use the same result.

Useful? React with 👍 / 👎.

throw new ServiceOwnershipError(
`Service was installed with CODEX_HOME=${state.codexHome}, but current CODEX_HOME=${actualCodexHome}. ` +
"Run the service command from the same Codex home so native Codex restore updates the correct config.",
Expand Down
5 changes: 3 additions & 2 deletions structure/02_config-and-codex-home.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ the resolved `CODEX_HOME`.
Service install-state ownership uses this same resolver. In WSL, an unset `CODEX_HOME` may resolve
to the single discoverable Windows Desktop home; recording Linux `~/.codex` instead would make a
later repair or uninstall look foreign even though the service and runtime were started from the
same environment. An explicit `CODEX_HOME` remains authoritative, and existing foreign ownership
records are never migrated implicitly.
same environment. Ownership checks therefore accept that exact legacy Linux-home record when WSL
now discovers a Windows home. An explicit `CODEX_HOME` remains authoritative, and other foreign
ownership records are never migrated implicitly.

[Decision Log]
- 목적과 의도: Keep service ownership metadata aligned with the Codex home the proxy actually uses.
Expand Down
25 changes: 24 additions & 1 deletion tests/codex-home-wsl.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, expect, test } from "bun:test";
import { wslAutomountRoot, listWslWindowsCodexHomes } from "../src/codex/home";
import { isWindowsInteropDir } from "../src/codex/shim";
import { currentServiceHomes } from "../src/service";
import { currentServiceHomes, serviceCodexHomeMatchesInstall } from "../src/service";

describe("wsl.conf automount root", () => {
test("defaults to /mnt when wsl.conf is absent or silent", () => {
Expand Down Expand Up @@ -63,4 +63,27 @@ describe("wsl.conf automount root", () => {
expect(homes.codexHome).toBe(windowsCodexHome);
expect(homes.codexHome).not.toBe("/home/example/.codex");
});

test("service ownership accepts the legacy Linux fallback when WSL now discovers Windows Codex", () => {
const usersRoot = ["/mnt/c", "Users"].join("/");
const windowsCodexHome = [usersRoot, "windows-user", ".codex"].join("/");
const deps = {
env: { WSL_DISTRO_NAME: "Ubuntu" },
platform: "linux",
homedir: () => "/home/example",
usersRoot,
existsSync: (path: string) => path === usersRoot
|| path === `${windowsCodexHome}/config.toml`,
readdirSync: () => ["windows-user"],
statSync: (() => ({ isDirectory: () => true })) as never,
realpathSync: (path: string) => path,
};

expect(serviceCodexHomeMatchesInstall("/home/example/.codex", deps)).toBe(true);
expect(serviceCodexHomeMatchesInstall("/home/other/.codex", deps)).toBe(false);
expect(serviceCodexHomeMatchesInstall("/home/example/.codex", {
...deps,
env: { ...deps.env, CODEX_HOME: windowsCodexHome },
})).toBe(false);
});
});
Loading