Skip to content

test(model-registry): hermeticize ambient-credential-sensitive fixtures - #4584

Merged
Yeachan-Heo merged 1 commit into
devfrom
fix/issue-4582-model-registry-dev-red
Aug 15, 2026
Merged

test(model-registry): hermeticize ambient-credential-sensitive fixtures#4584
Yeachan-Heo merged 1 commit into
devfrom
fix/issue-4582-model-registry-dev-red

Conversation

@Yeachan-Heo

@Yeachan-Heo Yeachan-Heo commented Aug 15, 2026

Copy link
Copy Markdown
Owner

Fixes #4582

Summary

All six reported failures in packages/coding-agent/test/model-registry.test.ts are fixture non-hermeticity, not a product regression: the six tests read ambient host credentials through registry.getAll() / registry.getActiveProviders(), so provider credentials exported by the launching shell (OPENAI_API_KEY, GH_TOKEN/GITHUB_TOKEN, ANTHROPIC_OAUTH_TOKEN) change which bundled providers are credential-active and change the resolution outcome. In a clean credential environment all 269 tests in the file pass on exact dev (45885ea287 then 804314081f), and CI's shard harness scrubs credential env — which is why the failures reproduce on a developer checkout but not in CI.

Exact reproduction of the reported six on unmodified dev 804314081f (fresh HOME, scrubbed env, plus only OPENAI_API_KEY + GH_TOKEN):

(fail) keeps credentialless discovery active with an irrelevant dangling selector
(fail) uses stored credentials for OpenAI-compatible providers without inline auth
(fail) alias A cannot select a sibling variant that only has alias B in the same record
(fail) fails closed for known aliases with no eligible variants
(fail) preserves full model and wire ids when resolving via alias
(fail) resolves final-slash-segment aliases through canonical records

Mechanism, per pair:

  • Four alias/provider-selection testsGH_TOKEN/GITHUB_TOKEN makes bundled github-copilot models available; resolveModelByLookupAlias("claude-sonnet-4.5", { candidates: registry.getAll() }) then legitimately ranks the copilot variant ahead of the demo fixture (sample failure: expected demo, received github-copilot). The fail-closed test stops failing closed for the same reason — an unrelated provider's variant becomes eligible through getAll().
  • Two credential/discovery tests — ambient OPENAI_API_KEY makes bundled openai credential-active, so getActiveProviders() returns an extra { provider: "openai", connectionKind: "credential" } row and the exact toEqual assertions break.

This is the same defect class #3207 fixed for the canonical-equivalence cases ("host provider credentials … cannot change sticky/bound/child resolution").

Change

Test-only, in packages/coding-agent/test/model-registry.test.ts (+63/−21):

No assertion is deleted or weakened. Fail-closed alias behavior, alias isolation, canonical/wire-id preservation, final-slash-segment aliases, stored credentials for OpenAI-compatible providers, and credentialless discovery under an irrelevant dangling selector all keep their original contract strength — they now assert it against a pinned fixture universe instead of the host's ambient credential state.

Known residual (documented in the commit, not worked around): an ambient ANTHROPIC_API_KEY cannot be suppressed in-process because $inheritedEnv pins the launch-time value; ranks expired OAuth ahead of an environment key still depends on a clean ANTHROPIC_API_KEY. The CI harness scrubs *_API_KEY, so CI is unaffected; a subprocess-based fixture would be needed to cover that locally.

Reproduction before/after (exact heads, base 804314081f → head a2f3ac3b3e)

Environment (fresh HOME, env -i) Base 804314081f (before) Head a2f3ac3b3e (after)
Clean 269 pass / 0 fail 269 pass / 0 fail
+ OPENAI_API_KEY + GH_TOKEN 6 fail (the reported six) 269 pass / 0 fail
+ GH_TOKEN alone 6 fail (the same six) 269 pass / 0 fail
+ each of GITHUB_TOKEN, ANTHROPIC_OAUTH_TOKEN, GEMINI_API_KEY, XAI_API_KEY, OPENAI_API_KEY alone 2–12 fails incl. the six 269 pass / 0 fail

Verification (head a2f3ac3b3e, base 804314081f)

  • bun test packages/coding-agent/test/model-registry.test.ts — 269 pass clean; 269 pass under the reporter's pollution signature; 269 pass under each individual polluter
  • Adversarial mask-check on the polluted matrices: base failures change exactly as the mechanism predicts per polluter (e.g. GH_TOKEN alone → the six; ANTHROPIC_OAUTH_TOKEN alone → the six minus the alias quartet plus excludes bundled providers… and ranks expired OAuth…), and the head is 0-fail in every matrix — the pinning repairs the fixtures without deleting or weakening any assertion (diff is test-file-only, +63/−21, model-registry.ts untouched)
  • Adjacent suites on head, clean and polluted: model-registry-runtime-provider 20/0, model-resolver 9/0, provider-ranking 10/0, provider-ranking-surfaces 5/0, provider-auth-health 8/0, model-selector-profiles 11/0, provider-onboarding 28/0, oauth-discovery 6/0, startup-auth-config 13/0
  • bun --cwd=packages/coding-agent run check (biome + tsc) — clean
  • bun run --cwd=packages/coding-agent build — clean; worktree contains only the intended file
  • bun scripts/verify-gjc-state-writers.ts --fail --root . — 0 violations
  • Remaining operator-shell sensitivity (two OPENAI_BASE_URL tests + keeps signed models.dev descriptor rows out of the model cache) is identical on base and head and caused by deliberate product behavior: $inheritedEnv pins launch-time credential/base-url values (packages/utils/src/env.ts), and ~/.gjc/agent/models.yml provider overrides legitimately win for baseUrl — host-config artifacts, out of scope for the reported six, not masked by this PR

merge-approved verdict/digest (promoted from needs-human)

Canonical question for the reviewer (probepark / HaD0Yun): the fix reclassifies the six failures as environment-dependent fixture sensitivity (ambient credential env vars) rather than a provider-selection implementation regression, and repairs them by pinning fixtures per the #3207 precedent — without changing model-registry.ts product code and without weakening any assertion. If the intended contract was instead that these resolutions must be invariant even against the ambient credential universe (i.e. alias resolution should never see host-enabled providers), that is a product-behavior change and this PR should be rejected in favor of one; the test-side pinning here matches the repo's existing precedent.

Promotion provenance: needs-humanmerge-approved at 2026-08-15T13:01:03Z from @probepark's exact-head APPROVED review of a2f3ac3b3e5e23e8bb5107e144426590ac3ed997 (repository write authority, non-author; review verifies the fail-today/green matrices and the unset/restore scoping, and corrects the original issue's diagnosis on the record). Digest unchanged — head and diff identical since the needs-human line was recorded. Product CI run 31884312121 terminal green.

gajae.pr-review-verdict.v1 merge-approved sha256:87748eacd90810a4e8299eb5bee4a7fef364df6ea03eebec15bd4436cf480e8a reviewer:human reviewer-id:probepark evidence:#4584 (review)


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

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Requesting independent exact-head review from @probepark (reporter) and @HaD0Yun. Head 75f86a3fcc, base 45885ea287 (exact current dev). The PR body carries the single canonical needs-human verdict/digest question: accept the fixture-hermeticity classification (no product change, no weakened assertions), or reject if the intended contract is ambient-credential invariance in product behavior.


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

Six model-registry tests read ambient host credentials through
registry.getAll()/getActiveProviders() and break whenever the launching
shell exports provider credentials (OPENAI_API_KEY, GH_TOKEN/GITHUB_TOKEN,
ANTHROPIC_OAUTH_TOKEN): bundled providers become credential-active and
either win alias resolution (expected demo, received github-copilot) or
add unexpected active-provider rows. All 269 pass in a clean environment,
so this is fixture non-hermeticity, not a product regression - the same
defect class #3207 fixed for the canonical-equivalence cases.

Pin alias/canonical candidate sets to the fixture providers and scope
active-provider row assertions to the fixture provider, matching the
established #3207 pattern. No assertion is deleted or weakened: the
fail-closed, alias-isolation, wire-id, final-segment, stored-credential,
and credentialless-discovery contracts all keep their original strength.
Also unset ANTHROPIC_OAUTH_TOKEN/ANTHROPIC_API_KEY in the two tests that
would otherwise race the fixture key against ambient anthropic
credentials. Ambient ANTHROPIC_API_KEY still cannot be suppressed
in-process ($inheritedEnv pins the launch-time value); the CI harness
scrubs credential env, so that residual case is documented here rather
than worked around.

Fixes #4582

Lore-id: 7c4e9a1f
Constraint: no assertion may be deleted or weakened silently
Rejected: deleting the six tests | masks the ambient-credential mechanism
Rejected: changing provider ranking in model-registry.ts | product behavior is correct
Confidence: high
Scope-risk: narrow
Reversibility: trivial
Tested: bun test model-registry.test.ts clean env 269/269; polluted (OPENAI_API_KEY+GH_TOKEN) 269/269; CI shard-4 harness 175 files; adjacent suites 206 pass
Not-tested: ambient ANTHROPIC_API_KEY suppression (in-process unfixable, CI-scrubbed)
@Yeachan-Heo
Yeachan-Heo force-pushed the fix/issue-4582-model-registry-dev-red branch from 75f86a3 to a2f3ac3 Compare August 15, 2026 12:16
@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Signed evidence — head moved to a2f3ac3b3e (base 804314081f, current dev); prior head 75f86a3fcc was stale vs dev and is superseded.

Rebase is a clean cherry-pick: same +63/−21 test-file-only diff on packages/coding-agent/test/model-registry.test.ts, identical canonical digest 87748eacd90810a4e8299eb5bee4a7fef364df6ea03eebec15bd4436cf480e8a (git diff --binary --full-index --no-ext-diff 804314081f...a2f3ac3b3e). No product code touched; model-registry.ts, env.ts, and every product path are byte-identical to dev.

Hermetic fail-today/green evidence on the exact pair (fresh HOME, env -i, so the operator's ~/.bashrc proxy exports and ~/.gjc/agent/models.yml cannot leak in):

Matrix (all vs the same file) Base 804314081f Head a2f3ac3b3e
Clean 269/0 269/0
OPENAI_API_KEY+GH_TOKEN (issue signature) 6 fail = the reported six 269/0
GH_TOKEN alone 6 fail (same six) 269/0
each of GITHUB_TOKEN / ANTHROPIC_OAUTH_TOKEN / GEMINI_API_KEY / XAI_API_KEY / OPENAI_API_KEY alone 2–12 fails incl. the six 269/0

Adversarial mask-check: base failure sets move exactly as the credential mechanism predicts per polluter (e.g. ANTHROPIC_OAUTH_TOKEN alone drops the alias quartet and adds excludes bundled providers… + ranks expired OAuth ahead of an environment key), and the head is 0-fail in every matrix while keeping every original assertion — pinning repairs the fixtures, it does not hide a provider-selection defect. Adjacent suites on head clean and polluted: model-registry-runtime-provider 20/0, model-resolver 9/0, provider-ranking 10/0, provider-ranking-surfaces 5/0, provider-auth-health 8/0, model-selector-profiles 11/0, provider-onboarding 28/0, oauth-discovery 6/0, startup-auth-config 13/0. bun --cwd=packages/coding-agent run check clean; build clean; bun scripts/verify-gjc-state-writers.ts --fail --root . 0 violations.

Replacement CI run for this head is 31884312121: its bootstrap parsed the single canonical verdict line and failed only on Verdict needs-human intentionally blocks merge — the honest pre-review state per CONTRIBUTING; product jobs are green (plan, all four gjc-state-gates, native-build in progress). Runs 31884205125 and 31884243813 read a pre-verdict/stale body and were superseded/cancelled — not product evidence.

@probepark @HaD0Yun — requesting fresh exact-head review of a2f3ac3b3e: the canonical question stands (fixture-hermeticity classification + no weakened assertions vs a product-behavior change for ambient-credential invariance). Bootstrap turns green only when a write-authorized non-author records merge-approved for this exact head; the body verdict will be promoted only from an actual approval.


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

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Product CI for exact head a2f3ac3b3e is terminal green on run 31884312121: all product jobs passed — test:packages/coding-agent/test/model-registry.test.ts, ts-build, native-build, Affected path validation (plan + evidence producer), gjc-state-gates (read/runtime/integrity/static + aggregate), and Virtual integration validation. The only red job is PR contract bootstrap, which is the contract working as designed: Verdict needs-human intentionally blocks merge until a write-authorized non-author approval exists for this exact head.

@probepark @HaD0Yun — the review is now the sole gate. Evidence for the fail-today/green claim is in the signed matrix comment: base 804314081f reproduces exactly the six reported failures under OPENAI_API_KEY+GH_TOKEN (fresh HOME, env -i), head is 269/0 under clean and every individual polluter, no assertion weakened, no product file touched.


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

@probepark probepark left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Approve at a2f3ac3b3 - and this corrects a wrong diagnosis of mine

I filed #4582 claiming six ModelRegistry tests were red on dev and framing it as provider
selection being "meaningfully unguarded", with a fail-open alias concern. This PR shows the real
cause, and my framing was wrong.

The evidence

Same machine, same command, only the head differs:

# base 804314081, unmodified
 263 pass  6 fail

# head a2f3ac3b3
 269 pass  0 fail

All six were the ones I reported. The cause is ambient host credentials leaking into fixtures - my
environment carries BROWSER_USE_LLM_API_KEY, CPA_LOCAL_API_KEY, JUNIE_API_KEY and friends, plus
state under ~/.gjc/agent/, so provider resolution legitimately preferred a credentialed provider
over the fixture's demo. That is the Expected: "demo" / Received: "github-copilot" signature I
quoted in the issue.

The fix is the right shape - scope the unset and restore it:

const restoreAnthropicKey = unsetEnvForTest("ANTHROPIC_API_KEY");
const restoreAnthropicToken = unsetEnvForTest("ANTHROPIC_OAUTH_TOKEN");
try {
  const registry = new ModelRegistry(authStorage, modelsJsonPath);
  await registry.refresh();
  expect(registry.getAll().some(model => model.provider === "anthropic")).toBe(true);
  expect(activeRowsFor(registry, ["anthropic"])).toEqual([]);
} finally {
  restoreAnthropicKey();
  restoreAnthropicToken();
}

try/finally around the restore matters: an assertion failure inside the block must not leave the
process with credentials unset for every later suite in the same run. Getting that wrong would trade
one ambient-state bug for a worse one.

The comment on the credentialless case states the invariant explicitly - ambient host credentials
enabling unrelated bundled providers must not affect that provider's discovery activity. That is the
property, written down where the next person will read it.

What I got wrong, on the record

The observation in #4582 was real and reproducible; the diagnosis was not. "Six red tests on dev"
was true on a developer machine and false on clean CI, and I asserted a product fail-open without
checking whether the environment was the variable. The tell was available and I missed it: a fixture
expecting demo receiving github-copilot is much more likely to be credential resolution finding
something real than an alias table silently rerouting.

I am closing #4582 with a correction pointing here.

Worth noting the underlying complaint was not baseless - non-hermetic tests are a genuine defect,
and this PR fixes it. But "the suite is environment-dependent" and "provider selection is unguarded"
are very different claims and I filed the wrong one.

merge-approved.

Reviewed by @probepark - method: ran the suite at base and head on the same credential-carrying machine to isolate the variable, inspected the unset/restore scoping for cross-suite leakage, confirmed the failure set matched the six reported in #4582.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Terminal receipt — #4584 merged, #4582 closed, lane retired.


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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants