Skip to content

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

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

HomenShum wants to merge 5 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). Done in code; stays a draft until an observed Environment smoke shows OpenRouter accepts the request (tool_choice downgraded to auto meanwhile).
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 spec asks the request-shape test to assert the installed pi-ai catalog resolves the id, but no pi-ai package is installed (the adapter wraps model()), so the test asserts only the id's name, provider and pricing, NOT a pi-ai catalog lookup.
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 Done for :84 (constant); the :136 fast-lane literal deliberately stays gemini-3.5-flash until the ladder receipt exists (spec: moves only after the receipt) - defer, condition = receipt file present.
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 is the non-default gemini-3.5-flash-lite, expecting [gemini-3.8-flash, gemini-3.5-flash-lite, gemini-3.6-flash], so env precedence is proven; a second case with a default env id proves de-duplication). providerParseSmoke.ts also compared the provider to "google" while getProviderForModel returns "gemini", so the Gemini smoke never reached Google; fixed (pre-existing on main).
  • 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.

Review round 3 (Codex round 2 defects and the two round-1 findings that stayed open)

Finding Disposition
R1.1 / R2.1 Free request becomes a paid request (SPEC A.0.10) Fixed for every path except one the spec sanctions (see discrepancy 1). adapter.fallbackModelFor and convexModel drop a paid fallback for a free primary (isFreeModelId) unless the caller sets allowPaidFallback: true and a reservation exists: adapter = a SpendMeter (meteredDispatch reserves the paid model's worst case before the request), Convex = a finite maxCostUsd on next() (the failover controller estimates each candidate against it and skips what does not fit). Tests: a concrete :free model that is down under the default policy, allowPaidFallback without a meter, a meter without the flag, both together, a paid fallback the meter cannot afford, fallbackModelIds, the deployment AGENT_FALLBACK_MODELS list, and paid -> paid / paid -> free unchanged (tests/modelCostClass.test.ts). No production caller sets allowPaidFallback: default is deny.
R1.2 / R2.2 Unknown ids dispatch Fixed at the resolver and both boundaries. resolveModelAlias throws UnknownModelError for an id that is not priced, not declared by the alias table and not free-looking; an unknown -free / :free id resolves to openrouter/free-auto. model(), convexModel(), judge(), the adaptive router and the fallback lists therefore refuse gemini-9-flash-unknown, acme/paid-unknown-model, claude-opus-4.9 before any request (fake transport: 0 fetch calls), metered or not. Readers that must tolerate historical ids (priceRun, reserveDispatch, creditModel, proofloopModelTracking, the egress policy, benchmark/run.ts, the interop normalizer, the LangChain strict check) call the new lookupModelAlias, which is the old behaviour.
R2.3 failed rung reports cost 0 Fixed. The catch branch of runRung reports the sum of its dispatch costs. Test drives main() with a charged answer then ECONNRESET: row cost equals the dispatch sum and budget.spentUsd (reverting to cost: 0 fails it).
R2.4 alias bypasses the free-auto fan-out Fixed. ladderRequestCeiling and estimateLadderUsd test /free-auto$/ on resolveModelAlias(route): free, free-auto, openrouter-auto, openrouter-free-auto, nodeagent-auto now count OPENROUTER_FREE_AUTO_LIMIT per step; openrouter/free and concrete aliases count 1.
R2.5 gpt-6.1-sol request shape Fixed, see Test proof.
R2.6 pricing agreement excluded two sources Fixed, see Pricing sources.

Existing tests that dispatched never-heard-of ids were retargeted, not loosened: evals/ladderBudget.test.ts (g), tests/convexModelFreeAutoMode.test.ts (two tests) and tests/modelAliasClaude.test.ts. The premise of each (an id the catalog does not know reaches a provider) is the defect, so each now asserts the refusal, and their original invariants (zero requests, meter untouched, terminal stop, conservative reservation, hard-budget block) are kept on the one kind of id the adapter can still route without a price row: an id the alias table declares (claude-sonnet-4.5 -> claude-sonnet-4-5). The old ids stay in comments in the diff.

Spec discrepancies for a human decision (SPEC section C) - not decided in this PR

  1. nodeagent/free-auto -> paid gemini-3.8-flash. The spec's adapter.ts:152 row sanctions it (NODEAGENT_FREE_AUTO_GOOGLE_MODEL precedence unchanged) but A.0.10 says a free lane never becomes a paid request. Left as the spec has it: it already honours crossModelFallback: false (the ladder receipt policy) and a refused reservation. Decision: keep it as a named exception (then it needs allowPaidFallback plus an awaited reservation in generateProviderNeutralAgentText), or remove the Google leg.
  2. Alias-declared ids with no price row (claude-sonnet-4.5, claude-opus-4.5, claude-opus-4.6 and what they resolve to). A.0.5/A.0.6 keep old strings valid for persisted rows, A.0.10 says unknown throws. They stay routable; a metered run refuses them (UnpricedModelError), an unmetered caller prices them at the guessed $1/$5. Decision: add verified price rows (no vendor price is in the spec) or drop the aliases.
  3. agentJobs.createOrReuse still stores any modelPolicy string (Codex's convex-test probe). The id is refused when the job runs (convexModel), not when it is created, because live clients persist strings such as scripted and fast; validating at creation needs a decision on those.
  4. getProviderForModel is still a classifier (any slash id classifies as OpenRouter) because free-auto candidates discovered live (for example openrouter/owl-alpha) must classify. The gate is the resolver and the two boundaries, not the classifier.
  5. An unknown concrete :free id now resolves to openrouter/free-auto, as A.0.10 asks. NodeRoom has no price row for the spec's measured free pick nvidia/nemotron-3-super-120b-a12b:free, so a request naming it is served by the free-auto route rather than that model.

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.priceRequest, priceRequestStrict, reserveDispatch - tier-aware through modelCatalog.priceTokens, and ONLY for one request's prompt (the ladder meter uses the strict price and the reservation). adapter.priceRun prices usage summed over requests at the row's base tier (priceAggregateTokens): a sum of 60K + 60K prompts is not a 120K prompt.
  4. convexModel.convexPriceRequest, exactConvexUsageCost, convexAttemptCostReservationUsd - per-request, tier-aware; convexPriceRun is the base-tier aggregate. calculateRequestCost(useCachedInput) is per-request and reads the tier's own cache rate.
  5. creditModel private priceRun and proofloopModelTracking cost estimate - aggregate (base tier, priceAggregateTokens).
  6. scripts/benchmark/pricingAtRun.ts (moved out of run.ts in round 3, which runs the benchmark on import) - pricingAtRunFor PREFERS the OpenRouter /models snapshot (priceFromOpenRouterInfo, dated by that run's fetchedAt + routeSnapshotId, recorded on every row's pricingAtRun) and falls back to (1) through catalogPricingAtRun, then to a flagged fallback_estimate ($1/$5). It is not purely a catalog reader.
  7. scripts/proofloop-proxy-model-sweep.ts (:131/:238) - prices from the live OpenRouter catalog fetched when the sweep runs (dated fetchedAt per row); no fallback table.
  8. Chat.tsx price chips - read (1), show the base tier.

Round 3 (review defect 5): sources 6 and 7 are no longer excluded. Their per-token to per-1M conversion is code that can disagree with (1), and A.0.6 allows no exclusion. tests/modelPricingAgreement.test.ts now runs source 6 (pricingAtRunFor / catalogPricingAtRun) and source 7 (the sweep script itself in --dry-run, fake fetch) on a dated OpenRouter /models fixture (2026-10-10, literal per-token strings) and asserts {inPerM, outPerM} (and the cache-read rate) equal the catalog row for every OpenRouter-transport refresh row. Reverted to check: prompt * 1_000_000 -> * 100_000 in pricingAtRun.ts, and parsed * 1_000_000 -> * 100_000 in the sweep: each fails its agreement test (Codex's 10x undercharge now fails). The no-hand-typed-table check stays, now pointed at pricingAtRun.ts and asserting run.ts imports it.

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.

Round 2 (review defect 2): the tier is chosen per REQUEST only (reserveDispatch, meter settlement, runtime priceStep = priceRequest / convexPriceRequest, calculateRequestCost, exactConvexUsageCost). Aggregated usage is priced at base-tier rates (priceRun, convexPriceRun, creditModel, proofloopModelTracking), and the ladder sums meter.dispatches[].costUsd (metered) or the runtime's per-step usage.costUsd (scripted) instead of repricing summed usage. priceRun('claude-haiku-5-5', 120000, 2000) was $0.065 while two settled 60K/1K requests cost $0.013; a run of those two now reports $0.013. HAIKU_5_5_LONG_CONTEXT also carries the vendor's cachedInputPer1M: 0.05, so cached tokens above 100K no longer bill at $0.50/M.

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. Round 2: the LangChain bridge now passes { strict: true } through bindTools on the downgrade too (asserted in the LangChain request-shape test).
  • 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, capture/reasoning.ts, providerParserLive, and, new in round 2, scripts/benchmark/probe.ts and scripts/eval/providerParseSmoke.ts 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. Round 2: the pre-flight request ceiling and USD estimate multiply a free-auto route by openRouterFreeAutoLimit() (OPENROUTER_FREE_AUTO_LIMIT, default 8, max 20), the fan-out the adapter enforces, not by --free-auto-top.

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; round 2 also drives judge-demo-gif, judge-state-captures, spreadsheetbench-chart-visual-grade, gemini-demo-media-judge, gemini-ui-review, painJudge, the chart-visual probe, walkthrough-review (CLI default and shipped config), scripts/benchmark/probe.ts, providerParseSmoke and the startup-diligence provider eval through a fake transport; the AST temperature checks stay), 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/modelRefreshCallSites.test.ts (new, round 2) - the five DEFAULT_AGENT_MODEL call sites with no unit-reachable transport (convex/agent.ts x2, agentJobRunner.ts, streaming.ts, Chat.tsx) are pinned at the expression that owns the default; tests/qaMatrix.test.ts now checks EVERY AGENT_MODEL ?? ... fallback in convex/agent.ts, not the first. Re-inlining gemini-3.5-flash at each of the five fails a named test.
  • 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 and each of its five call sites, 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. Round 3 correction: that claim did not hold for the scripts/benchmark/probe.ts gpt-6.1-sol row (the test answered OpenAI with a 404 and asserted nothing about the request; Codex swapped the id and it stayed green). It is now asserted on the wire (POST https://api.openai.com/v1/responses, body.model === "gpt-6.1-sol", no sampling keys, no tools) and reverting the id to gpt-6.1-solx fails it. The round-3 reversions (cost-class guards in the adapter and in convexModel, the resolver throw, the failed-rung cost, the alias fan-out, both pricing conversions, the probe id) were each run and each fails its named test.

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

Round 3, head 6dabf77f:

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

tests/dockerSandboxProbe.test.ts, which timed out under the parallel run in round 1, passed in this one. tests/qaMatrix.test.ts still checks every AGENT_MODEL ?? ... fallback in convex/agent.ts against the ladder; its failure is the unrecorded gemini-3.8-flash ladder row (merge blocker 1); nothing was weakened. The ladder was not run live and no paid provider call was made.

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 15, 2026 6:54pm 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/20261015T185338Z.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/20261015T185344858Z-cf4d9f59_dirty.7207ac97749795b0/cascade-healthy.json
  • docs/eval/traces/credit/20261015T185344858Z-cf4d9f59_dirty.7207ac97749795b0/delta-incomplete.json
  • docs/eval/traces/credit/20261015T185344858Z-cf4d9f59_dirty.7207ac97749795b0/mapping-correct.json
  • docs/eval/traces/credit/20261015T185344858Z-cf4d9f59_dirty.7207ac97749795b0/mapping-misbind.json
  • docs/eval/traces/credit/20261015T185344858Z-cf4d9f59_dirty.7207ac97749795b0/summit-stressed.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/ladder_L1_read_scripted.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/ladder_L2_edit_scripted.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/ladder_L3_conflict_scripted.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/ladder_L4_blocked_scripted.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/ladder_L5_large_range_scripted.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/ladder_L6_long_horizon_scripted.json
  • docs/eval/traces/ladder/20261015T185344544Z-cf4d9f593ba0baf51b6f252daa972a42f4d3277d_dirty.712cbf00306fc18c/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/pricingAtRun.ts
  • scripts/benchmark/probe.ts
  • scripts/benchmark/run.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/proofloop-free-model-gauge.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/bankerToolBenchNodeAgentGeneral.ts
  • src/eval/proofloopModelTracking.ts
  • src/eval/spreadsheetBenchChartVisualProbe.ts
  • src/eval/spreadsheetBenchRunner.ts
  • src/nodeagent/capture/reasoning.ts
  • src/nodeagent/core/adaptiveRouter.ts
  • src/nodeagent/core/creditModel.ts
  • src/nodeagent/core/runtime.ts
  • src/nodeagent/guardrails/egressPolicy.ts
  • src/nodeagent/integrations/langchain.ts
  • src/nodeagent/integrations/modelInterop.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/convexModelFreeAutoMode.test.ts
  • tests/modelAdapterProviderNeutralRoute.test.ts
  • tests/modelAliasClaude.test.ts
  • tests/modelCatalogRefresh.test.ts
  • tests/modelCostClass.test.ts
  • tests/modelPricingAgreement.test.ts
  • tests/modelRefreshCallSites.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>
- long-context price tier is applied per request only (priceRequest / convexPriceRequest in
  reserveDispatch, meter settlement, runtime priceStep, calculateRequestCost,
  exactConvexUsageCost); summed usage is priced at base-tier rates (priceAggregateTokens) and
  the ladder sums settled dispatch costs; haiku-5-5 long-context tier gets cachedInputPer1M 0.05
- tests that fail on revert for the five DEFAULT_AGENT_MODEL call sites, every remaining judge,
  benchmark and smoke script through a fake transport, and both AGENT_MODEL fallbacks in qaMatrix
- providerParserLive env-precedence test uses a non-default env id again
- LangChain bridge passes strict: true on the forced tool_choice downgrade
- probe.ts and providerParseSmoke.ts send the explicit claude effort; providerParseSmoke
  compares the provider to "gemini" so the Gemini smoke reaches Google
- ladder pre-flight request ceiling and estimate multiply free-auto by OPENROUTER_FREE_AUTO_LIMIT
- pricing-source inventory lists run.ts (OpenRouter snapshot first) and the proxy sweep

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
- SPEC A.0.10: resolveModelAlias throws UnknownModelError for an id that is not priced, not
  declared by the alias table and not free-looking, so adapter model() and convexModel() (and
  the fallback lists they read) refuse it before any request; an unknown -free / :free id
  resolves to openrouter/free-auto. Pricing and telemetry readers use lookupModelAlias
- a free primary no longer falls back to a paid model: adapter fallbackModelFor and convexModel
  drop the paid candidate unless allowPaidFallback is set and a reservation exists (SpendMeter /
  finite maxCostUsd)
- ladder runRung reports the sum of its settled dispatch costs when a rung fails after charged
  dispatches (was hardcoded 0)
- ladder request ceiling and estimate resolve the route alias before the free-auto fan-out test
- probe.ts gpt-6.1-sol is asserted on the wire (URL, model, no sampling)
- benchmark/run.ts pricing moved to pricingAtRun.ts; the pricing agreement test runs it and the
  --dry-run sweep on a dated OpenRouter /models fixture
- tests that dispatched never-heard-of ids now assert the refusal; their invariants stay on the
  alias-declared unpriced id claude-sonnet-4.5

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

This branch was successfully deployed

1 active deployment
Preview — 6dabf77f Deployed Oct 15, 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