singularity-forge/src/resources/extensions
Mikael Hugo 089bf0cbeb fix(model-learner): resolve canonical-id lazy-load race + 23 wire-id aliases
Of 52 dispatches in this repo's journal this session, 51 landed in
.sf/model-performance.json's _unmapped bucket — meaning the live-outcome
learner couldn't tell which provider/model succeeded or failed. Only
1 dispatch (google-gemini-cli/gemini-3-flash-preview) bucketed correctly.

Root cause was NOT just missing aliases — it was a lazy-load race:
- model-learner.js declared canonicalIdFor as a fire-and-forget dynamic
  import side-effect at module bottom
- metrics.js called recordOutcome() synchronously after
  `await import("./model-learner.js")` resolved — before the registry
  injection promise settled
- Result: _canonicalIdForFn was null for the first dispatch every session.
  Every session. Since the file shipped.

Why nobody noticed: _unmapped is a bucket, not an error. No throw, no
warning, no UI surface. Selection still worked because benchmark-selector
+ static hand-tuned scores carry the routing decision. Only the
feedback loop (recordOutcome → adjust scores) was silently severed.

Fix:
- model-learner.js: export `registryReady` promise instead of swallowing it
- metrics.js: await registryReady before recordOutcome()
- model-registry.ts: 23 new CANONICAL_BY_ROUTE entries covering the actual
  production fallback chain — zai/glm-4.5{-air,-flash,5,5.1,5-turbo,4.6,4.7,4.7-flash},
  mistral/codestral-latest + devstral-2512 + devstral-{small,medium}-* +
  mistral-{large,medium,small}-latest, google-gemini-cli/gemini-{2.5-pro,3-flash-preview,3.1-pro-preview},
  opencode-go/{glm-5,glm-5.1,mimo-v2-omni,mimo-v2-pro}

Also adds opt-in backfillModelPerformanceFromJournal(basePath) to
reclassify the existing 51 _unmapped records from past journal events.
Never auto-runs; backs up the old file before overwriting.

Tests: 16 in canonical-id-mapping.test.mjs covering pattern matching,
non-mappable cases, bare canonical-id passthrough, and the backfill
path. Full suite 1906 pass, no regressions.

Known follow-up: CANONICAL_BY_ROUTE uses mixed casing (MiniMax-M2.7 vs
minimax-m2) — should be standardized lowercase in a future pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 14:02:58 +02:00
..
async-jobs
aws-auth
bg-shell
browser-tools
claude-code-cli
context7
github-sync
guardrails
mac-tools
mcp-client
ollama
remote-questions
search-the-web
sf fix(model-learner): resolve canonical-id lazy-load race + 23 wire-id aliases 2026-05-15 14:02:58 +02:00
shared
universal-config
voice
ask-user-questions.js
get-secrets-from-user.js
package.json