feat(git-providers): the agent's and the thread's repositories become references - #7035
Merged
Merged
Conversation
viktormarinho
force-pushed
the
t3code/git-providers-change-requests
branch
from
September 8, 2026 17:45
85b758e to
d94f3ea
Compare
Base automatically changed from
t3code/git-providers-change-requests
to
main
September 8, 2026 19:16
… references Migration 201 gave three consumers a real `repository_id` and left the two bindings that matter most to a person using the product still in JSON: the repository an AGENT works in (`connections.metadata.githubRepo` — a virtual MCP is a connections row) and the extra checkouts a THREAD holds (`threads.metadata.githubRepos`, what `TASK_ADD_REPO` appends to). That cost three things. Deleting a repository left every agent pointing at nothing with no FK to say so. "Which agents use this repository" was a JSON scan. And `resolveRepoTarget` had to keep a resolve-by-identity step — a lowercase path match, per request — purely because the binding carried no id. The thread list also loses a real bug. Its append is a `jsonb_agg` rebuild inside one UPDATE, written that way because two concurrent `TASK_ADD_REPO` calls lost each other under read-modify-write — with the pod already holding the checkout the lost entry described, so nothing looked wrong until the pod was recreated without it. A primary key makes that an `ON CONFLICT DO NOTHING`. Its dedup key was also `lower(owner/name)` with no host, so two `acme/site` on different hosts collided; a reference to a row cannot. EXPAND ONLY, and deliberately so: this changes no behaviour. Both bindings are dual-written, JSON included, because during a rolling deploy a pod on the previous release still reads only the JSON — and for the same reason the JSON, not the reference, is still the more complete source while both versions write. The read flip and the JSON removal are the next two releases. The backfill only links: 201 already created a repositories row for every agent binding. Threads it skipped, so the rows a checkout list names are created here first, anonymous like every other identity-only source. Sized against prod before writing: 311 agents carry a binding out of 11,575 connections, and 29 threads out of 95,678 carry a checkout list. Both backfills are a rounding error; the `connections` column is indexed partially for the same reason. Covered by a real-Postgres test, because the whole migration is SQL against two differently shaped metadata columns (`connections.metadata` is TEXT, `threads` is jsonb) and an in-memory fake would agree with a version that links nothing: every JSON binding ends up referenced, a repository only a thread names gets created, a connection whose metadata is not JSON does not abort the run, the backfill is idempotent, and the JSON is left intact.
viktormarinho
force-pushed
the
t3code/git-providers-repository-refs
branch
from
September 8, 2026 19:30
1f8cddf to
c01e74d
Compare
Each of these tests resets the schema in a per-test hook, and that reset truncates every table in it — a cost that grows with every migration the repo adds. They already ran ~2s against bun's 5s default; on a loaded CI runner the hook outlasts it, and bun aborting a hook mid-flight surfaces as an unrelated "driver has already been destroyed" from the teardown racing the seed. Give the hook a ceiling that matches what it actually does.
decocms Bot
pushed a commit
that referenced
this pull request
Sep 8, 2026
PR: #7035 feat(git-providers): the agent's and the thread's repositories become references Bump type: minor - decocms (apps/api/package.json): 4.339.1 -> 4.340.0 - @decocms/native (apps/native/package.json): 4.339.1 -> 4.340.0 Deploy-Scope: server
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.
Stacked on #7022. The expand half of moving the last two repository bindings
out of JSON.
Migration 201 gave
task_board_items,task_board_item_prsandorg_repo_synca realrepository_id, and left the two that matter most to aperson using the product:
connections.metadata.githubRepo, since avirtual MCP is a
connectionsrow;threads.metadata.githubRepos, the listTASK_ADD_REPOappends to so one run can hold several.Why it's worth a migration
ON DELETE SET NULL. Delete a repositorytoday and every agent pointing at it via JSON silently points at nothing.
resolveRepoTarget—findByRefon a lowercasedowner/name— exists only because the bindingcarries no id. It is a join standing in for a foreign key.
jsonb_aggrebuildinside one UPDATE, written that way because two concurrent
TASK_ADD_REPOcalls lost each other under read-modify-write — with the pod already holding
the checkout the lost entry described, so nothing looked wrong until the pod
was recreated without it. A primary key makes that
ON CONFLICT DO NOTHING.Its dedup key was also
lower(owner/name)with no host, so twoacme/siteon different hosts collided — the same class fixed inrepoKeyFromCloneUrlin feat(git-providers): first-class repositories behind a provider interface #6939. A reference cannot collide.Shape
githubReposis 1:N with insertion order and dedup by identity, so it is ajoin table; the agent's is 1:1 nullable, so it is a column. (The plural list
lives on the thread, not the agent — worth stating, because the names
invite the opposite reading.)
Expand only — this changes no behaviour
Both bindings are dual-written, JSON included. During a rolling deploy a
pod on the previous release still reads only the JSON, and for the same reason
the JSON — not the reference — is still the more complete source while both
versions write. Preferring the reference today would lose repos an old pod
added mid-deploy.
So: this release writes both. The next flips the read to
reference ?? JSONand dropsresolveRepoTarget's identity step. A thirddrops the JSON.
The backfill only links — 201 already created a repositories row for every
agent binding (its fourth insert). Threads it skipped deliberately, so the rows
a checkout list names are created here first, anonymous like every other
identity-only source.
Sized against prod before writing
311 agents carry a binding, out of 11,575 connections; 29 threads carry a
checkout list, out of 95,678. Both backfills are a rounding error, and the
connectionsindex is partial for the same reason.Testing
Real-Postgres, because the whole migration is SQL against two differently
shaped metadata columns (
connections.metadatais TEXT,threads.metadataisjsonb) and an in-memory fake would agree with a version that links nothing:
every JSON binding ends up referenced, an agent with none stays null, a
repository only a thread names gets created, a connection whose metadata is
not JSON does not abort the run (the guard 201 needed), the backfill is
idempotent, and the JSON is left intact.
check,fmt,lint,knipclean.Summary by cubic
Makes the agent's and thread's repository bindings real foreign keys instead of JSON metadata, without changing behavior yet (expand-only).
repository_idtoconnections(agents) and a newthread_repositoriestable with a composite primary key.TASK_ADD_REPOconcurrent calls and dedup collisions across hosts.Written for commit 5b257f5. Summary will update on new commits.