* Sync the index with the current file state. * * Change detection is filesystem-based, never git: a (size, mtime) stat * pre-filter skips unchanged files, then a content-hash compare confirms real * changes. This works in non-git projects and catches committed changes from * `git pul
(
onProgress?: (progress: IndexProgress) => void,
/**
* Watcher fast path: the exact project-relative paths the OS reported as
* changed. When provided, reconciliation runs over ONLY these paths —
* per-path logic identical to the full walk (stat pre-filter, hash
* confirm, the #1240 removal/resurrection flow) — skipping the O(repo)
* scan and tracked-load. Callers must pass undefined whenever the change
* set is not exactly known (directory removals, event overflow): the full
* scan-diff remains the ground truth those cases need (#1285).
*/
scopedPaths?: string[],
/**
* Writer-side WAL pressure valve (#1539). Called after every changed file
* is stored, when no extraction transaction is open, so a checkpoint can
* safely catch up before the next file grows the WAL further.
*/
backpressure?: () => Promise<void> | null,
/** Inspect changed inputs before deletion/re-extraction cascades their edges. */
onFileChange?: (filePath: string, content?: string) => void
)
| 3082 | * `git pull`/`checkout`/`merge`/`rebase` that `git status` cannot see. |
| 3083 | */ |
| 3084 | async sync( |
| 3085 | onProgress?: (progress: IndexProgress) => void, |
| 3086 | /** |
| 3087 | * Watcher fast path: the exact project-relative paths the OS reported as |
| 3088 | * changed. When provided, reconciliation runs over ONLY these paths — |
| 3089 | * per-path logic identical to the full walk (stat pre-filter, hash |
| 3090 | * confirm, the #1240 removal/resurrection flow) — skipping the O(repo) |
| 3091 | * scan and tracked-load. Callers must pass undefined whenever the change |
| 3092 | * set is not exactly known (directory removals, event overflow): the full |
| 3093 | * scan-diff remains the ground truth those cases need (#1285). |
| 3094 | */ |
| 3095 | scopedPaths?: string[], |
| 3096 | /** |
| 3097 | * Writer-side WAL pressure valve (#1539). Called after every changed file |
| 3098 | * is stored, when no extraction transaction is open, so a checkpoint can |
| 3099 | * safely catch up before the next file grows the WAL further. |
| 3100 | */ |
| 3101 | backpressure?: () => Promise<void> | null, |
| 3102 | /** Inspect changed inputs before deletion/re-extraction cascades their edges. */ |
| 3103 | onFileChange?: (filePath: string, content?: string) => void |
| 3104 | ): Promise<SyncResult> { |
| 3105 | await initGrammars(); // Initialize WASM runtime (grammars loaded lazily below) |
| 3106 | const startTime = Date.now(); |
| 3107 | let filesChecked = 0; |
| 3108 | let filesAdded = 0; |
| 3109 | let filesModified = 0; |
| 3110 | let filesRemoved = 0; |
| 3111 | let nodesUpdated = 0; |
| 3112 | const changedFilePaths: string[] = []; |
| 3113 | // `file\0name` definition pairs for the files this sync touches, sampled |
| 3114 | // BEFORE their nodes are replaced/deleted. Compared against the post-store |
| 3115 | // pairs below to derive `definitionDelta` (CG-33). |
| 3116 | const pairsBefore = new Set<string>(); |
| 3117 | |
| 3118 | onProgress?.({ |
| 3119 | phase: 'scanning', |
| 3120 | current: 0, |
| 3121 | total: 0, |
| 3122 | }); |
| 3123 | |
| 3124 | const filesToIndex: string[] = []; |
| 3125 | const failedFilePaths: string[] = []; |
| 3126 | // === Filesystem reconcile (git-independent) === |
| 3127 | // The source of truth for "what changed" is the filesystem vs the indexed |
| 3128 | // state — never git. We enumerate the current source files and reconcile |
| 3129 | // each against the DB. A cheap (size, mtime) stat pre-filter skips unchanged |
| 3130 | // files without reading or hashing them, so the expensive read+hash+parse |
| 3131 | // only runs for files that actually changed. This catches edits/adds/deletes |
| 3132 | // whether or not the project uses git, and crucially also catches committed |
| 3133 | // changes from `git pull`/`checkout`/`merge`/`rebase` — which `git status` |
| 3134 | // cannot see, because the working tree is clean afterward. |
| 3135 | const tSyncScan = Date.now(); |
| 3136 | let currentFiles: string[]; |
| 3137 | let trackedFiles: FileRecord[]; |
| 3138 | if (scopedPaths && scopedPaths.length > 0) { |
| 3139 | // Scoped reconcile: stat only the reported paths. filesChecked counts |
| 3140 | // the PATHS examined (not the files found) — it must stay non-zero even |
| 3141 | // when every scoped path was a deletion, because CodeGraph.watch() |
no test coverage detected