Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions LifeOS/install/LIFEOS/PULSE/modules/user-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,13 @@ export async function start(): Promise<void> {
if (!filename || !filename.endsWith(".md")) return
reindexDebounced(`${event}:${filename}`)
})
// Same async-error gap as the wiki watcher: an unopenable node under USER_DIR
// (socket, FIFO, dead symlink) emits 'error' asynchronously, which the catch
// below cannot see, and unhandled it takes the process down. The initial scan
// has already built the index, so log and carry on without live updates.
state.watcher.on("error", (err: unknown) => {
console.warn(`[${MODULE_NAME}] Watcher error (continuing without it):`, err)
})
console.log(`[${MODULE_NAME}] Watching ${USER_DIR}`)
} catch (err) {
console.warn(`[${MODULE_NAME}] Watch failed, polling disabled:`, err)
Expand Down
20 changes: 19 additions & 1 deletion LifeOS/install/LIFEOS/PULSE/modules/wiki.ts
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,13 @@ const pendingPaths: Set<string> = new Set()
function startWatchers(): void {
stopWatchers()

const watchPaths = [LIFEOS_DIR]
// Watch only the roots the reindexer consumes. Watching all of LIFEOS_DIR is
// both too broad and too narrow: too broad because it pulls in MEMORY/, the
// busiest subtree in an install (constant .jsonl appends) and the place where
// non-regular files appear — a unix socket there kills a recursive watcher
// outright; too narrow because skills/ and hooks/ live under ~/.claude, not
// under LIFEOS/, so changes there were never actually watched.
const watchPaths = [DOCUMENTATION_DIR, KNOWLEDGE_DIR, ALGORITHM_DIR]

for (const watchPath of watchPaths) {
if (!existsSync(watchPath)) continue
Expand All @@ -499,6 +505,18 @@ function startWatchers(): void {
}
})

// A recursive watch walks every node beneath its root, including ones that
// cannot be opened: unix sockets, FIFOs, dead symlinks, unreadable dirs.
// Those surface as an ASYNC 'error' event on the watcher, which the catch
// below cannot see — unhandled, it terminates the process. Under a
// supervisor Pulse then crash-loops for as long as the node exists, and
// because the walk happens during startup it presents as "Pulse won't
// start" rather than "one file is unwatchable". Watching is best-effort by
// design, so degrade to that instead of dying.
watcher.on("error", (err: unknown) => {
console.warn(`[wiki] watcher error on ${watchPath} (continuing without it): ${String(err)}`)
})

watchers.push(watcher)
} catch {
// File watching is best-effort; manual refresh still rebuilds on restart.
Expand Down