fix(startup): keep incremental catch-up alive when watcher startup throws

startWatching can throw outside its internal guard (lock-file writes,
gitignore reads). Wrap it so a watcher failure degrades to a warning
instead of skipping the project's catch-up update, which would leave
the index stale.
This commit is contained in:
Giancarlo Erra
2026-06-11 11:43:06 +01:00
parent f3362bbe7d
commit fa4dd92196
2 changed files with 29 additions and 4 deletions
+14 -4
View File
@@ -267,11 +267,21 @@ async function resumeProject(
return;
}
// Index is complete — start watcher and do incremental catch-up
// Index is complete — start watcher and do incremental catch-up.
// Watcher startup failure must not skip the catch-up below: a project
// without a live watcher is degraded, but a project with a stale index
// is broken for search.
if (!isWatching(resolvedPath)) {
const started = await startWatching(resolvedPath);
if (started) {
logger.info("Auto-resume: started file watcher", { projectPath: resolvedPath });
try {
const started = await startWatching(resolvedPath);
if (started) {
logger.info("Auto-resume: started file watcher", { projectPath: resolvedPath });
}
} catch (err) {
logger.warn("Auto-resume: failed to start watcher, continuing with incremental catch-up", {
projectPath: resolvedPath,
error: err instanceof Error ? err.message : String(err),
});
}
}
+15
View File
@@ -554,6 +554,21 @@ describe("autoResumeIndexedProjects (multi-project modes)", () => {
expect(mockUpdateProjectIndex).not.toHaveBeenCalled();
});
it("still runs the incremental catch-up when watcher startup throws", async () => {
process.env.SOCRATICODE_AUTO_RESUME_PROJECTS = dirA;
mockStartWatching.mockRejectedValue(new Error("inotify limit reached"));
await autoResumeIndexedProjects();
// A project without a live watcher is degraded; a project with a stale
// index is broken for search. The catch-up must survive watcher failure.
expect(logger.warn).toHaveBeenCalledWith(
"Auto-resume: failed to start watcher, continuing with incremental catch-up",
expect.objectContaining({ projectPath: dirA, error: "inotify limit reached" }),
);
expect(mockUpdateProjectIndex).toHaveBeenCalledWith(dirA);
});
it("unrecognized SOCRATICODE_AUTO_RESUME value warns and falls back to default cwd behavior", async () => {
process.env.SOCRATICODE_AUTO_RESUME = "yes";
mockListCollections.mockResolvedValue([collA]);