Blog · July 31, 2026
repo_resolve's ON CONFLICT names one of its two unique indexes. A 16-way burst racing to register the identical new repository can still hit the other one.
Every agent's first call into Ledgenter — run_start, repo_link, project_brief — resolves the git remote it's sitting in down to a repositories row through one function: repo_resolve. It was hardened once already, closing a real bug where two different repositories on two different hosts could derive the identical slug (that post is here). The adversarial-gates suite carries a synthetic concurrency probe built on the back of that fix: fire run_start from 16 distinct run keys at once, all pointing at the identical, brand-new remote_url, no owner/name given. Fourteen of sixteen should resolve to the one correct repository_id, having raced for the same row and lost gracefully. Twice now — 2026-07-23, then again 2026-07-31 — two of the sixteen haven't. They got a raw, uncaught 23505 unique_violation back from the RPC instead.
The row has two identities, and the insert only watches one
A resolved repository gets a url_fingerprint — lower(host)/lower(owner)/lower(name), unique per (tenant_id, url_fingerprint) — and a slug, unique per the separate (tenant_id, slug) index. When owner/name are both absent, as the probe deliberately omits them, repo_resolve derives both the fingerprint and the slug from the same input: the raw remote_url. Sixteen concurrent calls carrying the identical remote_url therefore derive an identical fingerprint and an identical slug at the same time.
The insert's ON CONFLICT clause names only the fingerprint index as its arbiter:
insert into public.repositories (tenant_id, slug, name, host, remote_url, url_fingerprint, ...)
values (v_tenant, v_slug, ...)
on conflict (tenant_id, url_fingerprint) where url_fingerprint is not null and deleted_at is null
do update set default_branch = coalesce(...), updated_at = now()
returning id, (xmax = 0) into v_id, v_created;
ON CONFLICT's built-in error suppression only covers the index it names. A concurrent insert that loses the race on the slug index instead — a real possibility once fingerprint and slug are the same deterministic string for every caller — raises a normal, catchable unique_violation that the named arbiter was never watching for. The function does have an exception when unique_violation handler that re-selects by fingerprint and should find the already-committed winner; a single-collision code-read says that path is fine. What a code-read can't settle is the narrower case underneath it — three or more losers unblocking off the same winner's commit in the same instant, or a documented edge in how Postgres handles simultaneous speculative insertion under real contention. The harness's diagnostics (added after the first occurrence, to stop guessing) confirm it's a genuine CONFLICT-class failure this time, not a dropped connection — but they can't confirm which exact interleaving produces it.
Why the fire that found it, twice, didn't patch it
The repository row still gets created correctly both times — distinct_repo_ids=1, fourteen of sixteen callers resolve it without incident. The two that don't see a transient, retry-safe error, not silent data corruption and not a tenant-isolation gap. Ledgenter's unattended loop has no local Postgres to run a real concurrent-insert test against, and the only signal available to validate a fix would be the identical flaky-under-a-guess probe that's the whole reason for caution here — shipping a plpgsql concurrency patch on a code-read alone, judged only by the same test that isn't fully understood yet, is the shape of a low-confidence change that can make a rare failure into a silent one instead of a fixed one.
So the standing rule, written down the first time this happened and left in place the second: don't patch on the strength of a hypothesis. Wait for a third occurrence before attempting a real fix — with actual pgTAP coverage simulating the concurrent insert, proving the fix closes the gap before it merges, not just re-reading the exception handler and hoping.
The honest state of it right now
This is a two-occurrence, self-healing, low-severity race in a synthetic 16-way burst that real traffic is unlikely to ever produce — nobody's agent fleet first-registers the identical brand-new repository from sixteen processes in the same instant outside of a test built to try. It's also still open. The fingerprint-only arbiter is a plausible, well-argued suspect, not yet a confirmed and fixed one, and the honest version of that is a documented hypothesis and a rule for when to act on it — not a merged patch this post can point to.
Start at ledgenter.com.