fix(#4717): consult the per-install runtime marker at both identity seams (#4861)

* test(#4717): add failing-first coverage for the two runtime-identity marker seams

* fix(#4717): consult the per-install runtime marker at both identity seams

resolveReportedRuntime (agent_runtime) and loadConfigResolved
(config.runtime) both ignored the per-install .gsd-runtime marker that
resolveRuntime and the model-resolver gate already read. On a
multi-runtime machine (e.g. a globally exported CODEX_HOME), host sniffing
misreported every Claude Code session as codex, and a shared
defaults.json stamped by the first non-Claude install leaked its runtime
to every other one.

Seam 1: the reported-runtime ladder becomes explicit > install marker >
host detection > claude. Seam 2: loadConfigResolved fills an empty
config.runtime from GSD_RUNTIME then the marker, copy-on-write (the
builtin-defaults branch returns a shared object). Explicit runtimes and
marker-less trees are unchanged.

* fix(#4717): a marker-detected runtime opts into its tier map (decision a)

* fix(#4717): stamped-defaults leg, marker fail-safe, docs, review fold-ins

* chore(#4717): backfill changeset PR number (4861)

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-09-18 13:50:01 -04:00
committed by GitHub
parent 58c7bbb16a
commit 9a41a95212
10 changed files with 343 additions and 33 deletions

View File

@@ -16,7 +16,8 @@
import fs from 'node:fs';
import path from 'node:path';
import { resolveExplicitRuntime } from './runtime-slash.cjs';
import { resolveExplicitRuntime, readInstallRuntimeMarker } from './runtime-slash.cjs';
import { resolveRuntimeNameFromCandidates } from './runtime-name-policy.cjs';
import { CODEX_CONFIG_MARKER } from './update-context.cjs';
export { CODEX_CONFIG_MARKER };
@@ -139,6 +140,13 @@ export function resolveReportedRuntime(projectDir: string | null | undefined, de
function resolveReportedRuntimeUnsafe(projectDir: string | null | undefined, deps?: DetectionDeps): string {
const explicit = resolveExplicitRuntime(projectDir, deps?.env ?? process.env);
if (explicit) return explicit;
// #4717: the per-install marker names the runtime that owns THIS tree — a
// stronger signal than host sniffing, which misreports every session on a
// multi-runtime machine (e.g. a globally exported CODEX_HOME makes a Claude
// Code session read as codex). Marker-less trees (dev/source, pre-#2297
// installs) fall through to host detection unchanged.
const marker = resolveRuntimeNameFromCandidates(readInstallRuntimeMarker());
if (marker) return marker;
const detected = detectHostRuntime(deps);
if (detected.runtime) return detected.runtime;
return 'claude';