persist AI provider connections while providers.json stays executable - #6421
Merged
Conversation
…utable (#6367) Adds the durable half of the provider connection graph designed in docs/plans/2026-09-06-provider-connections-and-harnesses.md, on top of the read-only preview #6366 shipped. Three new db-primary tables (ai_connections, ai_harness_bindings, ai_route_bindings) record which backend each harness talks to and which executable route it projects. data/providers.json remains the execution contract and stays fully materialized, so a downgraded release runs unchanged with the tables simply idle — and every saved selection, task pin, fallback reference and activeProvider keeps naming the same provider id it always did. The reconciliation pass is one function shared by boot, every legacy provider write and a re-upgrade after a downgrade, so those paths cannot drift apart. It acknowledges a projection whose file write landed, retries one that did not, and REFUSES to overwrite a third value some other writer put there — keeping the route's last valid executable settings readable and blocking further graph edits to that binding until it is repaired. A record deleted while the graph was not running drops its row without being resurrected; an edit that moves a binding's backend clones a shared connection rather than repointing another harness, updates an exclusive one in place, and splits a binding whose siblings now disagree. The toolkit gains one injected extension point (onProvidersSaved) plus applyProviderPatches, so it stays self-contained while PortOS learns about every write to the file it owns. Also included: GET /api/providers/management, revision-checked link/unlink and link-preview endpoints, DELETE for an emptied connection, and migration 358, which parks an untouched pre-graph copy of the install's own providers.json before anything can rewrite it.
atomantic
force-pushed
the
claim/issue-6367
branch
from
September 6, 2026 19:32
6879b77 to
7267394
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
ai_connections,ai_harness_bindings,ai_route_bindings) record which backend each harness talks to and which executable route it projects.data/providers.jsonstays the execution contract and stays fully materialized, so a downgraded release runs unchanged with the tables idle — and every saved selection, task pin, fallback reference andactiveProviderkeeps naming the provider id it always did.onProvidersSaved) plusapplyProviderPatches, so it stays self-contained while PortOS learns about every write to the file it owns.GET /api/providers/management, revision-checkedlink/link/preview/unlinkon a binding, andDELETEfor an emptied connection. The flatGET /api/providersand every existing mutation are untouched; an install without a database gets an explicitPROVIDER_GRAPH_UNAVAILABLErather than a silently empty graph.providers.json; that derived path is declared inmigrationOwnedPaths.jsand ships no seed.privacyNeverFederates.test.js.Linking UI is out of scope (#6369); this PR ships the service validation and projection contracts it will call.
Test plan
cd server && npm test— 2017 files, 40237 tests green.cd client && npm test— 872 files, 10612 tests green.npm run test:db— 34 files, 291 tests green againstportos_test, including the newproviderGraphStore.db.test.js(partial unique indexes,(binding_id, mode)uniqueness, FK refusal, JSONB snapshot round-trip, transactional rollback).server/lib/providerGraphRecords.test.js(import round-trip fidelity, DTO sanitization, crash recovery incl. the refuse-a-third-value case, detach/update/split, partial-restore in both directions),server/services/providerGraph.test.js(stage → write → acknowledge ordering, re-entrancy latch, revision gate),server/routes/providers.graph.test.js,server/lib/aiToolkit/providers.hostPersistence.test.js,scripts/migrations/358-provider-connection-graph.test.js,server/services/sharing/providerGraphNeverFederates.test.js.ollamareviewer returned inconclusive (no model configured for the ollama reviewer on this install); it is the optional reviewer, so no round was applied.Closes #6367