Repository navigation
Conversation
The two red checks are pre-existing base breakage, not this PR
Byte-identical. Unrelated PR #8599 fails on the same two jobs in the same CI window. Already tracked, no new issue filed:
Per #8542, a green board can't be the evidence here — the fail-fast masks downstream gates. Local validation for this PR: Everything else on this PR is green: Vitest, Unit 1/3/4, ESLint, Docs Gates, Merge integrity, semgrep, dast-smoke. |
…8601) The T05 Task-Aware Smart Routing config was persisted to settings.taskRouting by PUT /api/settings/task-routing but never read back, so it silently reverted to enabled:false + the hardcoded default model map on every restart. Two root causes, both fixed: - No boot hydration existed. Adds hydrateTaskRoutingConfig(settings), wired into src/instrumentation-node.ts next to the Thinking-Budget restore (diegosouzapw#5312). It accepts either the JSON string the route persists or an already-parsed object, and fails open on malformed values. applyRuntimeSettings does not cover this key, same as the Global System Prompt (diegosouzapw#2470). - The config lived in a plain module-level `let`, which is duplicated per module graph — a boot hydration would have landed on the instrumentation graph's copy and never reached the one src/sse/handlers/chat.ts reads. This is the exact break diegosouzapw#5312 fix-A hit on the VPS. Moves the store to the globalThis pattern already used by thinkingBudget.ts and systemPrompt.ts. Runtime stats are never restored from the persisted blob. Note the hydration is wired into instrumentation-node.ts, not the unused src/server-init.ts.
7d43ec9 to
593de28
Compare
Same tip fix as diegosouzapw#8657 so Merge integrity is green without waiting for that PR to land. Regenerated via generate-agent-skills --apply.
…adowing (#8602, #8603) (#8605) * fix(sse): restore task-aware routing config on restart (#8601) The T05 Task-Aware Smart Routing config was persisted to settings.taskRouting by PUT /api/settings/task-routing but never read back, so it silently reverted to enabled:false + the hardcoded default model map on every restart. Two root causes, both fixed: - No boot hydration existed. Adds hydrateTaskRoutingConfig(settings), wired into src/instrumentation-node.ts next to the Thinking-Budget restore (#5312). It accepts either the JSON string the route persists or an already-parsed object, and fails open on malformed values. applyRuntimeSettings does not cover this key, same as the Global System Prompt (#2470). - The config lived in a plain module-level `let`, which is duplicated per module graph — a boot hydration would have landed on the instrumentation graph's copy and never reached the one src/sse/handlers/chat.ts reads. This is the exact break #5312 fix-A hit on the VPS. Moves the store to the globalThis pattern already used by thinkingBudget.ts and systemPrompt.ts. Runtime stats are never restored from the persisted blob. Note the hydration is wired into instrumentation-node.ts, not the unused src/server-init.ts. * docs(changelog): add fragment for #8604 task-routing boot restore * fix(sse): route task-aware defaults by intent, guard fitness pattern order (#8602, #8603) Two related defects in the hand-maintained model-quality tables. #8602 — DEFAULT_TASK_MODEL_MAP hardcoded literal provider/model ids (openai/gpt-4o, gemini/gemini-2.5-flash-lite, deepseek/deepseek-chat, ...). Wrong twice over: the ids rotted by a generation or two, and applyTaskAwareRouting overwrites body.model directly, so a literal target skipped auto-combo's 13-factor scoring (quota, circuit-breaker health, cost, latency, stability), connection cooldown and model lockout — hard-failing for any operator with no connection for that provider. Refreshing the strings would only reset the rot clock, so the defaults now name auto/* INTENTS that resolve against the operator's actually connected backends: coding -> auto/coding analysis -> auto/reasoning vision -> auto/vision summarization -> auto/chat:fast background -> auto/chat:cheap creative and chat stay pass-through. Operators can still pin a specific model via PUT /api/settings/task-routing; only the shipped defaults change. No provider/model literal remains in the module. #8603 — the pattern-shadowing fix LANDED UPSTREAM while this PR was open (9f5be22, Train 1D). lookupStaticFitnessTable now ranks patterns longest-first, so gpt-4o-mini no longer inherits gpt-4o's 0.9 and deepseek-v3.2 no longer inherits deepseek-v3's 0.85. This PR therefore no longer changes that behaviour — the upstream scan is kept verbatim. What remains for #8603 is the regression guard. The resolution chain hits the DB (user_override / arena_elo / models.dev tier) before reaching layer 4, so asserting the ordering through getTaskFitness would depend on DB fixture state. The layer is exposed as getStaticFitnessTableScore and pinned directly by taskFitness-pattern-order-8603.test.ts (7 cases), so the guarantee survives future edits to FITNESS_TABLE. Those 7 cases were written against this PR's original implementation and pass unchanged against the upstream one — independent confirmation that the two are behaviourally equivalent. * docs(changelog): add fragment for #8605 task-routing intent + fitness order
Closing — this change shipped inside #8605Thanks for this — it was a real fix, and it did land. Verified against The reason this PR stayed open is worth recording: the squash commit of #8605 (merged today) carries as its first sub-commit exactly Closing as merged-via-#8605. The boot-time hydration is live on the release branch. |
…adowing (diegosouzapw#8602, diegosouzapw#8603) (diegosouzapw#8605) * fix(sse): restore task-aware routing config on restart (diegosouzapw#8601) The T05 Task-Aware Smart Routing config was persisted to settings.taskRouting by PUT /api/settings/task-routing but never read back, so it silently reverted to enabled:false + the hardcoded default model map on every restart. Two root causes, both fixed: - No boot hydration existed. Adds hydrateTaskRoutingConfig(settings), wired into src/instrumentation-node.ts next to the Thinking-Budget restore (diegosouzapw#5312). It accepts either the JSON string the route persists or an already-parsed object, and fails open on malformed values. applyRuntimeSettings does not cover this key, same as the Global System Prompt (diegosouzapw#2470). - The config lived in a plain module-level `let`, which is duplicated per module graph — a boot hydration would have landed on the instrumentation graph's copy and never reached the one src/sse/handlers/chat.ts reads. This is the exact break diegosouzapw#5312 fix-A hit on the VPS. Moves the store to the globalThis pattern already used by thinkingBudget.ts and systemPrompt.ts. Runtime stats are never restored from the persisted blob. Note the hydration is wired into instrumentation-node.ts, not the unused src/server-init.ts. * docs(changelog): add fragment for diegosouzapw#8604 task-routing boot restore * fix(sse): route task-aware defaults by intent, guard fitness pattern order (diegosouzapw#8602, diegosouzapw#8603) Two related defects in the hand-maintained model-quality tables. diegosouzapw#8602 — DEFAULT_TASK_MODEL_MAP hardcoded literal provider/model ids (openai/gpt-4o, gemini/gemini-2.5-flash-lite, deepseek/deepseek-chat, ...). Wrong twice over: the ids rotted by a generation or two, and applyTaskAwareRouting overwrites body.model directly, so a literal target skipped auto-combo's 13-factor scoring (quota, circuit-breaker health, cost, latency, stability), connection cooldown and model lockout — hard-failing for any operator with no connection for that provider. Refreshing the strings would only reset the rot clock, so the defaults now name auto/* INTENTS that resolve against the operator's actually connected backends: coding -> auto/coding analysis -> auto/reasoning vision -> auto/vision summarization -> auto/chat:fast background -> auto/chat:cheap creative and chat stay pass-through. Operators can still pin a specific model via PUT /api/settings/task-routing; only the shipped defaults change. No provider/model literal remains in the module. diegosouzapw#8603 — the pattern-shadowing fix LANDED UPSTREAM while this PR was open (f5df2cf, Train 1D). lookupStaticFitnessTable now ranks patterns longest-first, so gpt-4o-mini no longer inherits gpt-4o's 0.9 and deepseek-v3.2 no longer inherits deepseek-v3's 0.85. This PR therefore no longer changes that behaviour — the upstream scan is kept verbatim. What remains for diegosouzapw#8603 is the regression guard. The resolution chain hits the DB (user_override / arena_elo / models.dev tier) before reaching layer 4, so asserting the ordering through getTaskFitness would depend on DB fixture state. The layer is exposed as getStaticFitnessTableScore and pinned directly by taskFitness-pattern-order-8603.test.ts (7 cases), so the guarantee survives future edits to FITNESS_TABLE. Those 7 cases were written against this PR's original implementation and pass unchanged against the upstream one — independent confirmation that the two are behaviourally equivalent. * docs(changelog): add fragment for diegosouzapw#8605 task-routing intent + fitness order
…adowing (diegosouzapw#8602, diegosouzapw#8603) (diegosouzapw#8605) * fix(sse): restore task-aware routing config on restart (diegosouzapw#8601) The T05 Task-Aware Smart Routing config was persisted to settings.taskRouting by PUT /api/settings/task-routing but never read back, so it silently reverted to enabled:false + the hardcoded default model map on every restart. Two root causes, both fixed: - No boot hydration existed. Adds hydrateTaskRoutingConfig(settings), wired into src/instrumentation-node.ts next to the Thinking-Budget restore (diegosouzapw#5312). It accepts either the JSON string the route persists or an already-parsed object, and fails open on malformed values. applyRuntimeSettings does not cover this key, same as the Global System Prompt (diegosouzapw#2470). - The config lived in a plain module-level `let`, which is duplicated per module graph — a boot hydration would have landed on the instrumentation graph's copy and never reached the one src/sse/handlers/chat.ts reads. This is the exact break diegosouzapw#5312 fix-A hit on the VPS. Moves the store to the globalThis pattern already used by thinkingBudget.ts and systemPrompt.ts. Runtime stats are never restored from the persisted blob. Note the hydration is wired into instrumentation-node.ts, not the unused src/server-init.ts. * docs(changelog): add fragment for diegosouzapw#8604 task-routing boot restore * fix(sse): route task-aware defaults by intent, guard fitness pattern order (diegosouzapw#8602, diegosouzapw#8603) Two related defects in the hand-maintained model-quality tables. diegosouzapw#8602 — DEFAULT_TASK_MODEL_MAP hardcoded literal provider/model ids (openai/gpt-4o, gemini/gemini-2.5-flash-lite, deepseek/deepseek-chat, ...). Wrong twice over: the ids rotted by a generation or two, and applyTaskAwareRouting overwrites body.model directly, so a literal target skipped auto-combo's 13-factor scoring (quota, circuit-breaker health, cost, latency, stability), connection cooldown and model lockout — hard-failing for any operator with no connection for that provider. Refreshing the strings would only reset the rot clock, so the defaults now name auto/* INTENTS that resolve against the operator's actually connected backends: coding -> auto/coding analysis -> auto/reasoning vision -> auto/vision summarization -> auto/chat:fast background -> auto/chat:cheap creative and chat stay pass-through. Operators can still pin a specific model via PUT /api/settings/task-routing; only the shipped defaults change. No provider/model literal remains in the module. diegosouzapw#8603 — the pattern-shadowing fix LANDED UPSTREAM while this PR was open (74e2e45, Train 1D). lookupStaticFitnessTable now ranks patterns longest-first, so gpt-4o-mini no longer inherits gpt-4o's 0.9 and deepseek-v3.2 no longer inherits deepseek-v3's 0.85. This PR therefore no longer changes that behaviour — the upstream scan is kept verbatim. What remains for diegosouzapw#8603 is the regression guard. The resolution chain hits the DB (user_override / arena_elo / models.dev tier) before reaching layer 4, so asserting the ordering through getTaskFitness would depend on DB fixture state. The layer is exposed as getStaticFitnessTableScore and pinned directly by taskFitness-pattern-order-8603.test.ts (7 cases), so the guarantee survives future edits to FITNESS_TABLE. Those 7 cases were written against this PR's original implementation and pass unchanged against the upstream one — independent confirmation that the two are behaviourally equivalent. * docs(changelog): add fragment for diegosouzapw#8605 task-routing intent + fitness order
Fixes #8601.
Problem
PUT /api/settings/task-routingpersists the T05 Task-Aware Smart Routing config tosettings.taskRouting, but nothing ever reads it back. After any restart the feature reverts toenabled: falseand the hardcodedDEFAULT_TASK_MODEL_MAP, discarding the operator's configuration.Two independent root causes — fixing only the first would not have worked.
A. No boot hydration
rg taskRouting -t ts src open-ssereturned only the write path and the Zod schema.applyRuntimeSettingsdoes not cover this key, so it needs an explicit hydration step — same as the Global System Prompt (#2470) and the Thinking-Budget config (#5312).B. Module-level
letis duplicated per module graphthinkingBudget.ts:63-68documents why this breaks: a module-level binding is duplicated per graph, so boot hydration on the instrumentation graph never reaches the copysrc/sse/handlers/chat.ts:573reads. This is the exact failure #5312 fix-A hit on the VPS.Fix
taskAwareRouter.ts— move the config store to theglobalThispattern used bythinkingBudget.tsandsystemPrompt.ts; addhydrateTaskRoutingConfig(settings), accepting either the JSON string the route writes or an already-parsed object, failing open on malformed input. Runtimestatsare never restored from the persisted blob.src/instrumentation-node.ts— call it next to the Thinking-Budget restore. Wired into the real boot path, not the unusedsrc/server-init.ts(called out as unused atinstrumentation-node.ts:366/:407).No behavior change when nothing is persisted: the in-memory defaults stay in place, and the feature remains off by default.
Validation (Hard Rule #18 — TDD)
tests/unit/task-routing-config-restore-8601.test.ts, 5 cases. Written first, confirmed red:Green after the fix — 20/20 including the pre-existing
chat-route-coveragesuite:The cross-module-graph case (root cause B) was separately proven to have teeth — importing a module under two distinct specifiers does yield two real instances, so the test would fail on the old
let-based store:Gates
npm run typecheck:core— cleannpx eslinton changed files — 4no-explicit-any, all pre-existing (4 on the base ref, 4 after;eslint-suppressions.jsonalready recordscount: 4for this file). No new violations, no baseline update needed.