Skip to content

chore(models): refresh stale model defaults (NodeRoom) - #261

Draft
HomenShum wants to merge 3 commits into
mainfrom
claude/model-refresh-noderoom
Draft

HomenShum wants to merge 3 commits into
mainfrom
claude/model-refresh-noderoom

Conversation

@HomenShum

@HomenShum HomenShum commented Oct 14, 2026 •

Copy link
Copy Markdown
Owner

What

Refreshes NodeRoom's stale default model ids (Google deprecated gemini-3.5-flash 2026-10-08; OpenRouter drops google/gemini-2.5-* 2026-10-20; claude-sonnet-4 retired 2026-06-15; gemini-3.1-flash-tts-preview shuts down 2026-11-17) and fixes the request shapes the new ids reject. Env chains and caller arguments are unchanged: if prod sets AGENT_MODEL, AGENT_ORCHESTRATOR_MODEL, AGENT_TOP_PAID_MODEL, VOICE_GEMINI_TTS_MODEL etc., prod does not change - this PR moves code fallbacks only.

This PR is a draft and carries the needs-live-eval label. Both stay until the three merge blockers below are closed. The label is a block, not a note (SPEC A.0.3). Nothing in this PR made a paid provider call.

MERGE BLOCKERS (all three need a provider key; none was run here)

  1. L1-L4 ladder receipt gates the gemini-3.8-flash default (and the adaptiveRouter.ts:136 fast-lane literal). tests/qaMatrix.test.ts ("keeps the Convex interactive fallback on a ladder-safe route") fails on purpose until it exists: gemini-3.8-flash has no PASS row in docs/qa/production-matrix.json. The check is not weakened.

    LADDER_MAX_COST_USD=9 npm run ladder:real -- gemini-3.6-flash,gemini-3.8-flash --levels=1-4 --record --json-out docs/eval/ladder/model-refresh-noderoom.json
    

    Pre-flight (re-run on this head with fetch replaced by a function that throws; 0 fetch calls): worst case $8.0552 for 2 routes x 4 rungs, request ceiling 144, so the cap must be at least that or the run refuses to start. Actual spend is expected to be a small fraction of it. Commit the receipt in a receipt-only commit on top of the tested commit (A.0.3 form a) with a clean tree.

    The --json-out report now carries everything A.0.3 reviews: command, candidateIds, echoedModels, rows[] (one per provider request: requestedModel, resolvedModel, echoedModel, usage, costUsd, fallbackUsed, rung), environment, testedCommit (40-hex), gitDirty, constantSha256, budget (maxUsd, estimateUsd, spentUsd, requests, requestCeiling, stoppedBy) and dispatchPolicy. Reject the receipt unless: requestedRoutes == candidateIds == ["gemini-3.6-flash","gemini-3.8-flash"]; rungs are exactly L1-L4 with 8 passing result rows; every row has resolvedModel == requestedModel, fallbackUsed == false and an echoedModel equal to the requested id (a gemini-3.8-flash request answered as another model is the failure this field exists to expose; 3.5 is already served as 3.6); budget.stoppedBy == null; budget.requests <= budget.requestCeiling; gitDirty == false; dispatchPolicy == {maxRetries:0, crossModelFallback:false}. echoedModel is read from the raw response body (modelVersion / model): the AI SDK's response.modelId is the requested id for Google, so it could never show a re-route.

  2. Manual listen check of gemini-3.8-flash-tts (convex/http.ts:36, SPEC A.0.7: a frozen-class id with an announced shutdown inside 60 days moves to its vendor-named replacement, and a listen test is the gate). Shape-verified here by a fake-transport test through POST /voice/synthesize; the audio itself is unverified. To close it: on a deployment with GOOGLE_GENERATIVE_AI_API_KEY and VOICE_TTS_PROVIDER=gemini, POST one short line to /voice/synthesize, confirm X-Voice-TTS-Model: gemini-3.8-flash-tts and a playable WAV in the Kore voice, and record the result on this PR. The old gemini-3.1-flash-tts-preview shuts down 2026-11-17.

  3. Observed Environment smoke for top_paid (convex/agentJobs.ts:2101, anthropic/claude-sonnet-5.5 over OpenRouter). Whether OpenRouter rewrites a forced tool_choice, and whether it passes strict: true tool definitions through, is unobserved, so the request is sent as tool_choice: auto plus strict: true and one live request has to show a 200 with a tool call (SPEC 1.3, A.2.9).

Also not run: prod:gate (network, audit, design and proof gates). CI's verify job fails at the npm audit step on root undici 8.10.0, the same version as origin/main, so that failure is not from this PR (PR #256 bumps it); failing checks are not required for merge, which is why the block is the draft state plus the label.

Runtime sites (44; migrate 26, freeze 16, defer 2 per spec)

Site Today Disposition This PR
convex/agent.ts:346 gemini-3.5-flash migrate -> gemini-3.8-flash - Google deprecated 3.5-flash 2026-10-08; one DEFAULT_AGENT_MODEL constant (convex/modelDefaults.ts) replaces the 7 literals; env chain unchanged; the ladder receipt is merge blocker 1 (see MERGE BLOCKERS)
convex/agent.ts:345 minimax/minimax-m3 freeze research lane champion by recorded evidence (docs/eval/results.json; adaptiveRouter requireN5Promotion); active, no expiry -> Part B v2
convex/agent.ts:783 gemini-3.5-flash migrate -> gemini-3.8-flash - Google deprecated 3.5-flash 2026-10-08; one DEFAULT_AGENT_MODEL constant (convex/modelDefaults.ts) replaces the 7 literals; env chain unchanged; the ladder receipt is merge blocker 1 (see MERGE BLOCKERS)
convex/agent.ts:65 z-ai/glm-4.7-flash freeze listed, no OpenRouter expiration_date (sibling z-ai/glm-4.7 expires 2026-12-31 -> watch)
convex/agentJobs.ts:2103 gemini-3.5-flash migrate -> gemini-3.8-flash - Google deprecated 3.5-flash 2026-10-08; one DEFAULT_AGENT_MODEL constant (convex/modelDefaults.ts) replaces the 7 literals; env chain unchanged; the ladder receipt is merge blocker 1 (see MERGE BLOCKERS)
convex/agentJobs.ts:2102 minimax/minimax-m3 freeze research lane champion by recorded evidence (docs/eval/results.json; adaptiveRouter requireN5Promotion); active, no expiry -> Part B v2
convex/agentJobs.ts:2101 anthropic/claude-sonnet-4 migrate -> anthropic/claude-sonnet-5.5 - claude-sonnet-4-20250514 retired 2026-06-15; env chain AGENT_TOP_PAID_MODEL ?? AGENT_MODEL ?? const unchanged. convexModel now sends tool_choice: auto plus strict: true tool definitions for this id (SPEC 1.3) instead of a forced choice. Stays a draft until the observed Environment smoke (merge blocker 3).
convex/agentJobRunner.ts:512 gemini-3.5-flash migrate -> gemini-3.8-flash - Google deprecated 3.5-flash 2026-10-08; one DEFAULT_AGENT_MODEL constant (convex/modelDefaults.ts) replaces the 7 literals; env chain unchanged; the ladder receipt is merge blocker 1 (see MERGE BLOCKERS)
convex/agentJobRunner.ts:514 openrouter/free-auto freeze free-path sentinel, not a model
convex/http.ts:175 gemini-3.5-flash migrate -> gemini-3.8-flash - Google deprecated 3.5-flash 2026-10-08; one DEFAULT_AGENT_MODEL constant (convex/modelDefaults.ts) replaces the 7 literals; env chain unchanged; the ladder receipt is merge blocker 1 (see MERGE BLOCKERS)
convex/streaming.ts:68 gemini-3.5-flash migrate -> gemini-3.8-flash - Google deprecated 3.5-flash 2026-10-08; one DEFAULT_AGENT_MODEL constant (convex/modelDefaults.ts) replaces the 7 literals; env chain unchanged; the ladder receipt is merge blocker 1 (see MERGE BLOCKERS)
convex/http.ts:35 gemini-3.1-flash-tts-preview migrate -> gemini-3.8-flash-tts (constant now at convex/http.ts:36) - Google shutdown 2026-11-17, vendor-named replacement, frozen-class voice exception (A.0.7). VOICE_GEMINI_TTS_MODEL precedence unchanged. The manual listen check is merge blocker 2. A fake-transport test drives POST /voice/synthesize and asserts the wire id.
convex/http.ts:283 gpt-4o-mini-transcribe freeze voice; retires 2027-02-26 -> gpt-transcribe (advisory, >90 d)
convex/http.ts:352 gpt-4o-mini-tts freeze voice; dated snapshots retire 2027-01-06; alias target unknown (advisory)
convex/okfEmbeddingProvider.ts:43 gemini-embedding-2 freeze embedding dimension contract; change = re-embed
src/landing/modelRegistry.ts:37 z-ai/glm-5.2 freeze listed on OpenRouter / active at the vendor 2026-10-10, no expiry, no vendor-named successor -> Part B v2
src/landing/modelRegistry.ts:48 anthropic/claude-fable-5 defer anthropic/claude-fable-5 active (floor 2027-06-09); fable-5.1 rejects forced tool_choice -> no bump until the convexModel.ts:842/892 fix is observed
src/ui/Chat.tsx:1518 moonshotai/kimi-k3 freeze listed on OpenRouter / active at the vendor 2026-10-10, no expiry, no vendor-named successor -> Part B v2
src/ui/Chat.tsx:1690 gemini-3.5-flash migrate -> DEFAULT_AGENT_MODEL - import the constant
src/nodeagent/models/modelCatalog.ts:305 moonshotai/kimi-k3 freeze listed on OpenRouter / active at the vendor 2026-10-10, no expiry, no vendor-named successor -> Part B v2
src/nodeagent/models/modelCatalog.ts:232 gpt-5.4 freeze listed on OpenRouter / active at the vendor 2026-10-10, no expiry, no vendor-named successor -> Part B v2
src/nodeagent/models/modelCatalog.ts:272 claude-haiku-4.5 migrate -> +claude-haiku-5-5, +claude-sonnet-5-5, +claude-opus-5-5 (first entries) - add-only; old ids stay valid for persisted rooms; pricing rows (:63-216) for the new ids in the same PR
src/nodeagent/models/modelCatalog.ts:288 gemini-3-flash-preview migrate -> ["gemini-3.8-flash","gemini-3.5-flash-lite"] - removes gemini-3.1-flash-lite-preview (shut down 2026-05-25) and gemini-2.5-flash; gemini-3-flash-preview replaced by the GA Flash
src/nodeagent/models/modelCatalog.ts:342 gpt-5.4-mini freeze listed on OpenRouter / active at the vendor 2026-10-10, no expiry, no vendor-named successor -> Part B v2
src/nodeagent/models/modelCatalog.ts:187 claude-haiku-4.5 migrate -> +claude-haiku-5-5, +gemini-3.5-flash-lite - entitlement table (anonymous/free allowedModels): add the new ids, keep the old strings; exact-string match
src/nodeagent/models/modelCatalog.ts:612 claude-sonnet-4.6 migrate -> explicit alias table from SPEC 1.2 replaces the dotted-id regex (deleted). claude-opus-4.7 now maps to claude-opus-4-7 (it was silently rewritten to claude-opus-4-8); the opus and claude-opus shortcuts are pinned to claude-opus-4-8, which is what they effectively resolved to before, so their behaviour is unchanged; claude-*-5.5 dotted ids map to claude-*-5-5; every other dotted 4.x id that can sit in a persisted row is listed.
src/nodeagent/models/modelCatalog.ts:641 z-ai/glm-5.2 migrate -> 'auto' -> DEFAULT_AGENT_MODEL - 'openrouter' -> z-ai/glm-5.2 stays (active)
src/nodeagent/models/modelCatalog.ts:806 z-ai/glm-5.2 migrate -> fallback chains pruned of retired or dead members (claude-opus-4/4.1, claude-sonnet-4, claude-haiku-3.5, gemini-2.0-flash, gemini-2.5-*, gemini-3.1-flash-lite-preview) and extended with chains for claude-haiku-5-5, claude-sonnet-5-5, claude-opus-5-5, gemini-3.8-flash, gemini-3.6-flash and gemini-3.5-flash-lite; retired ids stay as keys so a persisted room has an escape route; AGENT_FALLBACK_MODELS precedence unchanged.
src/nodeagent/models/adapter.ts:132 claude-haiku-4-5 migrate -> claude-haiku-5-5 - legacy Haiku -> current Haiku (migration guide). Same PR: @ai-sdk/anthropic 3.0.120, explicit effort (haiku medium, sonnet high, opus medium), sdkToolChoiceForModel downgrades required to auto (with strict: true tools) for forcedToolChoice-forbidden ids, and the ladder meter's reserveDispatch + maxOutputTokens sit inside the three withRetry closures with the dispatch-policy option (A.2.9 Ladder budget).
src/nodeagent/models/adapter.ts:152 gemini-3.5-flash migrate -> DEFAULT_AGENT_MODEL - same constant (gemini-3.8-flash); NODEAGENT_FREE_AUTO_GOOGLE_MODEL precedence unchanged
src/nodeagent/models/adapter.ts:216 (env-provided) freeze env-provided override, no literal
src/nodeagent/models/piAiAdapter.ts:9 gemini-2.5-flash migrate -> gemini-3.8-flash - gemini-2.5 is prior-users-only since 2026-09-18 and expires on OpenRouter 2026-10-20; the request-shape test asserts the installed pi-ai catalog resolves the id (else pi-ai bump first)
src/nodeagent/capture/reasoning.ts:23 claude-haiku-4-5 migrate -> claude-haiku-5-5 - twin of NodeRL reasoning.ts:23; CAPTURE_REASONING_MODEL precedence unchanged; now sends the explicit SPEC 1.3 effort (claudeEffortProviderOptions).
src/nodeagent/models/providerParserLive.ts:110 gemini-3.5-flash migrate -> ["gemini-3.8-flash","gemini-3.6-flash"] - drops gemini-2.5-flash (OR expiry 2026-10-20) and gemini-2.0-flash (shut down 2026-06-01); gemini-3-flash-preview has the vendor-named replacement gemini-3.6-flash
src/nodeagent/models/providerParserLive.ts:111 gpt-5.5 migrate -> ["gpt-5.5","gpt-5.4-mini"] - drops gpt-5-mini (gpt-5-mini-2025-08-07 retires 2026-12-11) and gpt-4.1-mini (gpt-4.1-mini-2025-04-14 shuts down 2027-01-14, OpenAI, announced 2026-10-14); both remaining candidates are active, so no successor is appended; the caller's preferred id and PROVIDER_PARSER_OPENAI_MODEL still come first (providerParserModelCandidates, :116-126)
src/nodeagent/models/providerParserLive.ts:112 claude-haiku-4-5 migrate -> ["claude-haiku-5-5","claude-sonnet-5-5"] - claude-3-5-haiku-latest retired 2026-02-19; claude-sonnet-4-5-20250929 deprecated, retires 2026-11-30; the live parser sends the explicit SPEC 1.3 effort for the direct Anthropic transport.
src/nodeagent/models/providerParserLive.ts:113 z-ai/glm-5.2 migrate -> (remove nex-agi/nex-n2-pro) - absent from the OpenRouter catalog
src/nodeagent/core/adaptiveRouter.ts:82 minimax/minimax-m3 migrate -> minimax stays; :84/:136 -> DEFAULT_AGENT_MODEL - minimax/minimax-m3 active (freeze); the two gemini-3.5-flash literals import the constant; the :136 fast-lane literal moves only after the ladder receipt exists
src/lib/models/modelRouter.ts:47 nebius/zai-org/GLM-5.2 freeze DEFAULT_MODELS table has no importer (inventory); dead table, leave or delete
src/nodeagent/models/openRouterFreeModels.ts:59 cohere/north-mini-code:free freeze preference list for the live free discovery; cohere/north-mini-code:free listed, no expiry
backend/app/config.py:78 nvidia/nemotron-3-nano-omni defer Python backend on GMI MaaS (config.py:78-80, llm.py:37): nvidia/nemotron-3-nano-omni, nvidia/nemotron-3-ultra, google/gemini-3-flash-preview; GMI catalog not inventoried; verify separately with GET {GMI_MAAS_BASE_URL}/models + the backend's pytest; not covered by npm gates
btb_noderoom_agent/harbor_adapter.py:83 z-ai/glm-5.2 freeze Python BTB adapter; z-ai/glm-5.2 active; benchmark label continuity
scripts/gemini-demo-media-judge.ts:21 gemini-3.5-flash migrate -> gemini-3.6-flash - judge script: pin what Google already serves (3.5 auto-routed to 3.6 since 2026-10-08); remove temperature (deprecated 3.6+); 3.8 is a later human choice
scripts/design-qa/rubric-panel.ts:31 gemini-3.5-flash migrate -> gemini-3.6-flash - judge script: pin what Google already serves (3.5 auto-routed to 3.6 since 2026-10-08); remove temperature (deprecated 3.6+); 3.8 is a later human choice

Executable (E) and runtime-adjacent (R) sites (20; migrate 7, freeze 12, defer 1)

Site Class Today Disposition This PR
src/nodeagent/models/modelCatalog.ts:63 R claude-opus-4.7 migrate -> pricing rows added: claude-haiku-5-5 and anthropic/claude-haiku-5.5 ($0.10/$0.50, plus the >100K-prompt tier $0.50/$2.50 as longContext, applied by every cost and reservation path), claude-sonnet-5-5 ($2/$10), claude-opus-5-5 ($4/$20), gemini-3.8-flash ($0.75/$3.75; $1.50/$7.50 from 2027-01-01), gemini-3.6-flash ($0.75/$3.75), gemini-3.5-flash-lite ($0.30/$2.50); the claude-opus-4-7 row ($5/$25) the alias table now needs carries pricingAsOf; retired 4.x rows kept for persisted rooms; all rows are in the agreement test (A.0.6).
src/nodeagent/core/creditModel.ts:57 R z-ai/glm-5.2 freeze credit-pricing dominantModel z-ai/glm-5.2 (also :139,:152,:165): listed, no expiry; a change is a pricing decision, not a lifecycle one
src/nodeagent/models/convexModel.ts:968 E (any claude-*) migrate -> request shape: anthropicStep already sent no temperature/thinking/tool_choice/prefill; added the 2048 max_tokens floor for 5.5 ids (thinking counts), reading blocks by type (test), and strict: true tool definitions where a forced choice is downgraded to auto; tool_choice forwarding at :842/:892 sends auto (plus strict tools) for forcedToolChoice-forbidden ids.
scripts/benchmark/modelEvalConfig.ts:126 R deepseek/deepseek-v4-flash freeze SUPPORTED_MODEL_ROUTES promotion tags are the requireN5Promotion evidence (A.0.2); never edited by a refresh; new ids earn tags through the ladder
scripts/benchmark/run.ts:63 E gemini-3.1-flash-lite freeze benchmark judge gemini-3.1-flash-lite: frozen judge (B.9); Google deprecated it with shutdown 2027-05-07 (>60 d) -> advisory; replacement gemini-3.5-flash-lite only through the benchmark's own re-baseline
scripts/benchmark/probe.ts:13 E claude-opus-4-8 migrate -> +current ids - liveness probe list: add claude-haiku-5-5/claude-sonnet-5-5/claude-opus-5-5, gemini-3.8-flash, gemini-3.5-flash-lite, gpt-6.1-sol; drop ids retired before 2026-10-10 (they can only report 404); keep gemini-3.5-flash to observe the auto-route; claude-opus-4-8 stays (active)
evals/ladder.ts:670 E gpt-5.4-nano defer default --real list ['gpt-5.4-nano','gemini-3.1-flash-lite'] applies only when no ids are passed; the PR passes both ids as ONE comma-separated argument (parseRealModels reads only argv[idx+1], :666-671); gemini-3.1-flash-lite retires 2027-05-07 -> refresh with the next re-baseline (human)
evals/professionalLiveCatalog.ts:51 E deepseek/deepseek-v4-flash freeze deepseek/deepseek-v4-flash is listed on OpenRouter 2026-10-10 with no expiry (DeepSeek's first-party deepseek-v4-flash retirement is a different transport - per-transport fact, B.1); research_champion by evidence; also financeModelLive.ts:983, halo-live-path-calibration.ts:22, package.json:241,245
scripts/finch-official-judge.py:20 E gpt-5-mini freeze official FinCH judge gpt-5-mini (snapshot 2025-08-07): frozen (B.9). Announced retirement 2026-12-11 is <= 60 d from 2026-10-12 -> URGENT decision (section C.2): re-promote through scripts/proofloop-promote-official-score.ts, or mark the receipts historical (retiredJudge). Never an automatic edit
scripts/finauditing-official-judge.py:26 E gpt-5-mini freeze same official-judge decision as finch-official-judge.py:20; its gpt-4o*/gpt-4.1* branch sends temperature=0.0/max_tokens=4 (unchanged, those ids are not Claude/GPT-6)
scripts/mbabench-official-sweep.py:26 E google/gemini-3-flash-preview freeze official judge google/gemini-3-flash-preview: listed on OpenRouter; natively deprecated with no shutdown date -> advisory; frozen (B.9)
scripts/judge-demo-gif.ts:24 E gemini-3.5-flash migrate -> gemini-3.6-flash + GEMINI_JUDGE_MODEL env - pin what Google already serves under 3.5 (auto-routed since 2026-10-08); drop temperature:0 at :129 (deprecated on 3.6+); same for scripts/judge-state-captures.ts:17/:85
scripts/walkthroughs/judge-video.ts:53 E gemini-3.5-flash migrate -> gemini-3.6-flash - drop temperature 0.2 at :62; the same rule for gemini-ui-review.ts:11, gemini-visual-polish.ts:14/:55, gemini-mp4-workflow-review.ts:15/:82, eval/painJudge.ts:18, spreadsheetbench-chart-visual-grade.ts:13/:96, src/eval/spreadsheetBenchChartVisualProbe.ts:71, skills/seo-qa/scripts/judge-video-gemini.ts:12/:66, packages/walkthrough-review-cli/src/core.ts:78, walkthrough-review.config.json:3; env chains unchanged; 3.8 is a later human choice
scripts/eval/providerParseSmoke.ts:21 E claude-haiku-4-5 migrate -> gemini-3.8-flash,gpt-5.4-mini,claude-haiku-5-5 - mirrors the providerParserLive lists
scripts/startup-diligence-provider-eval.ts:150 E gemini-3.5-flash migrate -> gemini-3.8-flash; drop gemini-2.5-flash; claude-haiku-5-5 - gemini-2.5-flash expires on OpenRouter 2026-10-20; gpt-5.4-nano, glm-4.7-flash, kimi-k2.6 stay
scripts/bankertoolbench-nodeagent-full-sweep.ps1:7 E z-ai/glm-5.2 freeze z-ai/glm-5.2 listed, no expiry; benchmark label continuity (also src/eval/bankerToolBenchNodeAgentGeneral.ts:205, proofloop/live-browser-proof.spec.ts:65, scripts/sfn-nonbtb-nodeagent-runner.ts)
scripts/bankertoolbench-nodeagent-clean-sequential.mjs:8 E gpt-4.1-mini freeze benchmark identity, dated — the default names the recorded probe (prefix btb-clean-capability-full100-parallel-v3-gpt41mini; .proofloop/lanes/bankertoolbench/* is hash-bound evidence); OpenAI shuts native gpt-4.1-mini-2025-04-14 down 2027-01-14 (announced 2026-10-14), after which a default run fails at the provider; a successor probe (gpt-6-luna, vendor-named) is a new lane run under its own score/cost receipt (§C.7); also bankertoolbench-nodeagent-clean-parallel.ps1:7, smb-lending-four-mode-benchmark.ts:17 (its OpenRouter branch, taken when OPENROUTER_API_KEY is set, sends openai/gpt-4.1-mini, undated on OpenRouter), src/app/bankerToolBenchRoomSeed.ts:26 (recorded evidence label, never rewritten)
scripts/inference-nodetrace/run.mjs:13 E gpt-5.4 freeze gpt-5.4 active; INFERENCE_MODEL precedence unchanged
scripts/walkthroughs/voiceover.ts:60 E gpt-4o-mini-tts freeze voice gpt-4o-mini-tts; dated snapshots retire 2027-01-06 (advisory)
package.json:82 E gpt-5-mini freeze benchmark:finch:canonical-judge --judge-model gpt-5-mini is the official judge (see finch-official-judge.py:20); shadow judge openrouter/free unchanged

Class N (24) dispositions - docs loop, test assertions, examples, fixtures

doc (5)

  • src/nodeagent/models/modelCatalog.ts:1 header comment ("Primary NodeBench default: z-ai/glm-5.2"): freeze - glm-5.2 is untouched by this PR.
  • README.md:1604 (npx convex env set AGENT_MODEL gemini-3.5-flash): defer - an operator command that names the ladder-recorded route. It moves to gemini-3.8-flash in the same commit as the ladder receipt (merge blocker 1), exactly like the adaptiveRouter.ts:136 literal; moving it first would recommend a route with no PASS row.
  • production-agent.json:108 (modelMigration.pinnedModel z-ai/glm-5.2): freeze - pins the glm-5.2 default, unchanged.
  • docs/eval/FIRST_USER_E2E.md:209 (z-ai/glm-5.2 env example): freeze - recorded run instructions, glm-5.2 unchanged.
  • docs/ historical mentions (841 docs, 20 evidence, 30 .proofloop, 8 episodes files): freeze - hash-bound evidence and archived prototypes are never rewritten.

eval-fixture (1)

  • public/eval/proofloop-prod-proxy-benchmark-matrix.json: freeze - generated benchmark artifact derived from src/eval/proofloopProdProxyBenchmarkMatrix.ts; glm-5.2 unchanged.

example (7)

  • .env.example:65 NODEAGENT_FREE_AUTO_GOOGLE_MODEL: migrate -> gemini-3.8-flash (the code default).
  • .env.example:116 GEMINI_SEO_VIDEO_JUDGE_MODEL: migrate -> gemini-3.6-flash (the judge script default).
  • .env.example:165 PROVIDER_PARSER_GEMINI_MODEL: migrate -> gemini-3.8-flash,gemini-3.6-flash; the neighbouring :167 PROVIDER_PARSER_ANTHROPIC_MODEL -> claude-haiku-5-5,claude-sonnet-5-5 and :168 PROVIDER_PARSER_OPENROUTER_MODEL without the unlisted nex-agi/nex-n2-pro are part of the same copy (the inventory records them under :165). :84 VOICE_GEMINI_TTS_MODEL -> gemini-3.8-flash-tts (the shutdown-deadline fix). Env vars beat code defaults, so a .env built from the old file would have silently restored every one of these ids.
  • .env.example:66 AGENT_MODEL=gpt-5.4-nano, :103 AGENT_FILE_EGRESS_MODEL, :147 FREE_AUTO_JOB_MODEL: freeze - values this PR does not change.
  • src/nodeagent/core/subagentDispatcher.ts:32 ('minimax/minimax-m3' in a zod .describe): freeze - research lane champion.
  • A test (tests/modelRefreshServerRoles.test.ts, last describe) now fails if any of these .env.example values drifts from what the code defaults to.

test-assertion (11)

  • Updated with the retirement or price cited in the diff: modelAliasClaude.test.ts:11 (claude-opus-4.7 -> claude-opus-4-7), phaseModel.test.ts:73 (the top_paid fallback fixture is now anthropic/claude-sonnet-5.5; claude-sonnet-4 retired 2026-06-15), agentJobsRuntime.test.ts (new: default model per route policy, asserted as literals), openRouterFreeModels.test.ts (auto follows the constant), openAiTokenLimit.test.ts (gpt-6 case), providerEgressPolicy.test.ts (re-run, passes unchanged), and in this round providerParserLive.test.ts (env id gemini-3.6-flash, which is a default, so the de-duplication check still bites).
  • Unchanged because the ids are not touched here: kimiK3Default.test.ts:22, convexModelFreeAutoMode.test.ts, chatModelPickerPolish.test.tsx:168 (claude-sonnet-4-6 is still a live catalog row), openRouterConvexBenchmark.test.ts, finchOfficialJudgeCostGuard.test.ts (frozen official judge), modelEvalMatrix.test.ts (promotion tags).

Hash-bound evidence files untouched.

Pricing sources (A.0.6) - every source is enumerated and in tests/modelPricingAgreement.test.ts

  1. modelCatalog.modelPricing - the only hand-typed table. Every row this PR adds or relies on now carries pricingAsOf (2026-10-10), including claude-opus-4-7 ($5/$25, which claude-opus-4.7 now resolves to) and is in the agreement cases. claude-haiku-5-5 and anthropic/claude-haiku-5.5 also carry the >100K-prompt tier ($0.50/$2.50) as longContext.
  2. getModelPricing - accessor over (1).
  3. adapter.priceRun, priceRunStrict, reserveDispatch - tier-aware through modelCatalog.priceTokens (the ladder meter uses the strict price and the reservation).
  4. convexModel.convexPriceRun, exactConvexUsageCost, convexAttemptCostReservationUsd - tier-aware through the same helper.
  5. creditModel private priceRun and proofloopModelTracking cost estimate - tier-aware through the same helper.
  6. Readers with no table of their own: Chat.tsx price chips (show the base tier) and scripts/benchmark/run.ts catalogPricingAtRun (records a flat {inputPer1M, outputPer1M} snapshot per run; benchmark prompts are short and it is not a spend guard, so it is left flat).

The tier fix is behaviour, not just data: 120K input + the 8192-token cap now reserves $0.08048 (was $0.016096), and the pre-flight estimate prices each step by its prompt size.

Breaking-parameter fixes (spec section 1.3)

  • sdkToolChoiceForModel (adapter.ts), convexModel.openAiCompatibleToolChoice (OpenRouter anthropic/claude-*-5.5) and the LangChain bridge send auto instead of a forced required/any for Sonnet/Opus 5.5 (and Fable 5.1); Haiku 5.5 keeps a forced choice. Keyed by exact wire id in modelCatalog.forbidsForcedToolChoice. New this round: wherever that downgrade happens the tool definitions carry strict: true (needsStrictTools: AI-SDK adapter, direct Anthropic anthropicStep, OpenRouter openAiTool), which is SPEC 1.3's replacement for the forced choice. It is applied only on the downgrade, so no other path changes.
  • modelMaxOutputTokens(id) (shared src/nodeagent/models/outputTokens.ts): 2048 floor for 5.5 ids because thinking counts toward max_tokens; anthropicStep reads content blocks by type (test).
  • Explicit effort (haiku medium / sonnet high / opus medium) on every AI-SDK Anthropic path that moved to a 5.5 id: the adapter, and, new this round, capture/reasoning.ts and providerParserLive through one helper (claudeEffortProviderOptions); @ai-sdk/anthropic ^3.0.84 -> ^3.0.120.
  • openAiTokenLimit regex also matches gpt-6 (precondition only; no gpt-6 id lands here).
  • Judge scripts pinned to gemini-3.6-flash with temperature removed (deprecated on 3.6+); GEMINI_JUDGE_MODEL env chains unchanged.
  • resolveModelAlias: the dotted-id regex rewrite is deleted for an explicit table; claude-opus-4.7 now maps to claude-opus-4-7 (was silently -4-8). The opus and claude-opus shortcuts are pinned to claude-opus-4-8 (their previous effective target, behaviour preserved). Old 4.x strings stay valid for persisted rows.
  • Ladder budget (spec "Ladder budget", lands before the receipt run): --real refuses to start without LADDER_MAX_COST_USD / --max-cost-usd; job-wide meter; reserveDispatch inside every withRetry dispatch (maxRetries: 0 on the SDK, one reservation per physical request); priceRunStrict throws UnpricedModelError instead of a guessed $1/$5; dispatch: { maxRetries, crossModelFallback } option. New this round: a refused reservation or unpriced id is terminal for the meter (the failed dispatch can no longer fall through to AGENT_FALLBACK_MODEL, a retry, or a free-auto candidate), and the provider-neutral nodeagent/free-auto Google fallback honours crossModelFallback: false (it previously always went to paid Gemini). ladder:free gets --max-cost-usd=1 because a cap is now mandatory.

Test proof

New request-shape tests build a real request through the repo's own builders with a fake fetch, asserting the literal wire id (never imported from the code under test) and that forbidden keys are absent:

  • tests/modelRefreshRequestShape.test.ts - convexModel (direct Anthropic, OpenRouter), adapter.model, aiSdkReasoner, the LangChain bridge, pi-ai default; now also strict tools and effort.
  • tests/modelRefreshServerRoles.test.ts (new) - the roles that had no fake-transport test: Gemini TTS (drives POST /voice/synthesize), private stream (drives POST /stream-private-reply, nothing injected: the route's own AGENT_MODEL ?? DEFAULT_AGENT_MODEL reaches geminiStream), provider parser (runLiveProviderParser with no model argument, so the default candidate reaches the wire; effort and wire id for haiku and sonnet), judge scripts (rubric-panel, visual-polish, mp4-workflow-review, SEO judge and walkthroughs/judge-video are executed with fixtures and a fake fetch; four scripts that need ImageMagick, LibreOffice or the benchmark workbooks are checked on the TypeScript AST for any temperature property), and .env.example against the code defaults.
  • tests/modelPricingAgreement.test.ts (A.0.6 agreement, tier-aware), tests/modelCatalogRefresh.test.ts.
  • evals/ladderBudget.test.ts drives the actual adapter path. Round 1 tightened (b) to the specified sequence (charged response, then ECONNRESET) and (e) to usage that stays within every reservation: the cap is the pre-flight estimate itself, the context outgrows the scripted dry run, and the test asserts the grid stops before the cap (spentUsd < maxUsd, a refused reservation, budget.requests equals the fetch count and is below the ceiling). Also covered: terminal-stop behaviour, per-dispatch receipt rows (echoedModel, fallbackUsed) and the report fields.
  • tests/modelAdapterProviderNeutralRoute.test.ts - crossModelFallback: false never reaches paid Gemini.

Each new assertion was checked by reverting the line it protects (TTS constant, DEFAULT_AGENT_MODEL, a judge script's temperature/model, the parser default and effort, strict tools, the tier, the terminal stop, the fallback guard): the matching test fails every time.

Check output (npm run check = typecheck + convex tsc + vitest)

Test Files  2 failed | 392 passed (394)
     Tests  2 failed | 2869 passed (2871)
FAIL tests/proofStaleness.test.ts   [docs/eval/noderoom-fresh-user-vertical-proof.json is 45.9 days old, window 30d; this PR does not touch it]
FAIL tests/qaMatrix.test.ts   [the ladder receipt blocker above; fails on purpose]

The files typechecked for the first time because the new tests import them needed two type-only fixes (scripts/design-qa/rubric-panel.ts content type, an unused import in the SEO judge); both lines are unchanged from origin/main.

Not in this PR / for the human

  • The three merge blockers above.
  • Python backend (backend/app/config.py:78-80, llm.py:37 on GMI MaaS): defer - list GET {GMI_MAAS_BASE_URL}/models, confirm the ids, run the backend pytest.
  • src/landing/modelRegistry.ts:48 (anthropic/claude-fable-5): defer until the forced-tool-choice downgrade is observed live (merge blocker 3).
  • README.md:1604 and adaptiveRouter.ts:136: move to gemini-3.8-flash with the ladder receipt.
  • Frozen advisories: official judges gpt-5-mini (retires 2026-12-11, URGENT decision), google/gemini-3-flash-preview, benchmark judge gemini-3.1-flash-lite (2027-05-07).
  • Gemini 3.8 Flash and 3.8 TTS intro prices double on 2027-01-01 ($1.50/$7.50); the catalog rows say so and need re-pricing then.
  • Merge-order note: Codex PR Improve README onboarding, package dependencies, and CHANGELOG handoff #256 bumps undici in package.json / package-lock.json and PR fix(evals): reject unconfigured real-mode runs #260 owns evals/runEval.ts; this PR touches neither. The lockfile change here adds a nested @ai-sdk/anthropic/node_modules/undici 6.29.0 entry; re-run npm ci after whichever PR merges second.

Spec: model-defaults SPEC Part A section A.2 (NodeRoom)

HomenShum and others added 2 commits October 14, 2026 12:10
Move NodeRoom's default model ids off retired or deprecated ones, per model-defaults
SPEC Part A section A.2.9:

- one DEFAULT_AGENT_MODEL (gemini-3.8-flash) in convex/modelDefaults.ts replaces the
  gemini-3.5-flash literals (Google deprecated it 2026-10-08); env chains unchanged
- top_paid default anthropic/claude-sonnet-4 -> anthropic/claude-sonnet-5.5; Haiku defaults
  -> claude-haiku-5-5; pi-ai default gemini-2.5-flash -> DEFAULT_AGENT_MODEL
- TTS gemini-3.1-flash-tts-preview -> gemini-3.8-flash-tts (shutdown 2026-11-17)
- catalog: 5.5 and gemini pricing rows, tier allowlists, fallback chains pruned; the dotted
  claude-id regex becomes an explicit table (claude-opus-4.7 now maps to claude-opus-4-7)
- Claude 5.5 request shape: forced tool_choice downgraded to auto for Sonnet/Opus 5.5,
  explicit effort on the AI-SDK path, 2048 max_tokens floor, @ai-sdk/anthropic >= 3.0.120
- judge scripts pinned to gemini-3.6-flash with temperature removed
- ladder: --real needs a dollar cap, per-dispatch cost reservation, receipt carries the
  tested commit, budget and dispatch policy

Merge is gated on the L1-L4 ladder receipt (not run here: paid). The adaptiveRouter fast
lane stays on gemini-3.5-flash until that receipt exists.

Spec: model-defaults SPEC Part A §A.2 (NodeRoom)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
noderoom Ready Ready Preview Oct 14, 2026 8:21pm UTC

Request Review

@github-actions

github-actions Bot commented Oct 14, 2026 •

Copy link
Copy Markdown

Scaffold Handoff — For Your Coding Agent

Your coding agent (Codex, Claude Code, etc.) should apply the accepted
scaffold proposals below. Do NOT touch any immutable files.

Immutability Check

Mode: advisory

✅ No immutable files were modified in this branch.

Changed Files

  • .env.example
  • .tours/01-primary-user-flow.tour
  • .tours/03-debug-and-recovery.tour
  • CHANGELOG/README.md
  • CHANGELOG/qa/model-refresh-noderoom.md
  • convex/agent.ts
  • convex/agentJobRunner.ts
  • convex/agentJobs.ts
  • convex/http.ts
  • convex/modelDefaults.ts
  • convex/streaming.ts
  • docs/eval/OFFICIAL_BENCHMARK_READINESS.md
  • docs/eval/OFFICIAL_BENCHMARK_TASK_COVERAGE.md
  • docs/eval/OPENROUTER_CONVEX_BENCHMARK.md
  • docs/eval/agent-improvement-loop.md
  • docs/eval/agent-improvement-loop.svg
  • docs/eval/agent-improvement-loop/20261014T202022Z.json
  • docs/eval/agent-improvement-loop/latest.json
  • docs/eval/agent-workspace-sandbox-smoke.json
  • docs/eval/algorithm-artifact-smoke.json
  • docs/eval/bankertoolbench-official-contract.json
  • docs/eval/docker-sandbox-probe.json
  • docs/eval/eval-runs.jsonl
  • docs/eval/halo-convex-context-telemetry.json
  • docs/eval/halo-self-improvement-smoke.json
  • docs/eval/halo-variant-selection.json
  • docs/eval/official-benchmark-readiness.json
  • docs/eval/official-benchmark-task-coverage.json
  • docs/eval/openrouter-convex-benchmark.json
  • docs/eval/professional-catalog-proofs.json
  • docs/eval/professional-proof-ledger.json
  • docs/eval/spreadsheetbench-chart-visual-probe.json
  • docs/eval/traces/credit/20261014T202028599Z-239ce68c_dirty.f6de2518d6ed9e60/cascade-healthy.json
  • docs/eval/traces/credit/20261014T202028599Z-239ce68c_dirty.f6de2518d6ed9e60/delta-incomplete.json
  • docs/eval/traces/credit/20261014T202028599Z-239ce68c_dirty.f6de2518d6ed9e60/mapping-correct.json
  • docs/eval/traces/credit/20261014T202028599Z-239ce68c_dirty.f6de2518d6ed9e60/mapping-misbind.json
  • docs/eval/traces/credit/20261014T202028599Z-239ce68c_dirty.f6de2518d6ed9e60/summit-stressed.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L1_read_scripted.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L2_edit_scripted.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L3_conflict_scripted.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L4_blocked_scripted.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L5_large_range_scripted.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L6_long_horizon_scripted.json
  • docs/eval/traces/ladder/20261014T202028275Z-239ce68ce8d03b3fd6ac4db46abd70bf963ebfd8_dirty.17846abb72503f62/ladder_L7_resume_scripted.json
  • evals/ladder.ts
  • evals/ladderBudget.test.ts
  • package-lock.json
  • package.json
  • packages/walkthrough-review-cli/README.md
  • packages/walkthrough-review-cli/src/core.ts
  • scripts/benchmark/probe.ts
  • scripts/design-qa/rubric-panel.ts
  • scripts/eval/painJudge.ts
  • scripts/eval/providerParseSmoke.ts
  • scripts/gemini-demo-media-judge.ts
  • scripts/gemini-mp4-workflow-review.ts
  • scripts/gemini-ui-review.ts
  • scripts/gemini-visual-polish.ts
  • scripts/judge-demo-gif.ts
  • scripts/judge-state-captures.ts
  • scripts/spreadsheetbench-chart-visual-grade.ts
  • scripts/startup-diligence-provider-eval.ts
  • scripts/walkthroughs/judge-video.ts
  • skills/seo-qa/scripts/judge-video-gemini.ts
  • src/eval/proofloopModelTracking.ts
  • src/eval/spreadsheetBenchChartVisualProbe.ts
  • src/nodeagent/capture/reasoning.ts
  • src/nodeagent/core/adaptiveRouter.ts
  • src/nodeagent/core/creditModel.ts
  • src/nodeagent/integrations/langchain.ts
  • src/nodeagent/models/adapter.ts
  • src/nodeagent/models/convexModel.ts
  • src/nodeagent/models/modelCatalog.ts
  • src/nodeagent/models/openAiTokenLimit.ts
  • src/nodeagent/models/outputTokens.ts
  • src/nodeagent/models/piAiAdapter.ts
  • src/nodeagent/models/providerParserLive.ts
  • src/ui/Chat.tsx
  • tests/agentJobsRuntime.test.ts
  • tests/agentJobsSource.test.ts
  • tests/modelAdapterProviderNeutralRoute.test.ts
  • tests/modelAliasClaude.test.ts
  • tests/modelCatalogRefresh.test.ts
  • tests/modelPricingAgreement.test.ts
  • tests/modelRefreshRequestShape.test.ts
  • tests/modelRefreshServerRoles.test.ts
  • tests/openAiTokenLimit.test.ts
  • tests/openRouterFreeModels.test.ts
  • tests/phaseModel.test.ts
  • tests/providerParserLive.test.ts
  • tests/qaMatrix.test.ts
  • tests/voiceHostedTtsProviders.test.ts
  • vitest.config.ts
  • walkthrough-review.config.json

Needs Adversarial Review — Do NOT Apply Yet

These proposals passed the reject check but have not been approved by
an adversarial reviewer. A human or frozen LLM judge must approve them first.

  • scaf-001 (AGENTS.md): Add explicit instruction for step spreadsheetbench-runner-fixture: Step spreadsheetbench-runner-fixture failed — scaffold may need explicit instruction or evidence assertion.
  • scaf-002 (AGENTS.md): Add explicit instruction for step convex-boundaries: Step convex-boundaries failed — scaffold may need explicit instruction or evidence assertion.

Safety Boundary

Agent may improve the scaffold.
Agent may NOT weaken the proof gate.

Immutable files (never modify):

  • scripts/proofloop.mjs
  • scripts/agent-improvement-loop.ts
  • tests/harnessChangeEval.test.ts
  • .github/workflows/
  • src/eval/evalTrustPolicy.ts
  • src/eval/architectureBudget.ts
  • evals/evalStore.ts

Scaffold files (safe to modify):

  • AGENTS.md
  • CLAUDE.md
  • proofloop/scenarios/*.yaml
  • proofloop/rubrics/*.yaml
  • proofloop/subagents/*.md
  • proofloop/adapters/*.js
  • .proofloop/memory.jsonl
  • src/nodeagent/models/prompts/systemPrompt.ts

@HomenShum HomenShum added the needs-live-eval Merge blocked until a live receipt or manual check exists (SPEC A.0.3). A block, not a note. label Oct 14, 2026
@HomenShum
HomenShum marked this pull request as draft October 14, 2026 20:09
- merge gate: PR is a draft with the needs-live-eval label; the ladder receipt, the
  gemini-3.8-flash-tts listen check and the top_paid Environment smoke are listed as
  explicit blockers (PR body, CHANGELOG lane)
- ladder --json-out is now reviewable under A.0.3: command, candidateIds, echoedModels,
  per-dispatch rows (provider-echoed model from the raw body, usage, cost, fallbackUsed),
  environment, budget.requestCeiling
- a refused reservation or unpriced id is terminal for the meter: no fallback model, retry
  or candidate dispatches after it; the provider-neutral Google fallback honours
  crossModelFallback: false
- claude-haiku-5-5 is priced tier-aware (>100K prompt tokens: $0.50/$2.50) in every cost and
  reservation path; claude-opus-4-7 gets pricingAsOf and an agreement row
- Sonnet/Opus 5.5 send strict tool definitions where a forced tool_choice becomes auto;
  capture reasoner and provider parser send the explicit effort
- .env.example copies of the changed defaults now match the code (and a test pins them)
- request-shape tests for the TTS route, private stream route, provider parser default,
  judge scripts; ladder budget tests (b) and (e) follow the specified scenarios

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 0ff118a8 Deployed Oct 14, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-live-eval Merge blocked until a live receipt or manual check exists (SPEC A.0.3). A block, not a note.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant