From 9aff96861c42b2d6ab1c08163cf2056e0d4e7b46 Mon Sep 17 00:00:00 2001 From: Tim Date: Wed, 26 Aug 2026 20:06:24 -0400 Subject: [PATCH] Advance persists its own catalog when no working catalog is given The head confirm inside CompareAndAdvanceBranch reopens the store when a peer changed it, and that reload can replace the live session catalog with the peer's freshly written working set. Persisting the working set from the live session after that bound the peer's content to the newly advanced head, so the staleness gate accepted it: a fast-forward merge racing a concurrent commit could publish a working set missing the merged rows, and the peer's next commit made the loss permanent. Advancing with no explicit working catalog means the working set is the new catalog itself; persist that. Co-Authored-By: Claude Fable 5 --- src/doltlite_config.c | 9 ++++++++- src/doltlite_core.c | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/doltlite_config.c b/src/doltlite_config.c index 5880d8eaaa..5b3b4507e2 100644 --- a/src/doltlite_config.c +++ b/src/doltlite_config.c @@ -220,7 +220,14 @@ int doltliteMaybeSeedRepo(sqlite3 *db){ "Initialize data repository", NULL, NULL, 0, 0, &seedHash); if( rc!=SQLITE_OK ) return rc; - return doltliteAdvanceBranch(db, &seedHash, &emptyCatalog, 0); + /* Tables created before the seed live only in the session; the working + ** set must carry them, not the seed's empty catalog. */ + { + ProllyHash liveCatalog; + rc = doltliteFlushCatalogToHash(db, &liveCatalog); + if( rc!=SQLITE_OK ) return rc; + return doltliteAdvanceBranch(db, &seedHash, &emptyCatalog, &liveCatalog); + } } int doltliteConfigRegister(sqlite3 *db){ diff --git a/src/doltlite_core.c b/src/doltlite_core.c index 2353f183ed..a30da2e254 100644 --- a/src/doltlite_core.c +++ b/src/doltlite_core.c @@ -722,7 +722,13 @@ static int doltliteAdvanceBranchWithState( } } - rc = doltlitePersistWorkingSetWithHash(db, pWorkingCatHash); + /* A null working catalog used to flush the live session here, but the + ** head confirm can have reloaded that session from a peer's freshly + ** written working set: the persist then binds the peer's content to the + ** new head and the staleness gate waves it through. Advancing with no + ** explicit working catalog means the working set IS the new catalog. */ + rc = doltlitePersistWorkingSetWithHash( + db, pWorkingCatHash ? pWorkingCatHash : pCatalogHash); if( rc!=SQLITE_OK ){ return doltliteRestoreTxnStateOnFailure(db, pSaved, rc); }