Skip to content

fix(update): preserve Bun install ownership#3259

Merged
Yeachan-Heo merged 19 commits into
devfrom
feat/issue-3257-bun-update-owner
Jul 22, 2026
Merged

fix(update): preserve Bun install ownership#3259
Yeachan-Heo merged 19 commits into
devfrom
feat/issue-3257-bun-update-owner

Conversation

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Summary

Implements the narrow #3257 package-manager ownership path while keeping OMX on Node:

  • validates npm or Bun ownership from canonical current executable/package-root evidence;
  • freezes Bun ownership through stable, deferred, setup, stamp, and diagnostic paths, without npm fallback;
  • fails Bun --dev before effects with the owner-approved unsupported message;
  • preserves npm and Windows npm fallback behavior;
  • separates advisory postinstall provenance and preserves an existing Bun owner stamp;
  • adds deterministic ownership tests and the Phase-0 command harness/verification manifest.

Verification

  • npm run build
  • focused update and postinstall tests
  • npm run check:no-unused
  • npm run lint
  • npm test
  • npm run smoke:packed-install
  • no-op stable/dev command-harness receipts

Refs #3257


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 552fe97f9b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/update.ts
Comment on lines +831 to +833
if (usesNativeTransaction && !ownership) {
console.log(packageManagerOwnershipError());
return { status: 'failed', currentVersion: null, latestVersion: null };

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 Record failed ownership checks in the launch throttle

When the passive startup update path uses the native dependencies and ownership cannot be resolved (for example a local checkout, an unsupported/global install, or ambiguous npm+Bun evidence), this branch returns before writeUpdateState runs. Because maybeCheckAndPromptUpdate only throttles via that state file, every subsequent launch re-enters ownership detection and logs this failure instead of waiting for CHECK_INTERVAL_MS; record the advisory check or defer ownership resolution until an install is actually needed.

Useful? React with 👍 / 👎.

Comment thread src/cli/update.ts Outdated
}

return spawnInstalledSetupRefresh(cliEntry, cwd);
return spawnInstalledSetupRefresh(cliEntry, cwd, spawnSync, ownership?.manager === 'bun' ? ownership.bunCommand : process.execPath);

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 Keep setup refresh on the Node runtime

For a Bun-owned global install whose omx bin resolves to this package’s #!/usr/bin/env node entrypoint, the running CLI is still on Node, but this line switches the setup refresh to ownership.bunCommand. The verification manifest added in this commit says Node is the only runtime contract, so a Bun stable update can install successfully and then refresh setup under an unsupported runtime; keep Bun as the package manager but invoke the updated CLI with Node for setup refreshes.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent exact-head red-team verdict for 552fe97f9bc26dc6e558401254df2bb2e2ca2953.

Hosted CI is green, but the current head is not merge-ready:

  1. P1: src/cli/update.ts retains bare npm/npm.cmd/cmd.exe mutation and npm root -g discovery after owner selection, so npm executable/root authority is not frozen before mutation.
  2. P1: controller installs diverge from the frozen manifest (--ignore-scripts, --no-audit, --no-progress, explicit prefix). The implementation must match the script-suppressed transaction contract rather than claim it only in artifacts.
  3. P1: deferred commands resolve npm/bun through ambient shells and reconstruct setup after the parent exits; selected command/root/package/bin and Node process.execPath setup handoff are not carried as one revalidated transaction.
  4. P2: src/cli/package-manager-ownership.ts assumes a Bun executable adjacent to bun pm bin -g; configurable global-bin layout does not prove that executable location.
  5. P2: the harness intentionally marks real-manager evidence NOT_EXECUTED/REQUIRED; it cannot prove stable/deferred Bun execution.

Repair every finding, add focused regressions, push a new head, wait for fresh CI, and obtain a fresh independent exact-head review. The GitHub API disallows a PR author from submitting a REQUEST_CHANGES review on their own PR, so this signed terminal verdict is posted as a PR comment.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Repair push: exact head 66515ab0b704f49889ba10e18d13d2612c93680d addresses the signed red-team transaction findings.

  • freezes npm command/prefix/root/environment before native mutation;
  • aligns native stable controller installs with --ignore-scripts --no-audit --no-progress --prefix;
  • resolves Bun executable separately from configured global-bin evidence and fails closed when it cannot be proven;
  • replaces native deferred shell-manager execution with a detached Node worker carrying and revalidating frozen ownership data, then using process.execPath for setup;
  • extends deterministic fake-root/spawn coverage and retains Bun-dev no-effects failure.

Local verification passed on this head: build, focused update/postinstall tests, no-unused, lint, full npm test, packed-install smoke, and no-op stable/dev harness receipts. Fresh hosted CI and a new exact-head review are required before merge.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 66515ab0b7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/update.ts Outdated
// cmd.exe can still resolve and run npm from the user's configured PATH.
return spawnProcess('cmd.exe', ['/d', '/s', '/c', 'npm', ...args], options);
function stableInstallArgs(ownership: PackageManagerOwnership, installSource: string): string[] {
return ['install', '--global', '--ignore-scripts', '--no-audit', '--no-progress', '--prefix', ownership.npmPrefix!, installSource];

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 Build Bun installs with Bun-compatible flags

When the frozen owner is Bun, this helper is still passed to spawnProcess(ownership.bunCommand, ...), so stable immediate updates invoke Bun with npm-only flags (--prefix and --no-audit) while the log says bun add -g. I checked bun install --help: it supports --global, --ignore-scripts, and --no-progress, but not those npm flags; a parser check treats the --prefix value as another package and fails, so Bun-owned stable updates cannot complete. The deferred worker duplicates the same argv and needs the same fix.

Useful? React with 👍 / 👎.

Comment thread src/scripts/postinstall.ts Outdated
Comment on lines +88 to +90
...(existingStamp?.package_manager === "npm" || existingStamp?.package_manager === "bun"
? { package_manager: existingStamp.package_manager }
: {}),

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 Do not carry stale package-manager provenance

If a user switches package managers with a real global reinstall (for example, a Bun-stamped install followed by npm install -g oh-my-codex), this postinstall path writes the new version but preserves package_manager: "bun". Later resolvePackageManagerOwnership trusts that stamp as the only manager to probe, so the now npm-owned executable cannot resolve ownership and omx update keeps failing until the user manually edits the stamp. Drop stale provenance here unless the current lifecycle/install has been validated as the same manager.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES

Exact review target: PR #3259 head 66515ab0b704f49889ba10e18d13d2612c93680d against origin/dev.

The independent red-team report is blocking. Deduplicated transaction-boundary findings:

  1. P1 — PATH-selected npm authority: src/cli/package-manager-ownership.ts:64-70 probes literal npm/npm.cmd; runNpmCommand at 50-57 later executes that ambient name. A PATH swap between probe and install redirects mutation.
  2. P1 — noncanonical npm root/prefix: src/cli/package-manager-ownership.ts:173-176 freezes raw command output, while src/cli/update-worker.ts:68-71 compares raw strings. Symlink/case normalization can invalidate the claimed package target.
  3. P1 — ambient environment is copied into the controller: ownership construction at src/cli/package-manager-ownership.ts:140,176 spreads the full process environment, carrying arbitrary npm config/PATH/registry authority instead of the frozen allowlist contract.
  4. P1 — Bun discovery remains ambient: resolveBunGlobalBin at 89-97 runs bare bun/bun.exe; the subsequent custom-bin proof therefore starts from PATH-selected manager authority.
  5. P1 — Bun is given an npm prefix contract: src/cli/update.ts:215-217 and src/cli/update-worker.ts:28-30 pass --prefix ownership.npmPrefix to Bun; the value comes from dirname(packageRoot) at ownership line 140, not queried Bun global configuration.
  6. P1 — deferred payload integrity is insufficient: src/cli/update.ts:491-497 serializes a predictable payload in a user-writable log tree, and src/cli/update-worker.ts:86-108 trusts its deserialized command/root/environment after parent exit. No exclusive no-follow staging, identity binding, or trusted worker/payload relationship prevents same-user replacement.
  7. P1 — worker post-update verification does not prove the selected owner still owns the result: src/cli/update-worker.ts:97-102 validates package path then runs setup, but does not revalidate the manager/bin/root evidence after replacement before success.
  8. P1 — controller/install contract is incompletely normalized: legacy npm paths at src/cli/update.ts:195-212 remain a mutation-capable public route outside the script-suppressed frozen transaction, creating divergent behavior for callers/tests.
  9. P2 — retry/status text still advertises ambient commands: src/cli/update.ts:513-547 tells users to run bare npm/bun and omx, which conflicts with a fail-closed ownership transaction and can recreate cross-manager ambiguity.
  10. P2 — non-npm/non-Bun ownership lacks explicit manager semantics: manager enumeration defaults to npm/Bun only at src/cli/package-manager-ownership.ts:163-178; pnpm/Yarn/shim layouts are not positively identified or diagnosed as unsupported ownership.

Do not merge. Repair by transaction boundary, manager semantics, and worker integrity; add deterministic regression coverage; then push a new head, run fresh CI, and request a fresh independent exact-head verdict.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Red-team transaction-boundary repairs are pushed at exact head 7d75ce0b6d13ebdc337e1ba46a7f91eb907b2f54.

The repair adds executable-backed/canonical manager authority, a minimal transaction environment, Bun configured-bin semantics without npm-prefix reuse, exclusive staged deferred-worker payload validation with pre/post ownership checks, fail-closed pnpm/Yarn diagnostics, and deterministic regression coverage.

Local verification on this head passed: focused update/postinstall tests, build, no-unused, lint, full npm test, and packed-install smoke. Fresh hosted CI is in progress; the old review remains superseded and merge is blocked pending new CI plus independent exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7d75ce0b6d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +67 to +68
const npmExecPath = process.env.npm_execpath;
if (!npmExecPath || !isAbsolute(npmExecPath)) return null;

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 Allow npm ownership outside lifecycle scripts

For a normal npm install -g user running omx update, the package bin is launched directly by Node, not from an npm lifecycle script, so process.env.npm_execpath is usually unset. This early return leaves resolvePackageManagerOwnership() with no npm candidate, and the native update path aborts before any explicit or passive update can run for the default supported npm install; resolve or persist the npm command without requiring lifecycle-only env.

Useful? React with 👍 / 👎.

Comment thread src/cli/update-worker.ts Outdated
if (result.error || result.status !== 0) throw new Error(String(result.stderr || result.error?.message || 'controller install failed'));
const cliEntry = await validateOwnership(payload.ownership);
if (!cliEntry) throw new Error('Frozen manager, package root, or bin ownership validation failed after update.');
const setup = spawnSync(process.execPath, [cliEntry, ...payload.setupArgs], { cwd: payload.cwd, env: payload.ownership.environment, stdio: 'inherit', windowsHide: true });

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 Preserve setup-specific environment in worker

When the deferred worker reaches setup refresh, this replaces the worker environment with the minimal ownership snapshot. The scheduler intentionally starts the worker with OMX_SKIP_NATIVE_AGENT_REFRESH=1, and users may also rely on CODEX_HOME; both are dropped here, so background updates can refresh native agents and/or write setup output to the default Codex home instead of the active one. Merge the setup guard and user config environment into the refresh process.

Useful? React with 👍 / 👎.

Comment thread src/cli/update-worker.ts Outdated
async function ownerOnlyStage(stage: string): Promise<boolean> {
try {
const stat = await lstat(stage);
return stat.isDirectory() && !stat.isSymbolicLink() && (stat.mode & 0o077) === 0 && (typeof process.getuid !== 'function' || stat.uid === process.getuid());

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 Do not reject Windows stages by POSIX mode bits

On Windows, Node does not model Unix owner/group/other permission bits in the way this POSIX check expects, so after the scheduler chmods the temp stage to 0700, stat.mode can still expose group/other bits and the worker rejects the staged transaction before installing. Gate this owner-only bit check to POSIX or use a Windows-appropriate ACL/owner validation so deferred updates can run on Windows.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent exact-head review of 7d75ce0b6d13ebdc337e1ba46a7f91eb907b2f54.

Fresh CI is green, but the terminal red-team review found a blocking ownership bootstrap regression: src/cli/package-manager-ownership.ts:66-106 requires lifecycle-only npm_execpath provenance to resolve manager executables. A normally launched Node-shebang omx does not have that lifecycle hint, so both ordinary npm and Bun global installs can fail ownership resolution before update despite valid executable/package-root evidence. This violates the core self-update contract and makes the successful-path tests fixture-specific.

Do not merge. The owning lane must provide a canonical, non-PATH, normal-launch executable authority source for npm and Bun, add normal-launch regression coverage, then repeat CI and independent review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b1cd7751d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/package-manager-ownership.ts Outdated

/** Only accept Bun provenance from the current runtime, lifecycle launcher, or configured install root, never PATH. */
export function resolveBunCommand(): string | null {
const candidates = [process.execPath, process.env.npm_execpath, process.env.BUN_INSTALL && join(process.env.BUN_INSTALL, 'bin', 'bun')]

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 Resolve the Windows Bun executable name

When OMX is launched under Node from a Bun-owned global install on Windows, neither process.execPath nor npm_execpath points at Bun, so the BUN_INSTALL fallback is the path that has to recover ownership. Bun’s Windows installer writes <BUN_INSTALL>\bin\bun.exe, but this candidate hard-codes bin/bun; realpathSync does not perform PATHEXT lookup, so Bun ownership is never established and omx update fails before the install. Add the .exe candidate on win32 here and in the installed-root fallback.

Useful? React with 👍 / 👎.

Comment thread src/cli/package-manager-ownership.ts Outdated

function transactionEnvironment(source: NodeJS.ProcessEnv | undefined): NodeJS.ProcessEnv {
const environment: NodeJS.ProcessEnv = {};
for (const key of ['HOME', 'PATH', 'TMPDIR', 'TEMP', 'TMP', 'SystemRoot', 'USERPROFILE']) {

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 Preserve Bun global-dir environment

For Bun installs that set BUN_INSTALL_GLOBAL_DIR or BUN_INSTALL_BIN to move global packages/bins, ownership is resolved using the current environment but the frozen transaction drops those variables here. The later bun pm bin -g validation and bun add --global then run with Bun’s default global locations instead of the frozen bunGlobalBin/packageRoot, causing deferred updates to fail validation and immediate updates to install somewhere other than the owned install. Include Bun’s documented global install env overrides in the transaction environment.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent current-head review for 4b1cd7751db49928f3c21863e7be34df19b8bb4b.

Fresh CI is green, but the review remains blocking: the current transaction still has unresolved ownership/deferred-worker/Windows/script-suppression contract gaps in the 14-file diff. The normal-launch fix addressed one prior P1 only; it does not establish a terminal MERGE_READY result for this head. Do not merge. The owning lane must preserve the full current review findings, repair the remaining transaction boundary defects, and repeat fresh CI plus an independent exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7404931a33

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/update.ts Outdated
}
return ownership?.manager === 'bun'
? { ok: false, stderr: 'Bun dev updates are not yet supported' }
: runDevGlobalUpdate(spawnProcess, resolvedPlatform);

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 Use the frozen npm owner for dev updates

When omx update --dev runs from a validated npm-owned install, this branch drops the ownership transaction and runDevGlobalUpdate falls back to PATH-based npm commands, including a global install without the frozen --prefix or environment. On machines with a different npm earlier in PATH or a custom global prefix, the dev tarball can be installed into the wrong global root even though ownership was just validated; pass the frozen npm command/prefix into the dev install path or fail closed.

Useful? React with 👍 / 👎.

Comment thread src/cli/update-worker.ts
if (result.error || result.status !== 0) throw new Error(String(result.stderr || result.error?.message || 'controller install failed'));
const cliEntry = await validateOwnership(payload.ownership);
if (!cliEntry) throw new Error('Frozen manager, package root, or bin ownership validation failed after update.');
const setup = spawnSync(process.execPath, [cliEntry, ...payload.setupArgs], { cwd: payload.cwd, env: { ...payload.ownership.environment, [SKIP_NATIVE_AGENT_REFRESH_ENV]: '1' }, stdio: 'inherit', windowsHide: true });

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 Persist install stamps after deferred updates

When the deferred worker succeeds here, the install was invoked with --ignore-scripts, so the package postinstall that normally refreshes install-state.json never runs, and this worker does not write the immediate-update stamp after setup completes. After a background update, installed_version and setup_completed_version remain on the old version, leaving later setup/version checks to operate on stale metadata despite a successful update; write the same success stamp before exiting.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d4ce5e8686

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/package-manager-ownership.ts Outdated
const executable = dependencies.realpath(dependencies.currentExecutable);
const packageRoot = dependencies.realpath(dependencies.currentPackageRoot);
const shimName = dependencies.platform === 'win32' ? 'omx.cmd' : 'omx';
if (dependencies.realpath(join(bunGlobalBin, shimName)) !== executable) return null;

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 Avoid rejecting Windows Bun command shims

For Windows Bun-owned installs where omx is launched through an omx.cmd shim, currentExecutable is the JS entry under install/global/node_modules, while join(bunGlobalBin, 'omx.cmd') is the shim file itself. This equality therefore returns null even after a Bun command is found, so resolvePackageManagerOwnership() fails and both passive and explicit Bun updates abort before install. Validate the shim target, or accept the package-root evidence, instead of requiring the .cmd file to realpath to the JS entry.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8a15276987

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

if (path.basename(packageRoot) !== 'oh-my-codex' || !isPathWithin(executable, packageRoot, dependencies.platform)) return null;
if (dependencies.platform === 'win32') {
// A Windows .cmd shim is a separate file, so its canonical location is the ownership evidence.
if (!isPathWithin(shim, bunGlobalBin, dependencies.platform)) return null;

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 Require Windows Bun shims to prove the current package

When BUN_INSTALL exists on Windows, this branch treats any omx.cmd located in Bun's global bin as sufficient ownership evidence; it no longer ties that shim to currentExecutable nor requires packageRoot to be under <BUN_INSTALL>\install\global\node_modules. A user running a local checkout or another install named oh-my-codex while ~\.bun\bin\omx.cmd exists will be classified as Bun-owned, so omx update updates Bun's global package but refreshes/stamps the local package. Require the package root to match the Bun global install layout or validate the .cmd target before returning ownership.

Useful? React with 👍 / 👎.

Comment thread src/cli/update.ts Outdated
const canonicalStage = realpathSync(stage);
const canonicalPayload = realpathSync(payloadPath);
const payloadStat = lstatSync(payloadPath);
if (canonicalPayload !== join(canonicalStage, 'transaction.json') || payloadStat.isSymbolicLink() || !payloadStat.isFile() || (payloadStat.mode & 0o077) !== 0) throw new Error('Deferred update payload is not a canonical owner-only staged file.');

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 Gate the payload mode-bit check on POSIX

Separate from the worker validation, scheduling a deferred update on Windows still reaches this POSIX mode-bit check immediately after creating transaction.json. Node cannot reliably represent owner/group/other bits on Windows, so a file created with mode: 0o600 can still report group/other bits and this throws before the worker is spawned; gate the (mode & 0o077) test to POSIX or use Windows ACL validation.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f109a17ca4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/package-manager-ownership.ts Outdated

function transactionEnvironment(source: NodeJS.ProcessEnv | undefined): NodeJS.ProcessEnv {
const environment: NodeJS.ProcessEnv = {};
for (const key of ['HOME', 'PATH', 'TMPDIR', 'TEMP', 'TMP', 'SystemRoot', 'USERPROFILE', 'BUN_INSTALL']) {

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 Preserve npm prefix config in frozen env

When npm ownership is resolved, npmPrefix is computed with the caller's full environment, but this snapshot drops env-only npm prefix settings such as PREFIX or npm_config_prefix. The deferred worker later validates with npm prefix -g using only ownership.environment, so installs that were proven under a custom env prefix fall back to npm's default prefix and the worker rejects the transaction before updating; preserve the prefix config or validate with the frozen prefix.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent exact-head review for f109a17ca43b441b43069c7f759473998589ff37.

REST CI is terminal green, but the current ownership transaction is not merge-ready. The review found remaining standard Windows npm/deferred compatibility gaps, Bun installation authorization/target ambiguity, and deferred setup/finalization boundary defects. Do not merge. Repair the remaining manager/deferred transaction semantics in the owning lane, add deterministic regressions, then run fresh CI and another current-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8ee7c1eb1c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/package-manager-ownership.ts Outdated
const path = platformPath(dependencies.platform);
const packageRoot = dependencies.realpath(dependencies.currentPackageRoot);
if (path.basename(packageRoot) !== 'oh-my-codex') return null;
const script = dependencies.realpath(path.join(path.dirname(packageRoot), 'npm', 'bin', 'npm-cli.js'));

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 Resolve npm CLI from the Windows Node install

On the standard Windows npm layout, npm documents global packages under {prefix}\node_modules (npm folders), so a normal omx.cmd launch has packageRoot like %AppData%\npm\node_modules\oh-my-codex and no npm_execpath; the npm CLI that shipped with Node is under the Node installation, not the package-root sibling constructed here. The new fallback still hard-codes that sibling path, so realpath fails before npm root -g/prefix -g can run and omx update reports an ownership failure for supported Windows npm installs; derive or persist the npm CLI from the Node install instead.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review of exact head 8ee7c1eb1c5938a129cdaf1dbfea95b329496859.

REST CI is terminal green, but the current review found three HIGH transaction/Windows defects and one MEDIUM provenance defect. Frozen commands, script suppression, Node setup refresh, deferred finalization, and Bun-dev fail-closed behavior improved materially, but the remaining defects still block merge. Preserve all findings, repair transaction/Windows/provenance behavior in the owning lane, add deterministic regressions, and repeat fresh CI plus an exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2fe6ec8e18

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/cli/package-manager-ownership.ts Outdated
if (!resolved.currentExecutable) return null;
const stamp = await resolved.readInstallStamp();
const managers: PackageManager[] = stamp?.package_manager ? [stamp.package_manager] : ['npm', 'bun'];
if (!stamp?.package_manager && hasAmbiguousBunShimEvidence(resolved)) return null;

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 Allow unstamped Bun installs to prove ownership

For a POSIX Bun global install that has no package_manager stamp yet (fresh installs, or users upgrading from older stamps), Bun's global omx shim realpaths to the current package entry, so this guard returns null before the later Bun resolver can validate BUN_INSTALL, the package root, and the Bun command. Since runPostinstall only preserves an existing manager stamp and does not infer Bun on first install, these otherwise valid Bun-owned installs can never pass ownership resolution for omx update unless the user manually edits the stamp; let the Bun candidate be evaluated and rely on the single-candidate check instead of treating matching shim evidence as an immediate ambiguity.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review of exact head 2fe6ec8e184a07fe95605c06908682251cfd77de.

REST CI is terminal green, but the current head remains non-merge-ready. The review acknowledges material improvements to command freezing, script suppression, Node-only setup refresh, deferred finalization, manifest validation, and Bun-dev fail-closed behavior, while retaining blocking transaction/provenance compatibility findings. Do not merge. Preserve all findings, repair with deterministic regressions, then repeat fresh CI and an independent exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review of exact head eaae64b95ef1d71bf3b9d7ef15803f3c44a48bac.

REST CI is terminal green, and the review confirms improvements to command freezing, script suppression, Node setup refresh, deferred finalization, manifest checks, and Bun-dev fail-closed behavior. Core transaction/provenance compatibility findings remain blocking. Do not merge. Preserve and repair the terminal findings with deterministic coverage, then repeat fresh CI and an independent exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4f31c698da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


function transactionEnvironment(source: NodeJS.ProcessEnv | undefined): NodeJS.ProcessEnv {
const environment: NodeJS.ProcessEnv = {};
for (const key of ['CODEX_HOME', 'HOME', 'PATH', 'TMPDIR', 'TEMP', 'TMP', 'SystemRoot', 'USERPROFILE', 'BUN_INSTALL']) {

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 Preserve boxed OMX roots for setup refresh

When an update runs from a session that set the documented OMX_ROOT/OMX_STATE_ROOT (README.md:170-192), this allowlist drops those variables, and both immediate and deferred setup refreshes spawn the updated CLI with only ownership.environment. setup() then creates omxStateDir/omxPlansDir from omxRoot(projectRoot), so the refresh falls back to <cwd>/.omx and can create or update state in the wrong checkout instead of the active boxed root; include the boxed root variables in the frozen environment.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review for exact head 4f31c698da9b99b714f5c4b72575f0991c8f2a94.

The review confirms improvements to frozen commands, script suppression, Node-only setup refresh, deferred finalization, and Bun-dev fail-closed behavior. Seven P1 ownership-resolution findings remain, including supported Windows npm installs and fresh Bun installs failing ownership resolution, plus Windows deferred transaction compatibility gaps. Do not merge. Repair only concrete transaction findings with deterministic regressions, then require fresh CI and a new exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review of exact head f72f381862a8f53e02c9b43a606cf7cf4a37068f.

REST CI is terminal green. The review confirms the branch is materially safer: commands are mostly frozen, stable/global installs suppress lifecycle scripts, Bun dev fails before effects, setup remains on Node, and the deferred worker has integrity fingerprints. Remaining ownership/transaction defects still block merge. Do not merge; preserve the terminal findings, repair with deterministic regressions, then repeat fresh CI and exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9bd6708c23

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const packageRoot = dependencies.realpath(path.join(root, 'oh-my-codex'));
const currentPackageRoot = dependencies.realpath(dependencies.currentPackageRoot);
const executable = dependencies.realpath(dependencies.currentExecutable);
return pathsEqual(packageRoot, currentPackageRoot, dependencies.platform) && isPathWithin(executable, packageRoot, dependencies.platform) ? packageRoot : null;

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 Reject npm-linked roots before installing

When the global oh-my-codex entry is a symlink to a checkout outside npm root -g (for example npm link), this returns the checkout realpath as npm-owned because it only checks that the running executable is inside the symlink target. The later frozen validator requires the package root to be under globalInstallRoot, so deferred updates are scheduled but the worker aborts, while immediate updates run npm install -g first and then fail setup refresh against the stale outside package root. Reject package roots outside the global root during ownership resolution.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review of exact head 9bd6708c23259e0a4a54cc39ef7a3b937a5b7d87 against origin/dev 299f31ad824409e5cae0015d982de41aa1b09552.

The exact-head Actions run 29931889413 is green, but the independent review remains blocking. Do not merge. Preserve the complete review findings and repair only concrete ownership/deferred transaction defects with deterministic tests before a fresh CI and terminal exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9fa1ae9623

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


function transactionEnvironment(source: NodeJS.ProcessEnv | undefined): NodeJS.ProcessEnv {
const environment: NodeJS.ProcessEnv = {};
for (const key of ['CODEX_HOME', 'HOME', 'PATH', 'TMPDIR', 'TEMP', 'TMP', 'SystemRoot', 'USERPROFILE', 'BUN_INSTALL']) {

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 Preserve npm registry/proxy configuration

When a user relies on env-only npm configuration such as npm_config_registry, npm_config_userconfig with token substitution, or proxy/CA settings, this allowlist strips those variables before the frozen npm install/validation because the child process env is exactly ownership.environment. The previous updater inherited process.env, so these supported setups now make omx update contact the wrong registry or fail authentication/proxy setup; preserve the selected manager's registry/network config in the frozen transaction instead of reducing it to this list.

Useful? React with 👍 / 👎.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent terminal review of exact head 9fa1ae96236649cec71885154eb6ffd439ca7d85 against origin/dev 299f31ad824409e5cae0015d982de41aa1b09552.

Exact-head CI run 29934128715 is green. The independent review remains blocking. Do not merge. Preserve the terminal ownership/deferred transaction findings and repair them with deterministic regressions before fresh CI and an exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Repair update for exact head 280963da537a32ae06ff45c331c0588296ae62fb.

The deferred payload boundary now rejects malformed, incomplete, noncanonical-filename, and foreign owner-only staging inputs without deleting unrelated owner-only state. Scheduler namespace and canonical transaction filename checks are covered by deterministic regressions.

Local verification passed: build, focused update ownership suite (76/76), no-unused typecheck, and lint. The prior 9fa REQUEST_CHANGES remains review history. Fresh hosted CI and a new independent exact-head review are required before merge.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent exact-head review for 280963da537a32ae06ff45c331c0588296ae62fb.

Three P1 findings block merge:

  1. Resolve npm from the selected installed OMX package/root, not only a sibling CLI assumption.
  2. Discover and freeze Bun’s configured global package root before mutation; bin-shim evidence alone is insufficient to bind the update target.
  3. Revalidate the Bun global shim after installation before setup/provenance publication.

REST CI is green but does not override these ownership invariants. Repair all three with deterministic regressions, then require fresh CI and a fresh exact-head review.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

REQUEST_CHANGES — independent exact-head red-team review for 9ced9f0dc083038b50b9d5229389eab230536baf against origin/dev.

Hosted CI is terminal green and local verification is clean, but one P1 ownership regression blocks merge.

Verification evidence (exact head, this worktree)

  • git rev-parse HEAD = 9ced9f0dc083038b50b9d5229389eab230536baf; worktree clean (no tracked-file edits).
  • Fresh npm ci (lockfile-preserved, 151 packages) + npm run build from exact head: exit 0.
  • Focused compiled suites: dist/cli/__tests__/update.test.js 77/77 pass, dist/scripts/__tests__/postinstall.test.js 8/8 pass.
  • Deterministic runtime repro against freshly built dist (dependency-injected, no repo files touched): 5/5 scenarios confirm the wedge below.
  • The three prior P1 blockers from the 280963da review are addressed at this head (npm resolves from the selected installed package/root incl. the Windows Node-layout fallback; Bun's configured global root/bin is frozen pre-mutation via bun pm bin -g under the frozen environment; validatePackageManagerOwnership revalidates root/bin/shim after install before setup refresh and stamp publication in both the immediate path and the deferred worker).

P1 — stale Bun provenance in the install stamp permanently wedges self-update after a supported manager switch

Contract locations (exact head):

  1. src/scripts/postinstall.ts:88-90runPostinstall writes the bumped stamp preserving existingStamp.package_manager unconditionally, with no check that the current lifecycle's manager agrees with the stamped one.
  2. src/cli/package-manager-ownership.ts:351const managers = stamp?.package_manager ? [stamp.package_manager] : ['npm', 'bun'] narrows probing to only the stamped manager.
  3. src/cli/package-manager-ownership.ts:375return candidates.length === 1 ? candidates[0]! : null returns null when the stamped manager's evidence contradicts the current install; there is no fallback probe of the other supported manager.

Reproduction sequence (proven deterministically against fresh dist):

  1. bun install -g oh-my-codex, then a successful omx update --stable writes package_manager: "bun" into the user install stamp.
  2. User switches managers: npm install -g oh-my-codex (a supported, documented path). The npm lifecycle postinstall hits postinstall.ts:88-90 and carries package_manager: "bun" into the new stamp. (Runtime proof: npm-lifecycle runPostinstall with an existing Bun stamp wrote package_manager: "bun".)
  3. omx update from the now npm-owned install reads the stale stamp, probes only Bun (:351), Bun evidence correctly rejects the npm-owned package root, and resolution returns null (:375). In the wedged scenario the npm probe spies recorded zero calls — npm ownership was fully resolvable (control scenario with an identical machine and an unstamped install resolves manager: "npm") yet was never attempted. The wedge persists identically after Bun is fully removed (no BUN_INSTALL, no bun binary).
  4. Every omx update (immediate, deferred, and passive launch-time) fails closed at the ownership gate forever.

Recovery-path audit (all dead ends):

  • No OMX_UPDATE_PACKAGE_MANAGER override or --package-manager flag exists (issue-proposed, not implemented).
  • The emitted guidance at package-manager-ownership.ts:381 — "Reinstall OMX globally with one supported package manager, then retry" — re-wedges: a same-version npm reinstall returns noop-same-version and never rewrites the stamp; a bumped npm reinstall preserves the stale Bun provenance per step 2.
  • The only recovery is manually deleting or hand-editing the stamp file — undocumented, and the error text never mentions it.

Impact: after a legitimate, supported npm↔Bun manager switch, the affected installation can never self-update again, and OMX's own recovery instruction actively reproduces the wedge. Failure being closed prevents cross-install damage but does not make permanent loss of the self-update capability acceptable — this is a core ownership regression in the exact workflow this PR exists to protect.

Required fix:

  1. Rewrite or clear package_manager in the stamp when a legitimate manager replacement is detected — e.g. runPostinstall must not carry provenance that contradicts the current lifecycle evidence (npm_execpath/user-agent), and/or resolvePackageManagerOwnership must validate the stamped manager against current executable/package-root evidence and safely fall back to probing the remaining supported manager(s) before returning null (fail-closed only when both probes fail or both validate).
  2. Deterministic manager-switch regression tests covering both directions: Bun-stamped install replaced by npm (must resolve npm or re-probe, never wedge) and npm-stamped install replaced by Bun, including the same-version reinstall (noop-same-version) path and the bumped-reinstall path, plus a test asserting the recovery guidance does not recommend a self-defeating action.

Repair this finding with deterministic regressions, then require fresh CI and a fresh exact-head review. Do not merge at 9ced9f0d.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bfccdb0f09

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const path = platformPath(dependencies.platform);
const bunInstallRoot = dependencies.realpath(dependencies.bunInstallRoot);
const globalInstallRoot = dependencies.realpath(path.join(bunInstallRoot, 'install', 'global', 'node_modules'));
const packageRoot = matchesCurrentInstall(globalInstallRoot, dependencies);

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 Reject Bun-linked package roots before updating

When oh-my-codex is registered with bun link, Bun creates <BUN_INSTALL>/install/global/node_modules/oh-my-codex as a symlink to the checkout and the global omx shim resolves into that outside tree; I confirmed this with bun link after checking bun link --help describes registering the current directory as a linkable package. matchesCurrentInstall follows that symlink and returns the external checkout here, but resolveBunOwnership never requires the returned packageRoot to remain under globalInstallRoot, so immediate omx update can run bun add --global against the Bun global install and only fail later during setup validation against the linked checkout. Reject Bun-linked roots during ownership resolution, like the later validator already does, before any install is spawned.

Useful? React with 👍 / 👎.

@Yeachan-Heo Yeachan-Heo left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

MERGE_READY — independent exact-head red-team review for bfccdb0f09cea366e686c58bdce73fdefe5519b5 against origin/dev 299f31ad824409e5cae0015d982de41aa1b09552 (merge-base == origin/dev; 20 linear commits).

Hosted CI at exact head is terminal green (runs 29940854714, 29940851946) and is treated as evidence, not proof. Independent local verification at the exact head is also clean, and the 9ced9f0d stale-Bun-stamp wedge is verifiably fixed in both directions.

Verification evidence (exact head, this worktree)

  • git rev-parse HEAD = bfccdb0f09cea366e686c58bdce73fdefe5519b5; worktree clean (no tracked-file edits).
  • Fresh npm ci (lockfile-preserved) + npm run build: exit 0.
  • Focused compiled suites: dist/cli/__tests__/update.test.js 79/79 pass, dist/scripts/__tests__/postinstall.test.js 8/8 pass.
  • npm run check:no-unused: exit 0. npm run lint (biome, 778 files): clean.
  • Deterministic runtime repro against freshly built dist (dependency-injected, no repo files touched): 10/10 scenarios pass, including one real Bun 1.3.14 end-to-end lane.

Prior blocker disposition

9ced9f0d P1 (stale Bun stamp → permanent self-update wedge) — FIXED.

  • package-manager-ownership.ts:351-352 now treats the stamp as probe priority only (bun stamp → ['bun','npm'], else ['npm','bun']); every candidate requires live ownership proof and resolution still returns null unless exactly one manager validates. Runtime proof: stale Bun stamp + npm-owned install resolves npm (including with Bun fully removed: no BUN_INSTALL, no bun binary); stale npm stamp + Bun-owned install resolves bun. The npm probe spy records the independent live validation the 9ced review found suppressed.
  • postinstall.ts no longer carries existingStamp.package_manager into a bumped stamp, so a legitimate bumped manager replacement clears contradicting provenance instead of perpetuating it (runtime proof: npm-lifecycle bump over a Bun stamp writes package_manager: undefined while preserving setup_completed_version).
  • Recovery guidance no longer re-wedges: the same-version reinstall (noop-same-version) leaves the stamp untouched but resolution recovers on live evidence; the bumped reinstall clears the stamp and resolves. Both paths were executed to a resolvable owner in the repro.

280963da three P1s — intact at this head. npm resolves from the selected installed OMX package/root (incl. the Windows Node-layout fallback, resolveInstalledNpmCommand); Bun's configured global root/bin is frozen pre-mutation via bun pm bin -g under the frozen transaction environment; validatePackageManagerOwnership revalidates root/bin/shim after install before setup refresh and stamp publication in both the immediate path and the deferred worker (target-proof: a missing/mismatched shim fails revalidation against the live Bun layout).

Deferred payload/transaction integrity (280963da) — intact. Worker self-digest, payload digest, canonical transaction.json staging, owner-only omx-update- stage namespace, and foreign owner-only directory preservation are all covered by deterministic compiled regressions (5/5 worker tests pass) and re-read at this head.

Adversarial target results

  • Fallback uniqueness: a stamped-manager mismatch falls back only when the alternate manager uniquely validates the exact current package root; both-validate stays fail-closed for unstamped/npm/bun stamps (3/3 in repro), no-owner fails closed.
  • Symmetry: Bun→npm and npm→Bun manager switches both recover without stamp deletion or manual intervention.
  • Windows: win32 path canonicalization (platformPath, case-insensitive pathsEqual), .cmd shim containment semantics, and npm CLI selection from the selected root before the Node-runtime sibling are covered by deterministic tests; POSIX shim requires canonical equality with the installed CLI entry.
  • Bun dev: rejected before any effect with the owner-approved unsupported message; no spawn occurs.
  • npm CLI selection: lifecycle npm_execpath is canonicalized and executed through process.execPath; normal-launch resolution derives npm from the selected installed root and fails closed when no npm CLI exists there.
  • Tests reflect implementation: the adjusted assertions (probe-priority stamp, independent live npm validation, cleared bump provenance, runNpmCommand contract regex) match the shipped semantics rather than the pre-fix narrowing assumption.

Residual notes (non-blocking)

  • A permanently stale stamp (same-version reinstall noop) persists but is now cosmetic: it only orders probes and never authorizes or suppresses a manager. A subsequent successful update rewrites correct provenance.
  • Bun ownership detection requires BUN_INSTALL (or Bun executable provenance) in the environment; without it the path fails closed with the actionable reinstall guidance, which now actually recovers. This is the accepted fail-closed trade-off, not a cross-install risk.
  • PR body still says postinstall "preserves an existing Bun owner stamp"; the head now clears stale provenance on bumped replacements. Description drift only — the code behavior is the correct one.

Evidence denominator: exact-head worktree + fresh npm ci/build + 87 focused compiled tests (79 update + 8 postinstall) + no-unused + lint + 10/10 runtime manager-switch repro scenarios (1 with real Bun 1.3.14) + exact-head hosted CI green. Merge is unblocked at bfccdb0f09cea366e686c58bdce73fdefe5519b5.


[repo owner's gaebal-gajae (clawdbot) 🦞]

@Yeachan-Heo
Yeachan-Heo merged commit 8f76c22 into dev Jul 22, 2026
20 checks passed
@Yeachan-Heo
Yeachan-Heo deleted the feat/issue-3257-bun-update-owner branch July 22, 2026 17:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant