{"version":3,"file":"index.cjs","names":[],"sources":["../../src/collection-watchers/config.ts","../../src/collection-watchers/clock.ts","../../src/collection-watchers/reconciler.ts","../../src/collection-watchers/watcher.ts"],"sourcesContent":["// Host-injected configuration for the collection-completion watchers.\n// The reconciler logic + watcher plumbing are host-agnostic; the\n// notification TAXONOMY (which plugin namespace, how a record priority\n// maps to a bell severity) and the in-app ROUTING (the deep-link a bell\n// row navigates to) are host-specific, so the host supplies them via an\n// adapter. MulmoClaude wires its legacy notification machinery; a future\n// MulmoTerminal wires its own routes + pluginData shape.\n\nimport type { NotifierSeverity } from \"../notifier\";\n\n/** Two-level urgency a pending record can carry, derived from the\n *  schema's `notifyWhen` spec. The host maps this onto its own severity\n *  scale via `priorityToSeverity`. */\nexport type CompletionPriority = \"normal\" | \"high\";\n\nexport interface CollectionWatcherLogger {\n  info: (message: string, data?: Record<string, unknown>) => void;\n  warn: (message: string, data?: Record<string, unknown>) => void;\n}\n\n/** The host-specific notification surface the reconciler binds to. The\n *  reconciler owns the internal `legacyId` key (it encodes slug+itemId\n *  and round-trips it through `pluginData`); the adapter only wraps /\n *  unwraps it into whatever shape the host's bell expects. */\nexport interface CollectionNotificationAdapter {\n  /** Plugin namespace these bell entries publish under (MulmoClaude: \"todo\"). */\n  pluginPkg: string;\n  /** Map a record's completion priority onto the host's bell severity. */\n  priorityToSeverity: (priority: CompletionPriority) => NotifierSeverity;\n  /** Build the in-app deep-link the bell row routes to on click. */\n  buildNavigateTarget: (slug: string, itemId: string) => string;\n  /** Wrap the reconciler's internal key + priority into the host's\n   *  `pluginData` shape. Stored verbatim on the entry; recovered via\n   *  `readEntry`. */\n  buildPluginData: (input: { legacyId: string; slug: string; itemId: string; priority: CompletionPriority; navigateTarget: string }) => unknown;\n  /** Recognise a bell entry produced by this reconciler and recover its\n   *  internal key + stored priority. Returns null for entries that didn't\n   *  originate here, so `listAll()` scans skip foreign entries. */\n  readEntry: (pluginData: unknown) => { legacyId: string; priority: CompletionPriority } | null;\n}\n\nconst NOOP_LOG: CollectionWatcherLogger = { info: () => {}, warn: () => {} };\n\nlet adapter: CollectionNotificationAdapter | null = null;\nlet activeLogger: CollectionWatcherLogger = NOOP_LOG;\n\n/** Wire the host adapter + logger. Call once at startup, before\n *  `startCollectionWatchers` or any direct reconcile call. */\nexport function configureCollectionWatchers(config: { adapter: CollectionNotificationAdapter; log?: CollectionWatcherLogger }): void {\n  ({ adapter } = config);\n  activeLogger = config.log ?? NOOP_LOG;\n}\n\nexport function requireAdapter(): CollectionNotificationAdapter {\n  if (!adapter) throw new Error(\"collection-watchers: configureCollectionWatchers() not called\");\n  return adapter;\n}\n\nexport function log(): CollectionWatcherLogger {\n  return activeLogger;\n}\n\n/** Test-only: clear the host wiring. */\nexport function resetCollectionWatchersConfig(): void {\n  adapter = null;\n  activeLogger = NOOP_LOG;\n}\n\nexport function errMsg(err: unknown): string {\n  return err instanceof Error ? err.message : String(err);\n}\n","// Evaluation-only injectable clock. When MULMOCLAUDE_FAKE_NOW is set to\n// a parseable date/datetime string, every reconcile pass derives \"now\"\n// from it instead of the wall clock — letting evaluation runs and tests\n// advance time deterministically (a `triggerField` coming due, a `spawn`\n// successor's own trigger firing later) without waiting for real days to\n// pass. Unset or unparseable (i.e. normal operation) → real wall clock.\n// Read per call, so a long-lived server picks up changes without restart.\nexport function evalNow(): Date {\n  const raw = process.env.MULMOCLAUDE_FAKE_NOW;\n  if (!raw) return new Date();\n  const parsed = new Date(raw);\n  return Number.isNaN(parsed.getTime()) ? new Date() : parsed;\n}\n","// Bell-notification reconciler for collections whose schema declares\n// `completionField`. Driven by `watcher.ts`, which calls into the\n// functions below on file-system events and on boot.\n//\n// The model is **convergent**: a watcher event re-reads the record from\n// disk and the reconciler enforces the invariant\n//\n//   bell entry exists for (slug, itemId)  ↔\n//     schema has completionField  ∧\n//     file exists  ∧\n//     `String(item[completionField])` ∉ completionDoneValues\n//     (∧ trigger due ∧ notifyWhen matches, when declared)\n//\n// Each reconcile is idempotent (`ensure*` / `clear*` no-op when state\n// already matches). This is why event-type quirks of `fs.watch`\n// (`rename` vs `change`, missed events, atomic-write coalescence) don't\n// matter — every event re-derives the desired state from the file.\n//\n// Lookup uses a deterministic internal `legacyId` derived from\n// `<slug>:<itemId>`, stashed on each entry's `pluginData` via the host\n// adapter, so the clear path and the dedup check both find the entry\n// without a side state file.\n\nimport { clear as notifierClear, listAll, publish as notifierPublish, updateForPlugin as notifierUpdate, type NotifierEntry } from \"../notifier\";\nimport { whenMatches, type CollectionItem, type CollectionSchema } from \"../collection\";\nimport { type DiscoveryOptions, listItems, readItem, type IoOptions, isTriggerDue, maybeSpawnSuccessor, loadCollection } from \"../collection/server\";\nimport { type CompletionPriority, errMsg, log, requireAdapter } from \"./config.js\";\nimport { evalNow } from \"./clock.js\";\n\n/** The internal-id prefix every collection-completion bell entry carries.\n *  Used both to build new keys and to filter sweep candidates from the\n *  active bell. */\nconst LEGACY_ID_PREFIX = \"collection-completion:\";\n\n/** Stable key encoding slug + item, round-tripped through the entry's\n *  `pluginData` so we can find it later without a side state file. Slug +\n *  itemId are upstream-validated via `safeSlugName`, which forbids the\n *  colon separator, so the two-segment parse below is unambiguous. */\nfunction completionLegacyId(slug: string, itemId: string): string {\n  return `${LEGACY_ID_PREFIX}${slug}:${itemId}`;\n}\n\n/** Decode a key back into its (slug, itemId) pair, or null if the string\n *  didn't originate from this module. Used by the sweep step. */\nfunction parseCompletionLegacyId(legacyId: string): { slug: string; itemId: string } | null {\n  if (!legacyId.startsWith(LEGACY_ID_PREFIX)) return null;\n  const body = legacyId.slice(LEGACY_ID_PREFIX.length);\n  const colon = body.indexOf(\":\");\n  if (colon < 0) return null;\n  return { slug: body.slice(0, colon), itemId: body.slice(colon + 1) };\n}\n\n/** The human-readable label shown in a completion notification's title.\n *  Uses the schema's `displayField` value when declared and non-empty;\n *  otherwise falls back to the record's primaryKey (`itemId`). */\nexport function resolveDisplayLabel(schema: CollectionSchema, item: CollectionItem, itemId: string): string {\n  const { displayField } = schema;\n  if (!displayField) return itemId;\n  const raw = item[displayField];\n  if (raw === undefined || raw === null) return itemId;\n  const label = String(raw).trim();\n  return label.length > 0 ? label : itemId;\n}\n\n/** True iff the schema declares completion tracking AND the item's\n *  `completionField` value (stringified) is in `completionDoneValues`. */\nexport function itemIsDone(schema: CollectionSchema, item: CollectionItem): boolean {\n  const { completionField, completionDoneValues } = schema;\n  if (!completionField || !completionDoneValues) return false;\n  const raw = item[completionField];\n  if (raw === undefined || raw === null) return false;\n  return completionDoneValues.includes(String(raw));\n}\n\n/** Every active bell entry whose key matches this (slug, itemId).\n *  Returns multiple when defensive cleanup is needed. Scans `listAll()`\n *  — cheap because the active set is bounded. */\nasync function findActiveEntries(slug: string, itemId: string): Promise<NotifierEntry[]> {\n  const adapter = requireAdapter();\n  const legacyId = completionLegacyId(slug, itemId);\n  const entries = await listAll();\n  return entries.filter((entry) => adapter.readEntry(entry.pluginData)?.legacyId === legacyId);\n}\n\nasync function findActiveEntryIds(slug: string, itemId: string): Promise<string[]> {\n  return (await findActiveEntries(slug, itemId)).map((entry) => entry.id);\n}\n\n/** Per-key in-flight lock. Serializes concurrent `ensureItemNotification`\n *  calls for the same (slug, itemId) so the `findActiveEntries → publish`\n *  check stays atomic across callers — not just across watcher events.\n *  `listAll` bypasses the engine's write queue, so without this lock two\n *  reconcile paths (a watcher event + a `reconcileAllItems` pass that a\n *  readdir-triggered event raced) could both miss each other's in-flight\n *  publish and produce duplicate entries. */\ninterface EnsureLock {\n  promise: Promise<void>;\n}\nconst ensureLocks = new Map<string, EnsureLock>();\n\n/** Bell priority for a record: the FIRST flagged value in `notifyWhen.in`\n *  (most urgent) reads `high`, every other flagged value `normal`.\n *  Collections with no `notifyWhen` (notify for every open record) stay\n *  `normal`. */\nfunction notifyPriorityForItem(schema: CollectionSchema, item: CollectionItem): CompletionPriority {\n  const spec = schema.notifyWhen;\n  if (!spec) return \"normal\";\n  const value = item[spec.field] === undefined || item[spec.field] === null ? \"\" : String(item[spec.field]);\n  return spec.in.indexOf(value) === 0 ? \"high\" : \"normal\";\n}\n\nasync function ensureItemNotification(\n  slug: string,\n  schema: CollectionSchema,\n  itemId: string,\n  displayLabel: string,\n  priority: CompletionPriority,\n): Promise<void> {\n  const legacyId = completionLegacyId(slug, itemId);\n  // Drain any in-flight publish for this key BEFORE our check + set. The\n  // drain + claim runs synchronously between `ensureLocks.get` and\n  // `ensureLocks.set`, so two callers can't both observe an empty slot.\n\n  while (true) {\n    const inflight = ensureLocks.get(legacyId);\n    if (!inflight) break;\n    await inflight.promise;\n  }\n  const lock: EnsureLock = { promise: doEnsureItemNotification(slug, schema, itemId, legacyId, displayLabel, priority) };\n  ensureLocks.set(legacyId, lock);\n  try {\n    await lock.promise;\n  } finally {\n    // Only clear the slot if it still points at OUR lock — a\n    // sufficiently-delayed cleanup must not stomp a later claim.\n    if (ensureLocks.get(legacyId) === lock) {\n      ensureLocks.delete(legacyId);\n    }\n  }\n}\n\n/** Converge any already-present bell entries to `priority`, updating in\n *  place (preserving id / position / createdAt) so a record whose flagged\n *  value changed while it stayed pending re-colours the bell without a\n *  clear+republish flicker. No-op when the stored priority already matches. */\nasync function reconcileEntrySeverity(slug: string, itemId: string, entries: NotifierEntry[], priority: CompletionPriority): Promise<void> {\n  const adapter = requireAdapter();\n  for (const entry of entries) {\n    const parsed = adapter.readEntry(entry.pluginData);\n    if (!parsed || parsed.priority === priority) continue;\n    await notifierUpdate(adapter.pluginPkg, entry.id, {\n      severity: adapter.priorityToSeverity(priority),\n      pluginData: adapter.buildPluginData({\n        legacyId: parsed.legacyId,\n        slug,\n        itemId,\n        priority,\n        navigateTarget: adapter.buildNavigateTarget(slug, itemId),\n      }),\n    });\n  }\n}\n\nasync function doEnsureItemNotification(\n  slug: string,\n  schema: CollectionSchema,\n  itemId: string,\n  legacyId: string,\n  displayLabel: string,\n  priority: CompletionPriority,\n): Promise<void> {\n  const adapter = requireAdapter();\n  try {\n    const existing = await findActiveEntries(slug, itemId);\n    if (existing.length > 0) {\n      await reconcileEntrySeverity(slug, itemId, existing, priority);\n      return;\n    }\n    const navigateTarget = adapter.buildNavigateTarget(slug, itemId);\n    // `lifecycle: \"action\"` — these are state-of-the-world entries\n    // mirroring an outstanding obligation (the item is pending), not\n    // transient pings. Validation requires a non-info severity and a\n    // non-empty `navigateTarget` (the slug + itemId deep-link).\n    await notifierPublish({\n      pluginPkg: adapter.pluginPkg,\n      severity: adapter.priorityToSeverity(priority),\n      lifecycle: \"action\",\n      title: `${schema.title}: ${displayLabel}`,\n      navigateTarget,\n      pluginData: adapter.buildPluginData({ legacyId, slug, itemId, priority, navigateTarget }),\n    });\n  } catch (err) {\n    log().warn(\"notify ensure failed\", { slug, itemId, error: errMsg(err) });\n  }\n}\n\n/** Idempotently clear EVERY bell entry that matches this (slug, itemId).\n *  Silent no-op when nothing matches. The \"every\" is defensive: if a\n *  duplicate ever slips through, this drains the lot. */\nexport async function clearItemNotification(slug: string, itemId: string): Promise<void> {\n  try {\n    const ids = await findActiveEntryIds(slug, itemId);\n    for (const entryId of ids) {\n      await notifierClear(entryId);\n    }\n  } catch (err) {\n    log().warn(\"notify clear failed\", { slug, itemId, error: errMsg(err) });\n  }\n}\n\n/** Reconcile one item to the desired bell state. Re-reads the record from\n *  disk so the decision is grounded in current truth, not in the event\n *  payload. Safe to call when the file is missing (delete path).\n *\n *  `ioOpts` flows into `readItem`'s workspace-containment check —\n *  production callers (the watcher) pass nothing; tests pass\n *  `{ workspaceRoot: <tmpdir> }` so the check accepts a fixture dataDir. */\nexport async function reconcileItem(\n  slug: string,\n  schema: CollectionSchema,\n  dataDir: string,\n  itemId: string,\n  ioOpts: IoOptions = {},\n  now: Date = evalNow(),\n): Promise<void> {\n  if (!schema.completionField) {\n    // Schema doesn't track completion — drop any stale entry.\n    await clearItemNotification(slug, itemId);\n    return;\n  }\n  const item = await readItem(dataDir, itemId, ioOpts);\n  if (item === null) {\n    await clearItemNotification(slug, itemId);\n    return;\n  }\n  // Recurrence: predicate-gated + create-if-absent, idempotent and\n  // independent of this item's own bell state. Runs before the done-clear\n  // below so marking an item done still spawns its successor.\n  await maybeSpawnSuccessor(slug, schema, dataDir, item, itemId, ioOpts);\n  if (itemIsDone(schema, item)) {\n    await clearItemNotification(slug, itemId);\n    return;\n  }\n  // Time gate: when the schema declares `triggerField`, suppress the bell\n  // until the clock reaches that date (minus `triggerLeadDays`).\n  // Unparseable date ⇒ fail safe (no bell); warn ONLY when the field carries\n  // a non-empty value that won't parse — an empty optional trigger date is a\n  // normal state and must not spam a WARN every reconcile tick.\n  if (schema.triggerField) {\n    const triggerRaw = item[schema.triggerField];\n    const due = isTriggerDue(triggerRaw, now, schema.triggerLeadDays);\n    const isEmpty = triggerRaw === undefined || triggerRaw === null || triggerRaw === \"\";\n    if (due === null && !isEmpty) {\n      log().warn(\"trigger date unparseable, suppressing bell\", { slug, itemId, triggerField: schema.triggerField });\n    }\n    if (due !== true) {\n      await clearItemNotification(slug, itemId);\n      return;\n    }\n  }\n  // Condition gate: when the schema declares `notifyWhen`, only bell\n  // records matching the predicate. Convergent — a record that stops\n  // matching has its bell cleared.\n  if (!whenMatches(schema.notifyWhen, item)) {\n    await clearItemNotification(slug, itemId);\n    return;\n  }\n  await ensureItemNotification(slug, schema, itemId, resolveDisplayLabel(schema, item, itemId), notifyPriorityForItem(schema, item));\n}\n\n/** Boot-time reconcile: walk every record under `dataDir` once and\n *  reconcile it. Catches up changes that happened while the server was\n *  down. Deleted items are covered by `sweepStaleActiveEntries`, not this\n *  function (it only sees files that exist). */\nexport async function reconcileAllItems(slug: string, schema: CollectionSchema, dataDir: string, ioOpts: IoOptions = {}, now: Date = evalNow()): Promise<void> {\n  if (!schema.completionField) return;\n  let items: CollectionItem[];\n  try {\n    items = await listItems(dataDir, ioOpts);\n  } catch (err) {\n    log().warn(\"reconcile list failed\", { slug, dataDir, error: errMsg(err) });\n    return;\n  }\n  const { primaryKey } = schema;\n  for (const item of items) {\n    const raw = item[primaryKey];\n    if (typeof raw !== \"string\" || raw.length === 0) continue;\n    await reconcileItem(slug, schema, dataDir, raw, ioOpts, now);\n  }\n}\n\n/** Boot-time sweep over the active bell: drop any entries whose underlying\n *  file is gone, whose collection was deleted, whose schema no longer\n *  tracks completion, or whose item is now done. Reverse-covers the cases\n *  `reconcileAllItems` misses (it only walks files that exist). */\nexport async function sweepStaleActiveEntries(opts: DiscoveryOptions = {}): Promise<void> {\n  const adapter = requireAdapter();\n  let entries;\n  try {\n    entries = await listAll();\n  } catch (err) {\n    log().warn(\"sweep list failed\", { error: errMsg(err) });\n    return;\n  }\n  for (const entry of entries) {\n    const own = adapter.readEntry(entry.pluginData);\n    if (!own) continue;\n    const parsed = parseCompletionLegacyId(own.legacyId);\n    if (!parsed) continue;\n    const { slug, itemId } = parsed;\n    try {\n      const collection = await loadCollection(slug, opts);\n      if (!collection || !collection.schema.completionField) {\n        await notifierClear(entry.id);\n        continue;\n      }\n      const item = await readItem(collection.dataDir, itemId, opts);\n      if (item === null || itemIsDone(collection.schema, item) || !whenMatches(collection.schema.notifyWhen, item)) {\n        await notifierClear(entry.id);\n      }\n    } catch (err) {\n      log().warn(\"sweep entry failed\", { slug, itemId, error: errMsg(err) });\n    }\n  }\n}\n\n/** Test-only: clear the per-key in-flight locks. */\nexport function _resetReconcilerLocksForTesting(): void {\n  ensureLocks.clear();\n}\n","// Filesystem watchers that drive collection-completion bell\n// notifications. One `fs.watch` per discovered collection's `dataDir`,\n// fanned out from a single boot call + a 30-second re-discovery interval\n// that catches newly-created / deleted collections (there is no\n// in-process \"collections changed\" event broadcast).\n//\n// Why a watcher, not just route hooks: the canonical pattern for\n// collection-skills has the agent Write records directly with the Write\n// tool — that path never hits the REST API, so a route-level hook would\n// miss most of the traffic the user generates. The watcher catches every\n// mutation regardless of who wrote the file.\n//\n// All decisions live in `reconciler.ts`; this module is pure plumbing:\n// discover, mkdir, fs.watch, forward events into the reconciler. Every\n// reconcile call is idempotent so fs.watch's well-known quirks (`rename`\n// vs `change`, atomic-write coalescence, filename === null on some\n// platforms) don't need special handling.\n\nimport { watch, type FSWatcher } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { discoverCollections, loadCollection, type DiscoveryOptions, type LoadedCollection } from \"../collection/server\";\nimport type { CollectionSchema } from \"../collection\";\nimport { errMsg, log } from \"./config.js\";\nimport { evalNow } from \"./clock.js\";\nimport { reconcileAllItems, reconcileItem, sweepStaleActiveEntries } from \"./reconciler.js\";\n\n// Collections don't get added / removed rapidly; 30 s is a comfortable\n// upper bound on how long a new schema can sit before its watcher is up.\nconst ONE_SECOND_MS = 1000;\nconst ONE_MINUTE_MS = 60 * ONE_SECOND_MS;\nconst REDISCOVERY_INTERVAL_MS = 30 * ONE_SECOND_MS;\n\n// Wall-clock tick that re-reconciles time-dependent collections (those\n// declaring `triggerField` and/or `spawn`). The fs.watcher only re-runs\n// the reconciler on FILE changes; a `triggerField` bell that should fire\n// \"when the clock reaches date X\" — and a `spawn` whose successor's own\n// trigger later comes due — change no file at that moment, so a periodic\n// re-derivation is required.\nconst TRIGGER_TICK_INTERVAL_MS = ONE_MINUTE_MS;\n\ninterface CollectionWatcher {\n  slug: string;\n  dataDir: string;\n  watcher: FSWatcher;\n  /** Last-seen serialized schema for change detection. When a rediscovery\n   *  tick observes a different value, the watcher's items are reconciled\n   *  and the cache is refreshed — this catches schema-only edits (e.g.\n   *  flipping `completionField` on or off) that don't touch any record\n   *  file and would otherwise leave bell state stale indefinitely. */\n  schemaJson: string;\n}\n\nconst watchers = new Map<string, CollectionWatcher>();\nlet rediscoveryTimer: ReturnType<typeof setInterval> | null = null;\nlet triggerTimer: ReturnType<typeof setInterval> | null = null;\nlet started = false;\n/** Discovery options threaded into every `discoverCollections` /\n *  `loadCollection` / `sweepStaleActiveEntries` call. Production: empty\n *  (live workspace). Tests: `{ workspaceRoot, userSkillsDir }` pointing\n *  at a fixture tree. Module-level so per-event handlers can read it\n *  without threading through every signature. */\nlet discoveryOpts: DiscoveryOptions = {};\n\n/** Per-key single-flight slot (declared here so `stopCollectionWatchers`\n *  can clear it during teardown). */\ninterface ReconcileSlot {\n  running: Promise<void>;\n  pending: boolean;\n}\nconst itemSlots = new Map<string, ReconcileSlot>();\n\n/** Test-only configuration knobs. Production callers pass nothing and get\n *  the live workspace defaults; tests pass a tmpdir-rooted `discoveryOpts`\n *  and override the tick cadences (or set them to `null` to disable the\n *  auto-ticks so the test drives sync manually). */\nexport interface CollectionWatcherOptions {\n  discoveryOpts?: DiscoveryOptions;\n  rediscoveryIntervalMs?: number | null;\n  triggerTickIntervalMs?: number | null;\n}\n\n/** Boot entry point: sweep stale active entries, then mount watchers for\n *  every discovered collection and arm the periodic re-discovery poll.\n *  Idempotent — a second call is a no-op. */\nexport async function startCollectionWatchers(opts: CollectionWatcherOptions = {}): Promise<void> {\n  if (started) return;\n  // `started` only flips on AFTER boot finishes. If sweep or syncWatchers\n  // throws mid-boot, reset state on failure so a supervisor / test\n  // harness can retry instead of being permanently latched.\n  discoveryOpts = opts.discoveryOpts ?? {};\n  try {\n    // Boot reconcile is split in two: sweep first (drop bell entries whose\n    // files / collections / schemas vanished while the server was down),\n    // then `syncWatchers` runs the per-collection forward fill. Both paths\n    // are idempotent and converge on the same end state.\n    await sweepStaleActiveEntries(discoveryOpts);\n    await syncWatchers();\n    const intervalMs = opts.rediscoveryIntervalMs === undefined ? REDISCOVERY_INTERVAL_MS : opts.rediscoveryIntervalMs;\n    if (intervalMs !== null) {\n      rediscoveryTimer = setInterval(() => {\n        syncWatchers().catch((err: unknown) => {\n          log().warn(\"watcher rediscovery failed\", { error: errMsg(err) });\n        });\n      }, intervalMs);\n      // `unref` so a clean process exit isn't blocked waiting for the tick.\n      rediscoveryTimer.unref();\n    }\n    const triggerMs = opts.triggerTickIntervalMs === undefined ? TRIGGER_TICK_INTERVAL_MS : opts.triggerTickIntervalMs;\n    if (triggerMs !== null) {\n      triggerTimer = setInterval(() => {\n        tickTimeTriggers().catch((err: unknown) => {\n          log().warn(\"watcher trigger tick failed\", { error: errMsg(err) });\n        });\n      }, triggerMs);\n      triggerTimer.unref();\n    }\n    started = true;\n  } catch (err) {\n    discoveryOpts = {};\n    throw err;\n  }\n}\n\n/** Tear down every watcher and stop the intervals. Used by tests;\n *  production never calls this (process exit reclaims the fds). Resets\n *  `started` so a subsequent `startCollectionWatchers` re-mounts. */\nexport async function stopCollectionWatchers(): Promise<void> {\n  if (rediscoveryTimer) {\n    clearInterval(rediscoveryTimer);\n    rediscoveryTimer = null;\n  }\n  if (triggerTimer) {\n    clearInterval(triggerTimer);\n    triggerTimer = null;\n  }\n  for (const watcher of watchers.values()) {\n    try {\n      watcher.watcher.close();\n    } catch {\n      /* fs.watch close is best-effort */\n    }\n  }\n  watchers.clear();\n  itemSlots.clear();\n  discoveryOpts = {};\n  started = false;\n}\n\n/** Test-only: manually trigger one rediscovery + reconcile pass. */\nexport async function _syncWatchersForTesting(): Promise<void> {\n  await syncWatchers();\n}\n\n/** Test-only: drive one wall-clock tick synchronously, with an optional\n *  injected clock. */\nexport async function _tickTimeTriggersForTesting(now?: Date): Promise<void> {\n  await tickTimeTriggers(now);\n}\n\n/** Re-reconcile every watched collection that depends on the clock — i.e.\n *  declares `triggerField` (a bell that fires at a date) and/or `spawn`\n *  (recurrence whose successors come due over time). Collections with\n *  neither are skipped. Idempotent. The schema is parsed back from the\n *  watcher's cached `schemaJson` to avoid a per-tick disk read. */\nasync function tickTimeTriggers(now: Date = evalNow()): Promise<void> {\n  for (const entry of watchers.values()) {\n    let schema: CollectionSchema;\n    try {\n      schema = JSON.parse(entry.schemaJson) as CollectionSchema;\n    } catch (err) {\n      log().warn(\"trigger tick: bad cached schema\", { slug: entry.slug, error: errMsg(err) });\n      continue;\n    }\n    if (!schema.triggerField && !schema.spawn) continue;\n    await reconcileAllItems(entry.slug, schema, entry.dataDir, discoveryOpts, now);\n  }\n}\n\n/** Reconcile the watcher set against the currently-discovered\n *  collections. Adds watchers for new slugs (with a boot reconcile of\n *  their items), drops watchers for vanished slugs, and re-reconciles\n *  items for collections whose schema changed. Runs a final sweep when\n *  this tick changed the watcher set or any schema. */\nasync function syncWatchers(): Promise<void> {\n  let collections;\n  try {\n    collections = await discoverCollections(discoveryOpts);\n  } catch (err) {\n    log().warn(\"watcher discover failed\", { error: errMsg(err) });\n    return;\n  }\n  const liveSlugs = new Set(collections.map((collection) => collection.slug));\n  const vanishedMutated = stopVanishedWatchers(liveSlugs);\n  const schemaMutated = await reconcileChangedSchemas(collections);\n  const addedMutated = await startNewWatchers(collections);\n  if (vanishedMutated || schemaMutated || addedMutated) {\n    await sweepStaleActiveEntries(discoveryOpts);\n  }\n}\n\nfunction stopVanishedWatchers(liveSlugs: Set<string>): boolean {\n  let mutated = false;\n  for (const slug of [...watchers.keys()]) {\n    if (liveSlugs.has(slug)) continue;\n    const watcher = watchers.get(slug);\n    if (watcher) {\n      try {\n        watcher.watcher.close();\n      } catch {\n        /* best-effort */\n      }\n    }\n    watchers.delete(slug);\n    mutated = true;\n    log().info(\"watcher stopped\", { slug });\n  }\n  return mutated;\n}\n\n/** Re-reconcile already-watched collections whose schema changed since\n *  the last tick. New collections fall through to `startNewWatchers`. */\nasync function reconcileChangedSchemas(collections: readonly LoadedCollection[]): Promise<boolean> {\n  let mutated = false;\n  for (const collection of collections) {\n    const existing = watchers.get(collection.slug);\n    if (!existing) continue;\n    const nextJson = JSON.stringify(collection.schema);\n    if (existing.schemaJson === nextJson) continue;\n    existing.schemaJson = nextJson;\n    log().info(\"watcher schema changed, re-reconciling\", { slug: collection.slug });\n    await reconcileAllItems(collection.slug, collection.schema, collection.dataDir, discoveryOpts);\n    mutated = true;\n  }\n  return mutated;\n}\n\nasync function startNewWatchers(collections: readonly LoadedCollection[]): Promise<boolean> {\n  let mutated = false;\n  for (const collection of collections) {\n    if (watchers.has(collection.slug)) continue;\n    await startWatcherFor(collection.slug, collection.schema, collection.dataDir);\n    mutated = true;\n  }\n  return mutated;\n}\n\nasync function startWatcherFor(slug: string, schema: CollectionSchema, dataDir: string): Promise<void> {\n  try {\n    // `fs.watch` throws on a missing dir, so ensure it exists. New\n    // collections legitimately start with no records — mkdir is the\n    // canonical first-use bootstrap.\n    await mkdir(dataDir, { recursive: true });\n    // Boot reconcile this collection's existing items BEFORE mounting the\n    // watcher: a pending item the user added during downtime needs its\n    // bell entry even if no event fires today.\n    await reconcileAllItems(slug, schema, dataDir, discoveryOpts);\n    const watcher = watch(dataDir, { persistent: false }, (_eventType, filename) => {\n      // Errors from inside the callback would propagate as unhandled\n      // rejections — wrap so a single bad event can't unwind the watcher.\n      onEvent(slug, filename).catch((err: unknown) => {\n        log().warn(\"watcher event failed\", { slug, filename, error: errMsg(err) });\n      });\n    });\n    watcher.on(\"error\", (err) => {\n      log().warn(\"watcher error\", { slug, error: errMsg(err) });\n    });\n    watchers.set(slug, { slug, dataDir, watcher, schemaJson: JSON.stringify(schema) });\n    log().info(\"watcher started\", { slug, dataDir });\n  } catch (err) {\n    log().warn(\"watcher start failed\", { slug, error: errMsg(err) });\n  }\n}\n\n/** Test-only: the per-key single-flight scheduler. Exported so test code\n *  can drive rapid-fire calls directly and observe the trailing coalesce\n *  — `fs.watch` event timing is too flaky to assert against.\n *\n *  Single-flight semantics: while a reconcile is in flight for a given\n *  (slug, itemId), additional events on the same key set `pending = true`\n *  and return — the running reconcile re-runs once after it completes.\n *  This collapses fs.watch's rapid-fire bursts (atomic rename surfaces as\n *  2-3 events) into a single reconcile + one trailing re-run. */\nexport function _scheduleItemReconcileForTesting(slug: string, schema: CollectionSchema, dataDir: string, itemId: string): Promise<void> {\n  return scheduleItemReconcile(slug, schema, dataDir, itemId);\n}\n\nfunction scheduleItemReconcile(slug: string, schema: CollectionSchema, dataDir: string, itemId: string): Promise<void> {\n  const key = `${slug}\\x00${itemId}`;\n  const existing = itemSlots.get(key);\n  if (existing) {\n    existing.pending = true;\n    return existing.running;\n  }\n  const slot: ReconcileSlot = { running: Promise.resolve(), pending: false };\n  slot.running = (async () => {\n    try {\n      // Re-run while events keep arriving — the trailing re-run captures\n      // any state change that landed during a prior pass. After each pass\n      // we read `pending` and zero it before the next iteration, so an\n      // event that fires *during* the last reconcile's await still\n      // triggers one more pass before the slot is freed.\n      let keepGoing = true;\n      while (keepGoing) {\n        slot.pending = false;\n        await reconcileItem(slug, schema, dataDir, itemId, discoveryOpts);\n        keepGoing = slot.pending;\n      }\n    } finally {\n      itemSlots.delete(key);\n    }\n  })();\n  itemSlots.set(key, slot);\n  return slot.running;\n}\n\n/** Handle a single fs.watch event. Re-loads the collection (schema may\n *  have changed since startup), filters out non-record files, and\n *  forwards to the single-flighted reconciler. `filename === null` (rare,\n *  platform-specific) triggers a full directory rescan to be safe. */\nasync function onEvent(slug: string, filename: string | Buffer | null): Promise<void> {\n  const collection = await loadCollection(slug, discoveryOpts);\n  if (!collection) return;\n  if (filename === null) {\n    // Some platforms omit the filename on a watch event — we don't know\n    // which record changed. `reconcileAllItems` covers items whose file\n    // still exists; pair it with a sweep so any record deleted inside the\n    // same opaque event has its stale bell entry cleared too.\n    await reconcileAllItems(slug, collection.schema, collection.dataDir, discoveryOpts);\n    await sweepStaleActiveEntries(discoveryOpts);\n    return;\n  }\n  const name = typeof filename === \"string\" ? filename : filename.toString(\"utf-8\");\n  // Filter: only record files (`*.json`), skip dot-prefixed (atomic\n  // writes / OS metadata / editor swap files). The reconciler is\n  // idempotent so a stray non-record event would be harmless, but\n  // skipping early avoids needless I/O.\n  if (!name.endsWith(\".json\") || name.startsWith(\".\")) return;\n  const itemId = name.slice(0, -\".json\".length);\n  await scheduleItemReconcile(slug, collection.schema, collection.dataDir, itemId);\n}\n"],"mappings":"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"}