Skip to content

persist AI provider connections while providers.json stays executable - #6421

Merged
atomantic merged 1 commit into
mainfrom
claim/issue-6367
Sep 6, 2026
Merged

persist AI provider connections while providers.json stays executable#6421
atomantic merged 1 commit into
mainfrom
claim/issue-6367

Conversation

@atomantic

Copy link
Copy Markdown
Owner

Summary

  • Adds the durable half of the provider connection graph from the design record, on top of the read-only preview Preview provider connection identity and harness bindings without changing execution #6366 shipped. Three 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 stays 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 and activeProvider keeps naming the provider id it always did.
  • One reconciliation pass serves 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 another writer put there — keeping the route's last valid executable settings readable and blocking further graph edits to that binding until repaired.
  • Deletions are not 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 CLI/TUI siblings now disagree.
  • The vendored 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.
  • New endpoints: GET /api/providers/management, revision-checked link / link/preview / unlink on a binding, and DELETE for an emptied connection. The flat GET /api/providers and every existing mutation are untouched; an install without a database gets an explicit PROVIDER_GRAPH_UNAVAILABLE rather than a silently empty graph.
  • Migration 358 is gated on its INPUT and parks an untouched pre-graph copy of the install's own providers.json; that derived path is declared in migrationOwnedPaths.js and ships no seed.
  • Graph rows never federate — endpoints, credentials and projection snapshots are machine-local, with a guard test mirroring 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 against portos_test, including the new providerGraphStore.db.test.js (partial unique indexes, (binding_id, mode) uniqueness, FK refusal, JSONB snapshot round-trip, transactional rollback).
  • New suites: 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.
  • Local ollama reviewer returned inconclusive (no model configured for the ollama reviewer on this install); it is the optional reviewer, so no round was applied.

Closes #6367

…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
atomantic merged commit 79cd266 into main Sep 6, 2026
12 checks passed
@atomantic
atomantic deleted the claim/issue-6367 branch September 6, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Persist provider connections with recoverable legacy route projection

1 participant