singularity-forge/src
Mikael Hugo 7570aac4b7 feat(sf): generation-aware failover + canonical-keyed metrics
Two parallel refactors building on the model-registry consolidation:

1. Generation-aware failover (model-route-failure.js, agent-end-recovery.js)

   - resolveNextModelRoute now takes unitType so it knows whether the
     caller is solver-pinned per ADR-0079 (autonomous-solver). When pinned,
     rejects candidates whose canonicalIdFor() differs from the failed
     route's canonical id — closes the latent solver-invariant violation
     where kimi-coding/kimi-k2.6 could silently fail over to
     ollama-cloud/kimi-k2.5:cloud (different generation).
   - Cross-generation failover in non-pinned units now emits a structured
     logWarning so generation downgrades are visible in traces instead of
     looking like an equivalent route switch.

2. Canonical-keyed performance metrics (model-learner.js)

   - .sf/model-performance.json now keys by canonical_id with an
     {aggregate, by_route} sub-shape instead of fused provider/wire-model
     strings. Cross-route history per model is now coherent — kimi-k2.6
     reached via kimi-coding accumulates into the same aggregate as
     reached via openrouter.
   - Migration runs at boot: detects old shape (no 'aggregate' key in
     unit-type blob values), distributes each entry into by_route,
     recomputes aggregate, writes a backup to
     .sf/model-performance.json.pre-canonical-backup. Unmappable route
     keys land in _unmapped so nothing is dropped.
   - getRouteStats(taskType, routeKey) added for per-route failover
     ordering; existing getRankedModels emits canonical IDs for
     cross-route strength queries.

3. Tests

   - model-registry.test.ts: bundled in this commit (Swarm A's test file
     was left untracked when the registry module was committed).
   - model-route-failure.test.ts: 12 tests covering solver-pin guard,
     same-canonical multi-route failover, generation-downgrade log emit.
   - model-learner-canonical.test.ts: 17 tests covering migration
     round-trip, aggregate invariant, _unmapped bucket, and zero-default
     reads.
   - model-learner.test.ts: one existing test updated for the new
     _unmapped.by_route shape on bare model IDs.

4. Results

   - Targeted tests: 147/147 across registry, route-failure, learner,
     learner-canonical.
   - Full npm run test:unit: 4707 pass, 0 fail, 83 skipped (no new
     regressions vs pre-edit baseline of 4669).

Work parallelized across two Sonnet 4.6 sub-agents in isolated git
worktrees. Contract authored in docs/dev/drafts/model-registry-contract.md
(committed earlier in 1d753af6b) and consumed by both agents.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 04:15:08 +02:00
..
resources feat(sf): generation-aware failover + canonical-keyed metrics 2026-05-14 04:15:08 +02:00
tests test: migrate node:test imports to vitest and stabilize timing thresholds 2026-05-13 21:30:21 +02:00
web feat(notifications): tag remaining auto/loop/register-hooks notices + trace-writer 2026-05-10 20:14:22 +02:00
app-paths.ts sf snapshot: uncommitted changes after 93m inactivity 2026-05-06 11:37:27 +02:00
bundled-extension-paths.ts feat: introduce repo-vcs skill and add JSDoc annotations across core modules 2026-05-01 21:36:32 +02:00
bundled-resource-path.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
claude-cli-check.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
cli-logs.ts sf snapshot: uncommitted changes after 93m inactivity 2026-05-06 11:37:27 +02:00
cli-stats.ts style: format repository with biome 2026-05-05 14:31:16 +02:00
cli-status.ts feat(notifications): NOTICE_KIND enum, schema v2 dedup, sf-db cleanup 2026-05-10 20:13:58 +02:00
cli-web-branch.ts fix: make autonomous mode canonical 2026-05-05 15:42:10 +02:00
cli.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
env.ts feat: migrate src/ core TS files to LogTape structured logging 2026-05-08 21:01:08 +02:00
errors.ts feat: introduce repo-vcs skill and add JSDoc annotations across core modules 2026-05-01 21:36:32 +02:00
extension-discovery.ts fix: consolidate extensions into sf, migrate kernel.ts, fix test suite 2026-05-11 02:40:52 +02:00
extension-registry.ts feat: introduce repo-vcs skill and add JSDoc annotations across core modules 2026-05-01 21:36:32 +02:00
headless-answers.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
headless-context.ts feat(wiki): wire .sf/wiki/ as tracked context source 2026-05-11 19:24:23 +02:00
headless-events.ts Make SF direct command surface baseline 2026-05-08 01:34:07 +02:00
headless-import-backlog.ts sf snapshot: uncommitted changes after 49m inactivity 2026-05-12 16:45:04 +02:00
headless-query.ts sf snapshot: uncommitted changes after 49m inactivity 2026-05-08 01:07:24 +02:00
headless-types.ts feat(notifications): NOTICE_KIND enum, schema v2 dedup, sf-db cleanup 2026-05-10 20:13:58 +02:00
headless-ui.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
headless-uok-status.ts feat(sf): fix gate health — import, DB fallback, and enrich status uok 2026-05-11 18:47:42 +02:00
headless-usage.ts feat(headless,gemini-cli): add sf headless usage + unify gemini quota path 2026-05-14 03:42:53 +02:00
headless.ts feat(headless,gemini-cli): add sf headless usage + unify gemini quota path 2026-05-14 03:42:53 +02:00
help-text.ts feat(headless,gemini-cli): add sf headless usage + unify gemini quota path 2026-05-14 03:42:53 +02:00
interactive-session-lock.ts fix: enforce one interactive sf per repo 2026-05-05 20:55:53 +02:00
loader.ts fix(lint): reformat 6 files touched during web dep upgrade 2026-05-10 12:10:10 +02:00
logger.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
logo.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
models-resolver.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
onboarding.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
pi-migration.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
project-sessions.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
provider-migrations.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
remote-questions-config.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
resource-loader.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
rtk.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
security-overrides.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
startup-model-validation.ts chore: commit current workspace state 2026-05-05 14:46:18 +02:00
startup-timings.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
tool-bootstrap.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00
traces.ts sf snapshot: uncommitted changes after 49m inactivity 2026-05-08 01:07:24 +02:00
update-check.ts fix: clean provider surfaces and core build 2026-05-05 16:31:53 +02:00
update-cmd.ts fix: clean provider surfaces and core build 2026-05-05 16:31:53 +02:00
web-mode.ts fix(sf): recover model routes and self-feedback 2026-05-02 22:07:10 +02:00
welcome-screen.ts fix: update test snapshots for queryInstruction and complete /sf prefix Phase 2 deprecation 2026-05-09 00:17:47 +02:00
wizard.ts refactor: rename pi-* packages to forge-native names (Phase 1) 2026-05-10 11:28:01 +02:00
worktree-cli.ts sf snapshot: uncommitted changes after 43m inactivity 2026-05-05 21:39:56 +02:00
worktree-name-gen.ts batch: snapshot of in-flight v2 work 2026-04-29 12:42:31 +02:00