Summary
gbrain sync --source <id> correctly tracks the per-source sync state
(last_commit, last_sync_at on the sources row) but does not
attribute imported pages to the named source — pages always land in
default. As a result, gbrain sources list shows 0 pages for any
source whose content arrives via sync, and source partitioning is
effectively non-functional for the sync path.
Repro
mkdir -p /tmp/foo && cd /tmp/foo && git init -q && echo "# hello" > a.md
git add . && git commit -qm init
gbrain sources add foo --path /tmp/foo --federated
gbrain sync --source foo
gbrain sources list
Expected:
foo federated 1 pages last sync ...
Actual (v0.21.0):
default federated N+1 pages
foo federated 0 pages last sync ...
Root cause
commands/sync.ts calls importFile/importFromFile without threading
sourceId:
commands/sync.ts:464 — await importFile(engine, filePath, to, { noEmbed });
commands/sync.ts:498 — await importFile(engine, filePath, path, { noEmbed });
core/import-file.ts:338 — importFromFile's opts type is
{ noEmbed?: boolean }. There's no source_id parameter, so the page
write defaults to 'default' regardless of which source the sync was
scoped to.
sourceId is otherwise correctly used for sync-state plumbing in
commands/sync.ts (readSyncAnchor, writeSyncAnchor,
readChunkerVersion, writeChunkerVersion — lines ~174-245). It just
never reaches the page-write call.
Suggested fix
- Extend
importFromFile opts to { noEmbed?: boolean; sourceId?: string }.
- Inside
importFromFile, pass source_id to the page write (default
to 'default' when not provided, preserving current behavior).
- In
commands/sync.ts, pass { noEmbed, sourceId: opts.sourceId } at
both importFile call sites.
- Recommended: add
--source <id> to gbrain import <dir> for parity
— commands/import.ts likewise hard-codes attribution.
Adjacent observation
core/sync.ts:202-203 supports include/exclude glob filtering inside
isSyncable, but commands/sync.ts:354 constructs syncOpts as
opts.strategy ? { strategy: opts.strategy } : undefined —
include/exclude from source config are never plumbed through. Likely
a separate gap, but in the same area; could fold into the same fix or
split.
Version
gbrain 0.21.0
Summary
gbrain sync --source <id>correctly tracks the per-source sync state(
last_commit,last_sync_aton thesourcesrow) but does notattribute imported pages to the named source — pages always land in
default. As a result,gbrain sources listshows0 pagesfor anysource whose content arrives via sync, and source partitioning is
effectively non-functional for the sync path.
Repro
Expected:
Actual (v0.21.0):
Root cause
commands/sync.tscallsimportFile/importFromFilewithout threadingsourceId:commands/sync.ts:464—await importFile(engine, filePath, to, { noEmbed });commands/sync.ts:498—await importFile(engine, filePath, path, { noEmbed });core/import-file.ts:338—importFromFile's opts type is{ noEmbed?: boolean }. There's nosource_idparameter, so the pagewrite defaults to
'default'regardless of which source the sync wasscoped to.
sourceIdis otherwise correctly used for sync-state plumbing incommands/sync.ts(readSyncAnchor,writeSyncAnchor,readChunkerVersion,writeChunkerVersion— lines ~174-245). It justnever reaches the page-write call.
Suggested fix
importFromFileopts to{ noEmbed?: boolean; sourceId?: string }.importFromFile, passsource_idto the page write (defaultto
'default'when not provided, preserving current behavior).commands/sync.ts, pass{ noEmbed, sourceId: opts.sourceId }atboth
importFilecall sites.--source <id>togbrain import <dir>for parity—
commands/import.tslikewise hard-codes attribution.Adjacent observation
core/sync.ts:202-203supportsinclude/excludeglob filtering insideisSyncable, butcommands/sync.ts:354constructssyncOptsasopts.strategy ? { strategy: opts.strategy } : undefined—include/excludefrom source config are never plumbed through. Likelya separate gap, but in the same area; could fold into the same fix or
split.
Version
gbrain 0.21.0