From 3fa01a453897c4bddc81aff7284b20910c74adf2 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Wed, 22 Jul 2026 11:06:41 -0700 Subject: [PATCH] =?UTF-8?q?fix(cycle):=20ambiguous=20local=5Fpath=20resolv?= =?UTF-8?q?es=20no=20source=20=E2=80=94=20don't=20stamp=20an=20arbitrary?= =?UTF-8?q?=20pick?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit resolveSourceForDir used LIMIT 1, so when two sources share a local_path the dir-keyed freshness stamp (new in this PR) fired for a nondeterministic one — exactly the 'freshness stamp that lies' the cycleSourceId comment warns against, and the CI failure in test/dream.test.ts ('gbrain dream (no --source) leaves all sources untouched'). Require exactly one match; ambiguous or no match falls back to the pre-v0.18 behavior (undefined), same as before this PR for the no-match case. Co-Authored-By: Claude Fable 5 --- src/core/cycle.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/core/cycle.ts b/src/core/cycle.ts index b1f332862..c5c253b96 100644 --- a/src/core/cycle.ts +++ b/src/core/cycle.ts @@ -854,7 +854,10 @@ interface SyncPhaseResult extends PhaseResult { /** * Resolve the source id for a brain directory by looking up the sources * table. Returns undefined when no registered source matches (falls back - * to pre-v0.18 global config.sync.* keys). + * to pre-v0.18 global config.sync.* keys) OR when MORE than one source + * claims the path — an ambiguous match must not scope phases or stamp + * last_full_cycle_at for an arbitrarily-picked source (the "freshness + * stamp that lies" this resolution exists to prevent). */ async function resolveSourceForDir( engine: BrainEngine, @@ -865,10 +868,10 @@ async function resolveSourceForDir( if (brainDir === null) return undefined; try { const rows = await engine.executeRaw<{ id: string }>( - `SELECT id FROM sources WHERE local_path = $1 LIMIT 1`, + `SELECT id FROM sources WHERE local_path = $1 LIMIT 2`, [brainDir], ); - return rows[0]?.id; + return rows.length === 1 ? rows[0]!.id : undefined; } catch { // sources table might not exist on very old brains — fall through. return undefined;