* 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:
@@ -603,13 +603,19 @@ function resolveModelInternal(cwd: string, agentType: string): string {
|
||||
// #2517 `runtime:"codex"` + resolve_model_ids:"omit" -> the codex tier
|
||||
// model. "Explicit non-Claude opt-in wins" — the operator naming a
|
||||
// runtime in the project config outranks an omit.
|
||||
// #2297 GSD_RUNTIME/marker = codex + a GLOBAL (defaults-poisoned) omit
|
||||
// -> "" (acceptance #4). A merely DETECTED runtime does not
|
||||
// constitute that opt-in, so the omit still wins.
|
||||
// #4717 (user-sanctioned decision a — supersedes #2297 acceptance #4):
|
||||
// a DETECTED runtime now constitutes that opt-in too. The identity
|
||||
// fill in config-loader materializes GSD_RUNTIME / the per-install
|
||||
// marker into `config.runtime` when it is empty, so a genuinely
|
||||
// installed non-Claude runtime resolves its own tier map instead of
|
||||
// the omit's "". The shared "omit" remains a Claude protection:
|
||||
// marker/env = claude still ignores it (native aliases), and
|
||||
// garbage runtime values still fail safe to "" (guard below).
|
||||
//
|
||||
// So the opt-in signal is specifically the `runtime` KEY, not the resolved
|
||||
// runtime: step 3 reads the active runtime's tier map, but only outranks the
|
||||
// omit gate when the operator wrote that key. Both contracts hold unchanged.
|
||||
// So the opt-in signal is the `runtime` KEY — written by the operator or
|
||||
// materialized by the #4717 fill: step 3 reads the active runtime's tier
|
||||
// map, but only outranks the omit gate when that key canonicalizes to a
|
||||
// recognised non-Claude runtime.
|
||||
const omitApplies = config['resolve_model_ids'] === 'omit'
|
||||
&& (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(activeRuntime));
|
||||
// CANONICALIZED, not the raw field. Comparing the raw value against the literal
|
||||
|
||||
Reference in New Issue
Block a user