Pre-Phase-7: routing resolver lands before connectors need it
Rather than bake model selection into each connector at ingest time, Archibald312 landed the routing surface first. Local inference (Ollama/vLLM) is deferred to post-launch; the resolver that will eventually dispatch to it is already there, tested, and audited. Every chat today still resolves to the user's requested model - the columns are nullable, so behavior is unchanged.
Two nullable model_preference text columns - one on projects, one on documents - are the entire schema change. The migration is in backend/migrations/model_routing_seam.sql.
backend/src/lib/llm/routing.ts implements resolveModelRouting() at 156 lines. Precedence is fixed: document-level first, then project-level, then the caller's requested model. If two documents in the same request carry different preferences, the resolver captures the conflict with a structured reason, takes the first non-null value, and records the decision - it doesn't block. Unknown model IDs are rejected with a recorded reason. All outcomes write into the routing_policy_applied jsonb column in audit_log, which Phase 6 provisioned precisely for this purpose.
streamChatWithTools accepts an optional routing context, dispatches against the resolved model, and records the resolution into the audit row. The main chat handler passes the project ID and the document IDs already in the in-memory document index. Because no preferences are set anywhere, every live request still routes to the user's requested model.
The test coverage for an "early seam" PR is unusually thorough: 166 lines across 8 tests covering document-over-project precedence, project-over-request precedence, conflict capture, unknown-model rejection at both layers, DB error tolerance, and the no-document-IDs skip path. Two manual checks in the test plan - a document-level override that audits correctly end-to-end, and a no-document chat that records a request-sourced resolution - were left unchecked at merge.
Phase 7 connectors set documents.model_preference at ingest. Phase 14 adds a local-inference adapter as another target. Neither requires revisiting dispatch sites.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?