{"version":3,"file":"discovery-CDOjfctD.cjs","names":[],"sources":["../src/host/hostSlot.ts","../src/collection/server/host.ts","../src/collection/server/paths.ts","../src/collection/server/io.ts","../src/collection/server/backendAvailability.ts","../src/collection/core/queryZ.ts","../src/collection/server/csvQuery.ts","../src/collection/server/csvStore.ts","../src/collection/server/watchFs.ts","../src/collection/server/storePage.ts","../src/collection/server/sqliteStore.ts","../src/collection/server/store.ts","../src/collection/core/mutateAction.ts","../src/collection/core/schemaRules.ts","../src/collection/core/schemaZ.ts","../src/collection/server/discovery.ts"],"sourcesContent":["// Shared host-injection primitives for the server-side engines. Each engine is\n// parameterized over host-owned dependencies (workspace root, logger, file\n// writer, …) that would be an uphill import if pulled in directly. Instead the\n// host wires them ONCE at startup and the engine reads them back through a slot.\n// This file centralises the storage/throw/no-op contract so the three domain\n// bindings (collection, feeds, google) can't drift into subtly different\n// behaviour.\n\nimport type { StructuredLogger } from \"@mulmoclaude/common\";\n\n/** Structured logger shape the engines log through — `(prefix, message,\n *  data?)`, matching the host `Logger`. The canonical declaration lives in\n *  `@mulmoclaude/common`; the domains alias it under their public names\n *  (`CollectionLogger` / `FeedsLogger` / `GoogleLogger`). */\nexport type { StructuredLogger };\n\n/** A single host-injected dependency, wired once at boot and read back by the\n *  engine. `get()` fails loudly when the host never configured (the engine\n *  cannot operate without it); `peek()` stays quiet for non-critical reads. */\nexport interface HostSlot<T> {\n  /** Wire the value. Re-binding to a *different* value throws — silently\n   *  redirecting a configured engine to another host would be a bug. Re-binding\n   *  the same value is a no-op. */\n  set: (value: T) => void;\n  /** The configured value, or throw (with the slot name) if never set. */\n  get: () => T;\n  /** The configured value, or `null` if never set — never throws. */\n  peek: () => T | null;\n  /** Test-only: forget the configured value. */\n  reset: () => void;\n}\n\nexport function createHostSlot<T>(name: string): HostSlot<T> {\n  let current: T | null = null;\n  const set = (value: T): void => {\n    if (current !== null && current !== value) {\n      throw new Error(`${name} was already called with a different host`);\n    }\n    current = value;\n  };\n  const get = (): T => {\n    if (current === null) throw new Error(`${name} was not called by the host`);\n    return current;\n  };\n  return { set, get, peek: () => current, reset: () => (current = null) };\n}\n\n/** Build the 4-method forwarding logger over a `() => StructuredLogger | null`\n *  getter. Calls made before a logger is available are dropped — logging is\n *  non-critical, unlike a required getter which fails loudly. Centralised so no\n *  domain can drift into throwing where a sibling drops. */\nexport function createForwardingLogger(getLogger: () => StructuredLogger | null): StructuredLogger {\n  return {\n    error: (prefix, message, data) => getLogger()?.error(prefix, message, data),\n    warn: (prefix, message, data) => getLogger()?.warn(prefix, message, data),\n    info: (prefix, message, data) => getLogger()?.info(prefix, message, data),\n    debug: (prefix, message, data) => getLogger()?.debug(prefix, message, data),\n  };\n}\n","// Host binding for the server-side collection engine.\n//\n// The engine is parameterized over the host's workspace + services, but\n// threading those through every call would be invasive. Instead each host\n// (MulmoClaude, MulmoTerminal) configures the binding ONCE at startup via\n// `configureCollectionHost`, and the engine reads it through the getters\n// below. This keeps the existing call sites (which default to the live\n// workspace root) unchanged while removing the package's dependency on\n// host-only modules (`server/workspace/workspace.ts`, the host logger).\n\nimport path from \"node:path\";\nimport { canonicalRoot } from \"../../files/root.js\";\nimport { createForwardingLogger, createHostSlot, type StructuredLogger } from \"../../host/hostSlot.js\";\n\n/** Public alias of the shared `StructuredLogger` — keeps the domain surface name-stable. */\nexport type CollectionLogger = StructuredLogger;\n\n/** Re-exported so collection-engine callers (the watcher, the reconciler) keep\n *  one import surface; the definition lives in `@mulmoclaude/core/files`\n *  because a root is an identity in subsystems that do not depend on this one. */\nexport { canonicalRoot };\n\n/** `err.code` on the throw from `getWorkspaceRoot()` under an explicit-root\n *  binding: this CALL is missing its `workspaceRoot` option. Exported because\n *  the fix differs from a watcher root conflict — that one means another root's\n *  watcher is already running — and a host catching both in one place should\n *  not have to match on message text to tell them apart. */\nexport const COLLECTION_ROOT_REQUIRED = \"COLLECTION_ROOT_REQUIRED\";\n\n/** INVARIANT — **a slug is unique within a root and nowhere else.**\n *\n *  A collection's identity is `(root, slug)`. Anything keyed by slug ALONE —\n *  a cache, a pubsub channel, a view token, a notification id, a rendered\n *  card — is a cross-root collision waiting to happen the moment two projects\n *  each own a `tasks` collection. A single-workspace host never sees it, which\n *  is exactly why it keeps being written that way.\n *\n *  Every engine surface that crosses a host boundary therefore carries the\n *  root: `CollectionChangePayload.root`, the completion bell's legacy id, the\n *  presented card's scope. When you add another, key it on the pair. */\nexport interface CollectionHost {\n  /** Absolute path to the host workspace root (e.g. `~/mulmoclaude`). The\n   *  default root for every path/containment check that isn't given an\n   *  explicit override.\n   *\n   *  `null` puts the binding in EXPLICIT-ROOT mode: the host declares that it\n   *  always passes `opts.workspaceRoot` per call, and `getWorkspaceRoot()`\n   *  throws instead of guessing. A multi-root host (MulmoTerminal, one root\n   *  per project) wants this — there, a forgotten option is not a crash but a\n   *  silent read/write against the WRONG project. A single-workspace host\n   *  (MulmoClaude) passes a string and nothing changes. */\n  workspaceRoot: string | null;\n  /** Host logger; the engine logs under the `\"collections\"` prefix. */\n  log: CollectionLogger;\n  /** Host workspace layout — supplied as the host's own path helpers so the\n   *  package owns no layout literals and works against a test/alt root. */\n  paths: {\n    /** Absolute user-scope skills dir (host-specific, e.g. `~/.claude/skills`). */\n    userSkillsDir: string;\n    /** Absolute project-scope skills dir for a workspace (`<root>/.claude/skills`). */\n    projectSkillsDir: (workspaceRoot: string) => string;\n    /** Absolute feeds-registry root for a workspace (`<root>/data/feeds`). */\n    feedsRoot: (workspaceRoot: string) => string;\n    /** Absolute project-skills *staging* dir for a root (`<root>/data/skills`),\n     *  or `null` when this root has NO staging tree.\n     *\n     *  Staging exists because a managed workspace gates writes into `.claude/`\n     *  and a skill-bridge hook mirrors `data/skills/<slug>/` across. A root\n     *  with no such bridge (a plain project folder) has nothing to mirror, so\n     *  the skill dir IS the authoring location and there is no staging.\n     *\n     *  Return `null` there — do NOT hand back the skill dir instead. It looks\n     *  equivalent (the read list becomes the same dir twice) but the delete\n     *  path `rm -rf`s the staging dir by name, which would then remove the\n     *  committed skill under the label \"staging\". */\n    skillsStagingDir: (workspaceRoot: string) => string | null;\n    /** Workspace-relative archive dir (a removed collection's files move here). */\n    archiveDir: string;\n    /** Absolute path to the user-supplied extra-registries config file for a\n     *  workspace (`<root>/config/collections-registries.json`). Injected so the\n     *  registry engine owns no app layout literal and a downstream host can point\n     *  it at its own workspace. */\n    collectionsRegistriesConfig: (workspaceRoot: string) => string;\n  };\n  /** True for a preset-skill slug (host-owned naming convention). */\n  isPresetSlug: (slug: string) => boolean;\n}\n\n/** A collection's records changed on disk. Carries the `slug` so the host can\n *  publish on a per-collection channel; `ids` lists the affected record ids\n *  when known (a consumer may ignore them and refetch the whole collection),\n *  and `op` is advisory. Deliberately carries NO record bodies — this is a\n *  \"refetch\" ping, not a data feed, so it stays cheap and leaks nothing when a\n *  host relays it into an opaque-origin custom-view iframe. */\nexport interface CollectionChangePayload {\n  slug: string;\n  /** Absolute workspace/project root the change happened under. Present only\n   *  when the engine call carried an explicit `workspaceRoot`; absent means\n   *  the host's configured root, so a single-workspace host never sets it.\n   *  A multi-root host MUST key its live-update fan-out on `(root, slug)` —\n   *  two projects each owning a `tasks` collection would otherwise refresh\n   *  each other's open views. */\n  root?: string;\n  ids?: string[];\n  op?: \"upsert\" | \"delete\";\n}\n\ntype CollectionChangePublisher = (payload: CollectionChangePayload) => void;\n\n/** Build a change payload, attaching `root` only when the engine call carried\n *  an explicit one. Centralised so every publish site states the root the same\n *  way, and so a single-workspace host's payload shape stays byte-identical to\n *  what it saw before multi-root support. */\nexport function collectionChangePayload(base: Omit<CollectionChangePayload, \"root\">, root: string | undefined): CollectionChangePayload {\n  // Canonical, because a host keys its live-update fan-out on this value: a\n  // direct `writeItem({ workspaceRoot: \"/proj/\" })` and the watcher's own\n  // publish for `/proj` must land on the same channel, not two.\n  return root === undefined ? base : { ...base, root: canonicalRoot(root) };\n}\n\nconst hostSlot = createHostSlot<CollectionHost>(\"@mulmoclaude/core/collection/server: configureCollectionHost()\");\nlet changePublisher: CollectionChangePublisher | null = null;\n\n/** Wire the engine to a host. Call once at server startup, before any\n *  collection storage operation. Re-binding to a *different* host throws —\n *  silently redirecting later filesystem operations to another workspace\n *  would be a bug, not a feature. Re-calling with the same host is a no-op. */\nexport function configureCollectionHost(host: CollectionHost): void {\n  hostSlot.set(host);\n}\n\n/** Wire a publisher that broadcasts record-change events; the host bridges it\n *  to its pubsub. Kept SEPARATE from `configureCollectionHost` because the\n *  host's pubsub instance isn't ready at host-binding time (the binding is set\n *  at the top of server startup, the pubsub later). Optional: left unset, every\n *  write is silent — the default for tests and for a host that doesn't want\n *  live view updates. Pass `null` to detach (test teardown). */\nexport function setCollectionChangePublisher(publish: CollectionChangePublisher | null): void {\n  changePublisher = publish;\n}\n\n/** Broadcast a record-change event if a publisher is wired (no-op otherwise).\n *  Called from the write path (`writeItem`/`deleteItem`). The wired publisher is\n *  expected to be fire-and-forget (it wraps its own pubsub call in try/catch),\n *  so this stays a thin pass-through and never throws into the write. */\nexport function publishCollectionChange(payload: CollectionChangePayload): void {\n  changePublisher?.(payload);\n}\n\nfunction requireHost(): CollectionHost {\n  return hostSlot.get();\n}\n\n/** The configured workspace root. Throws if the host never configured one —\n *  and, under an explicit-root binding (`workspaceRoot: null`), throws rather\n *  than guessing, which is the whole point of that mode: on a multi-root host\n *  a missing `opts.workspaceRoot` must fail loudly instead of silently\n *  resolving against some other project. */\nexport function getWorkspaceRoot(): string {\n  const root = requireHost().workspaceRoot;\n  if (root === null) {\n    throw Object.assign(\n      new Error(\n        \"@mulmoclaude/core/collection/server: the host is bound in explicit-root mode (workspaceRoot: null), \" +\n          \"so there is no ambient workspace root — pass an explicit `workspaceRoot` in this call's options.\",\n      ),\n      { code: COLLECTION_ROOT_REQUIRED },\n    );\n  }\n  return root;\n}\n\n/** The configured workspace root, or `null` under an explicit-root binding /\n *  before the host configures one. Never throws — for callers that need to\n *  COMPARE roots (is this the one we are already running for?) rather than\n *  operate on one. Anything that will touch the filesystem wants\n *  `getWorkspaceRoot()` and its loud failure instead. */\nexport function peekWorkspaceRoot(): string | null {\n  return hostSlot.peek()?.workspaceRoot ?? null;\n}\n\n// Workspace-layout accessors — thin wrappers over the host binding, named to\n// match the host helpers they replace so the moved engine modules keep their\n// call sites. Each throws (via requireHost) if the host never configured.\nexport function userSkillsDir(): string {\n  return requireHost().paths.userSkillsDir;\n}\nexport function projectSkillsDir(workspaceRoot: string): string {\n  return requireHost().paths.projectSkillsDir(workspaceRoot);\n}\nexport function feedsRoot(workspaceRoot: string): string {\n  return requireHost().paths.feedsRoot(workspaceRoot);\n}\nexport function skillsStagingDir(workspaceRoot: string): string | null {\n  return requireHost().paths.skillsStagingDir(workspaceRoot);\n}\n\n/** `<staging>/<slug>` for a root, or `null` when the root has no staging tree.\n *  The single place the staging-or-not branch is spelled, so every caller\n *  (read bases, schema write targets, archive, delete) agrees on it. */\nexport function stagingSkillDir(workspaceRoot: string, slug: string): string | null {\n  const staging = requireHost().paths.skillsStagingDir(workspaceRoot);\n  return staging === null ? null : path.join(staging, slug);\n}\nexport function archiveDir(): string {\n  return requireHost().paths.archiveDir;\n}\n/** Absolute path to a workspace's `collections-registries.json`. Takes the\n *  root explicitly — reading the ambient one here would throw under an\n *  explicit-root binding and take the Discover tab down with it. */\nexport function collectionsRegistriesConfigPath(workspaceRoot: string): string {\n  return requireHost().paths.collectionsRegistriesConfig(workspaceRoot);\n}\nexport function isPresetSlug(slug: string): boolean {\n  return requireHost().isPresetSlug(slug);\n}\n\n/** Logger proxy so engine modules can `import { log }` and use it exactly like\n *  the host logger — each call forwards to the live host binding. Logging is\n *  non-critical, so calls before the host configures a binding (e.g. unit tests\n *  that exercise pure logic) are dropped rather than throwing — unlike\n *  `getWorkspaceRoot()`, which fails loudly because the engine cannot operate\n *  without a workspace root. */\nexport const log: CollectionLogger = createForwardingLogger(() => hostSlot.peek()?.log ?? null);\n","// Path helpers + safe-slug guard for the collections module. Mirrors the\n// pattern used by `server/workspace/skills/catalog.ts` so CodeQL's\n// `js/path-injection` sanitiser recognises our taint-launder.\n\nimport path from \"node:path\";\nimport { realpathSync } from \"node:fs\";\nimport { getWorkspaceRoot } from \"./host\";\n// The character rules live in ../core/ids so the isomorphic schema validator\n// (../core/schemaZ) gates on the SAME patterns these sanitisers do.\nimport { SAFE_SLUG_PATTERN, SAFE_RECORD_ID_PATTERN } from \"../core/ids\";\nimport { isErrorWithCode } from \"@mulmoclaude/common\";\n\nexport const SCHEMA_FILE = \"schema.json\";\n\n/** Sanitise a user-supplied slug into a safe directory-name leaf.\n *  Returns null for anything that fails the slug whitelist OR isn't a\n *  basename (i.e. survives `path.basename` round-trip unchanged).\n *  The basename round-trip is the pattern CodeQL recognises as a\n *  `js/path-injection` sanitiser. */\nexport function safeSlugName(slug: string): string | null {\n  if (typeof slug !== \"string\") return null;\n  if (!SAFE_SLUG_PATTERN.test(slug)) return null;\n  const basename = path.basename(slug);\n  if (basename !== slug) return null;\n  return basename;\n}\n\n/** Sanitise a user-supplied record id into a safe filename stem. Like\n *  `safeSlugName` but tolerates interior dots (so natural keys work),\n *  while still rejecting any `..` substring, path separators, and\n *  leading/trailing dots. The `path.basename` round-trip is the same\n *  `js/path-injection` sanitiser CodeQL recognises on `safeSlugName`. */\nexport function safeRecordId(recordId: string): string | null {\n  if (typeof recordId !== \"string\") return null;\n  if (!SAFE_RECORD_ID_PATTERN.test(recordId)) return null;\n  if (recordId.includes(\"..\")) return null;\n  const basename = path.basename(recordId);\n  if (basename !== recordId) return null;\n  return basename;\n}\n\n/** Realpath the closest existing ancestor of `absPath` and return it.\n *  Returns null if no ancestor exists or if the realpath call fails\n *  for a non-ENOENT reason (permissions, etc.). Used by\n *  `containedPath` to defend against symlinks pointing outside the\n *  workspace even when the leaf hasn't been created yet. */\nfunction realpathClosestAncestor(absPath: string): string | null {\n  let cursor = absPath;\n  while (cursor !== path.dirname(cursor)) {\n    try {\n      return realpathSync(cursor);\n    } catch (err) {\n      if (isErrorWithCode(err) && err.code === \"ENOENT\") {\n        cursor = path.dirname(cursor);\n        continue;\n      }\n      return null;\n    }\n  }\n  return null;\n}\n\n/** True iff the realpath'd closest existing ancestor of `absPath`\n *  resolves under `rootPath`'s realpath. Pure helper, takes both\n *  paths explicitly so tests can drive it against a `mkdtempSync`\n *  root without touching the user's workspace. Defends against the\n *  data dir or any ancestor being a symlink to a directory outside\n *  the workspace — lexical-only checks (`path.resolve` + prefix\n *  match) would miss this case, which is the class of bug the rest\n *  of this codebase uses realpath-based containment to avoid (see\n *  `server/utils/files/safe.ts#resolveWithinRoot`). */\nexport function isContainedInRoot(absPath: string, rootPath: string): boolean {\n  let rootReal: string;\n  try {\n    rootReal = realpathSync(rootPath);\n  } catch {\n    return false;\n  }\n  const ancestorReal = realpathClosestAncestor(absPath);\n  if (ancestorReal === null) return false;\n  if (ancestorReal === rootReal) return true;\n  return ancestorReal.startsWith(rootReal + path.sep);\n}\n\n// NOTE: there is deliberately no `isContainedInWorkspace(absPath)` helper here.\n// It existed, had zero callers, and was exactly the shape a contributor reaches\n// for by reflex — at which point containment is checked against the AMBIENT\n// root while the operation it guards runs under an explicit one. Pass the root\n// you are operating on to `isContainedInRoot` instead.\n\n/** Resolve a schema-declared dataPath against `rootPath` (default:\n *  the live workspace), refusing anything that escapes — absolute\n *  paths, `..`-segments, empty string, or symlinks pointing outside\n *  the root. Returns the absolute path on success, null on refusal.\n *  Does NOT require the directory to exist; the caller may create it\n *  on first write. The realpath containment check covers the symlink\n *  case at discovery time; io operations re-check before each write\n *  to defend against symlinks introduced between discovery and use.\n *\n *  `rootPath` exists as an optional override so a test (or a tool\n *  driving discovery against a `mkdtempSync` tree) gets a dataDir\n *  rooted at the same place it asked to scan, not the real workspace.\n *  Without this, `discoverApps({ workspaceRoot: tmpdir })` would\n *  discover skills in tmpdir but resolve every app's dataDir against\n *  `~/mulmoclaude/`, breaking isolation. */\nexport function resolveDataDir(dataPath: string, rootPath: string = getWorkspaceRoot()): string | null {\n  if (typeof dataPath !== \"string\" || dataPath.length === 0) return null;\n  if (path.isAbsolute(dataPath)) return null;\n  const normalized = path.normalize(dataPath);\n  if (normalized.startsWith(\"..\") || normalized.includes(`${path.sep}..${path.sep}`)) return null;\n  const resolved = path.resolve(rootPath, normalized);\n  if (!isContainedInRoot(resolved, rootPath)) return null;\n  return resolved;\n}\n\n/** Compose the absolute path to a single record file. Both arguments\n *  must have been passed through `safeSlugName` / `resolveDataDir`\n *  before reaching here so the join can't escape. */\nexport function itemFilePath(dataDir: string, itemId: string): string {\n  return path.join(dataDir, `${itemId}.json`);\n}\n\n/** Resolve an action's skill-relative `template` path against\n *  `skillDir`, refusing escapes — absolute paths, `..`-segments, or a\n *  symlink pointing outside the skill dir. Mirrors `resolveDataDir`;\n *  the realpath containment is the hard guarantee. Returns the\n *  absolute path on success, null on refusal. */\nexport function resolveTemplatePath(skillDir: string, templateRelPath: string): string | null {\n  if (typeof templateRelPath !== \"string\" || templateRelPath.length === 0) return null;\n  if (path.isAbsolute(templateRelPath)) return null;\n  const normalized = path.normalize(templateRelPath);\n  if (normalized.startsWith(\"..\") || normalized.includes(`${path.sep}..${path.sep}`)) return null;\n  const resolved = path.resolve(skillDir, normalized);\n  if (!isContainedInRoot(resolved, skillDir)) return null;\n  return resolved;\n}\n","// Read / write item files for schema-driven collections. Records live at\n// `<dataDir>/<itemId>.json`, one JSON object per file. Writes are\n// atomic; deletes are idempotent enough to expose a clear 404 when\n// the file is missing.\n\nimport { lstat, mkdir, open, readdir, readFile, unlink } from \"node:fs/promises\";\nimport { randomBytes } from \"node:crypto\";\nimport path from \"node:path\";\nimport { collectionChangePayload, getWorkspaceRoot, log, publishCollectionChange } from \"./host\";\nimport { writeFileAtomic } from \"../../files/atomic.js\";\nimport { isContainedInRoot, itemFilePath, safeRecordId } from \"./paths\";\nimport type { CollectionItem, CollectionSchema } from \"../core/schema\";\nimport { isErrorWithCode, isRecord } from \"@mulmoclaude/common\";\n\nexport interface IoOptions {\n  /** Override the workspace root for containment checks. Default:\n   *  the live `workspacePath`. Tests point this at a `mkdtempSync`\n   *  tree so the realpath-based escape detection can be exercised\n   *  without touching `~/mulmoclaude/`. Same pattern as\n   *  `server/workspace/skills/catalog.ts#CatalogOptions`. */\n  workspaceRoot?: string | undefined;\n  /** Collection slug this write/delete belongs to. When provided, a\n   *  successful write/delete publishes a record-change event (see\n   *  `publishCollectionChange`) so live views refetch. `writeItem` has no\n   *  slug of its own (it's keyed by `dataDir`), so callers thread it through;\n   *  omitting it just means no event is published (internal / test writes). */\n  slug?: string | undefined;\n}\n\n/** True iff `filePath` exists and is a regular file (NOT a symlink).\n *  Defends `listItems` / `readItem` against `*.json` symlinks placed\n *  inside an otherwise-contained data dir — without this, a record\n *  file could symlink to /etc/passwd and the detail endpoint would\n *  happily serve it. Returns false on ENOENT and on any other lstat\n *  failure so the caller's \"missing\" branch covers those cases too.\n *  Exported so `ontology.ts`'s record COUNT classifies entries with the\n *  SAME lstat logic — the two must agree on what a record file is. */\nexport async function isRegularFile(filePath: string): Promise<boolean> {\n  try {\n    const info = await lstat(filePath);\n    return info.isFile();\n  } catch {\n    return false;\n  }\n}\n\n/** Read one JSON record file. Returns null when the file is missing,\n *  is a symlink (file-disclosure defense), parses to a non-object,\n *  or has a read/parse error. Caller logs the per-entry skip — this\n *  helper just classifies. Split out to keep `listItems` under the\n *  `sonarjs/cognitive-complexity` threshold. */\n/** Parse a record file's text into a plain-object `CollectionItem`, or\n *  null when it isn't a JSON object (array / scalar / null). */\nfunction parseRecordJson(raw: string): CollectionItem | null {\n  const parsed: unknown = JSON.parse(raw);\n  return isRecord(parsed) ? parsed : null;\n}\n\nasync function tryReadRecord(filePath: string): Promise<CollectionItem | null> {\n  if (!(await isRegularFile(filePath))) return null;\n  try {\n    return parseRecordJson(await readFile(filePath, \"utf-8\"));\n  } catch {\n    return null;\n  }\n}\n\n/** Read every record under `dataDir`. Returns [] if the dir doesn't\n *  exist yet (legitimate first-use state). Malformed JSON files and\n *  symlinked records are skipped (the latter is a file-disclosure\n *  defense — see `isRegularFile`). Re-validates the realpath\n *  containment to defend against a symlinked data dir appearing\n *  between discovery and use. */\nexport async function listItems(dataDir: string, opts: IoOptions = {}): Promise<CollectionItem[]> {\n  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();\n  if (!isContainedInRoot(dataDir, workspaceRoot)) {\n    log.warn(\"collections\", \"listItems refused: dataDir escapes workspace via symlink\", { dataDir });\n    return [];\n  }\n  let entries: string[];\n  try {\n    entries = await readdir(dataDir);\n  } catch (err) {\n    if (isErrorWithCode(err) && err.code === \"ENOENT\") return [];\n    throw err;\n  }\n  const results: CollectionItem[] = [];\n  for (const name of entries) {\n    if (!name.endsWith(\".json\")) continue;\n    if (name.startsWith(\".\")) continue;\n    const filePath = path.join(dataDir, name);\n    const record = await tryReadRecord(filePath);\n    if (record === null) {\n      log.warn(\"collections\", \"skipping record (missing, symlink, or unreadable)\", { path: filePath });\n      continue;\n    }\n    results.push(record);\n  }\n  return results;\n}\n\n/** Read one record by id. Returns null when the file is missing,\n *  when the resolved path escapes the workspace via a symlink, or\n *  when the record file itself is a symlink (file-disclosure\n *  defense — see `isRegularFile`). */\nexport async function readItem(dataDir: string, itemId: string, opts: IoOptions = {}): Promise<CollectionItem | null> {\n  const safeId = safeRecordId(itemId);\n  if (safeId === null) return null;\n  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();\n  if (!isContainedInRoot(dataDir, workspaceRoot)) return null;\n  const filePath = itemFilePath(dataDir, safeId);\n  if (!(await isRegularFile(filePath))) return null;\n  try {\n    return parseRecordJson(await readFile(filePath, \"utf-8\"));\n  } catch (err) {\n    if (isErrorWithCode(err) && err.code === \"ENOENT\") return null;\n    throw err;\n  }\n}\n\nexport interface WriteItemOptions extends IoOptions {\n  /** When true (POST/create), refuse to overwrite an existing file\n   *  and return `kind: \"conflict\"`. Update flow (PUT) leaves it false. */\n  refuseOverwrite?: boolean | undefined;\n}\n\nexport type WriteItemResult =\n  | { kind: \"ok\"; itemId: string; item: CollectionItem }\n  | { kind: \"invalid-id\"; itemId: string }\n  | { kind: \"conflict\"; itemId: string }\n  | { kind: \"path-escape\"; itemId: string };\n\n/** The symlink-containment refusal every record path shares: one check, one\n *  warn, one answer. Extracted because this is a security RULE applied at\n *  three sites (write pre-mkdir, write post-mkdir, delete) — a fix to the\n *  check must not be able to land at only one of them.\n *\n *  `stage` names the call site so the warn stays as diagnosable as the three\n *  hand-written copies were.\n *\n *  Scope, stated explicitly because a reviewer asks every time: this catches\n *  a symlink that EXISTS when we look — `isContainedInRoot` realpaths the\n *  closest existing ancestor, so a pre-planted escape is refused. It does not\n *  and cannot close the check-then-use race, where an ancestor is swapped for\n *  a symlink between this call and the `mkdir` / `open` / `unlink` that\n *  follows. Closing that needs directory-handle I/O anchored at the workspace\n *  (`openat` + `O_NOFOLLOW`), which `node:fs` does not expose — it would mean\n *  a different I/O layer, not a tighter check here.\n *\n *  That race is deliberately outside this app's threat model: the process is\n *  loopback-bound and bearer-authed, so anyone able to swap directories inside\n *  the workspace is already the workspace owner — the same trust principal the\n *  writes belong to. Revisit if collections ever serve a lower-trust caller. */\nfunction escapesWorkspace(dataDir: string, workspaceRoot: string, itemId: string, stage: string): boolean {\n  if (isContainedInRoot(dataDir, workspaceRoot)) return false;\n  log.warn(\"collections\", `${stage} refused: dataDir escapes workspace via symlink`, { dataDir, itemId });\n  return true;\n}\n\n/** Write a record. Ensures the directory exists, validates the id,\n *  re-checks symlink containment after mkdir, and writes atomically.\n *\n *  Create path (`refuseOverwrite: true`) uses an O_EXCL `wx` open\n *  rather than `stat` + `writeFileAtomic` to close a check-then-write\n *  race: two concurrent POSTs would otherwise both pass the existence\n *  check and one would silently overwrite the other. The trade-off\n *  is that the create path is not crash-atomic (a partial file could\n *  remain if the process dies mid-write); acceptable here because\n *  records are small JSON blobs and the next read either parses or\n *  is skipped via the \"malformed JSON\" branch in `listItems`.\n *\n *  Update path (`refuseOverwrite: false`) uses `writeFileAtomic` so\n *  PUT remains crash-atomic. No race there — the URL pins the id. */\nexport async function writeItem(dataDir: string, itemId: string, item: CollectionItem, opts: WriteItemOptions = {}): Promise<WriteItemResult> {\n  const safeId = safeRecordId(itemId);\n  if (safeId === null) return { kind: \"invalid-id\", itemId };\n  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();\n  // Containment check runs BEFORE mkdir so we never create\n  // directories outside the workspace even if a symlink ancestor\n  // was swapped after discovery. We re-check AFTER mkdir to catch\n  // a symlink racing in between the two — belt + suspenders, cheap\n  // and the only honest defense against TOCTOU on directory creation.\n  if (escapesWorkspace(dataDir, workspaceRoot, safeId, \"writeItem (pre-mkdir)\")) return { kind: \"path-escape\", itemId: safeId };\n  await mkdir(dataDir, { recursive: true });\n  if (escapesWorkspace(dataDir, workspaceRoot, safeId, \"writeItem (post-mkdir)\")) return { kind: \"path-escape\", itemId: safeId };\n  const filePath = itemFilePath(dataDir, safeId);\n  const payload = `${JSON.stringify(item, null, 2)}\\n`;\n\n  if (opts.refuseOverwrite) {\n    let handle;\n    try {\n      handle = await open(filePath, \"wx\");\n    } catch (err) {\n      if (isErrorWithCode(err) && err.code === \"EEXIST\") return { kind: \"conflict\", itemId: safeId };\n      throw err;\n    }\n    try {\n      await handle.writeFile(payload);\n    } finally {\n      await handle.close();\n    }\n  } else {\n    await writeFileAtomic(filePath, payload);\n  }\n  // Publish AFTER the write lands so a live subscriber that refetches always\n  // sees the new record (never a read-before-write race).\n  if (opts.slug) publishCollectionChange(collectionChangePayload({ slug: opts.slug, ids: [safeId], op: \"upsert\" }, opts.workspaceRoot));\n  return { kind: \"ok\", itemId: safeId, item };\n}\n\nexport type DeleteItemResult =\n  { kind: \"ok\"; itemId: string } | { kind: \"invalid-id\"; itemId: string } | { kind: \"not-found\"; itemId: string } | { kind: \"path-escape\"; itemId: string };\n\nexport async function deleteItem(dataDir: string, itemId: string, opts: IoOptions = {}): Promise<DeleteItemResult> {\n  const safeId = safeRecordId(itemId);\n  if (safeId === null) return { kind: \"invalid-id\", itemId };\n  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();\n  if (escapesWorkspace(dataDir, workspaceRoot, safeId, \"deleteItem\")) return { kind: \"path-escape\", itemId: safeId };\n  const filePath = itemFilePath(dataDir, safeId);\n  try {\n    await unlink(filePath);\n    if (opts.slug) publishCollectionChange(collectionChangePayload({ slug: opts.slug, ids: [safeId], op: \"delete\" }, opts.workspaceRoot));\n    return { kind: \"ok\", itemId: safeId };\n  } catch (err) {\n    if (isErrorWithCode(err) && err.code === \"ENOENT\") return { kind: \"not-found\", itemId: safeId };\n    throw err;\n  }\n}\n\n/** Generate a short random hex id. Used by POST when the form doesn't\n *  carry a primary-key value (UI shortcut — Claude normally derives a\n *  semantic id from the record's name). */\nexport function generateItemId(): string {\n  return randomBytes(4).toString(\"hex\");\n}\n\n/** The item id a CREATE should use for `schema`, or null when the\n *  caller should generate one. A singleton collection pins every\n *  create to its fixed `schema.singleton` id, so the \"at most one\n *  record\" contract is enforced server-side (a second create targets\n *  the same file and hits `writeItem`'s refuseOverwrite conflict) —\n *  not only in the UI. Otherwise the record's own primaryKey value\n *  wins, falling back to a generated id (null = \"generate\"). */\nexport function resolveCreateItemId(schema: CollectionSchema, record: CollectionItem): string | null {\n  if (schema.singleton) return schema.singleton;\n  const primaryRaw = record[schema.primaryKey];\n  return typeof primaryRaw === \"string\" && primaryRaw.length > 0 ? primaryRaw : null;\n}\n","// \"The backend can't serve this right now\" — as distinct from \"the record\n// isn't there\" or \"the stored record is malformed\".\n//\n// Both non-file backends have such a state, and neither is a data problem:\n//\n//   - sqlite: `node:sqlite` needs Node >= 22.5, and the app's floor is 20.12,\n//     so on an older runtime ONLY sqlite collections fail (sqliteStore.ts).\n//   - csv/dataSource: `@duckdb/node-api` is a native module whose prebuilt\n//     binding can be missing for the platform (csvStore.ts).\n//\n// The distinction matters because the layers above catch broadly. Without a\n// type to test, `store.read(...).catch(() => null)` reports \"record missing\",\n// a merge reports \"malformed stored file\", and an ontology count reports 0 —\n// each of which sends the agent after a data problem that does not exist, and\n// the last of which can have it offer to recreate records that are intact.\n//\n// Anything that summarises or swallows a store error MUST re-check with\n// `isBackendUnavailable` and let it through.\n\n/** Thrown by a store when its engine or session cannot serve the request. */\nexport class BackendUnavailableError extends Error {\n  constructor(message: string) {\n    super(message);\n    this.name = \"BackendUnavailableError\";\n  }\n}\n\nexport function isBackendUnavailable(err: unknown): err is BackendUnavailableError {\n  return err instanceof BackendUnavailableError;\n}\n","// The aggregation-query DSL for `dataSource` collections (v2 of\n// plans/done/feat-collection-csv-duckdb-source.md). A structured JSON query —\n// NEVER raw SQL — because SQL-the-language isn't scoped to the data file\n// (`read_csv`/`read_text`/`COPY TO` reach the whole filesystem) and the\n// query authors (custom views, the agent) are influenceable by untrusted\n// content. The DSL is safe by construction: column names / aliases become\n// quoted identifiers, every VALUE rides as a prepared-statement parameter,\n// and the shape can't express file access at all. Compiled to SQL\n// server-side (`collection/server/csvQuery.ts`).\n//\n// Isomorphic: custom views build these objects in the browser; the server\n// validates with the same zod schema before compiling.\n\nimport { z } from \"zod\";\n\n/** Result-column aliases double as SQL identifiers and JSON keys — keep\n *  them to a conservative identifier charset so neither side needs\n *  escaping gymnastics. */\nconst SAFE_ALIAS_PATTERN = /^[A-Za-z_]\\w{0,63}$/;\n\n/** Hard ceiling on returned rows; `limit` clamps below it. A group-by on\n *  a near-unique column would otherwise return one row per source row —\n *  the exact materialization the aggregate path exists to avoid. */\nexport const MAX_QUERY_ROWS = 10000;\n/** Default row cap when the query declares no `limit`. */\nexport const DEFAULT_QUERY_ROWS = 1000;\n/** Cap on aggregate expressions per query — a record shape has no\n *  intrinsic size limit, and thousands of expressions would make one\n *  full-file scan arbitrarily wide. */\nexport const MAX_QUERY_AGGREGATES = 32;\n\n/** One aggregate column: `count` (rows; `column` optional to count\n *  non-null cells) or `sum`/`avg`/`min`/`max` over a named CSV column. */\nexport const QueryAggregateZ = z\n  .object({\n    op: z.enum([\"count\", \"sum\", \"avg\", \"min\", \"max\"]),\n    column: z.string().min(1).optional(),\n  })\n  .refine((aggregate) => aggregate.op === \"count\" || aggregate.column !== undefined, {\n    message: \"`column` is required for every aggregate op except `count`\",\n    path: [\"column\"],\n  });\n\n/** One filter condition. Same op vocabulary as the schema-level `where`\n *  (`core/where.ts`) so authors learn one set; values may be typed\n *  (number / boolean) since CSV columns are. `in` requires an array\n *  value, every other op a scalar. */\nexport const QueryWhereZ = z\n  .object({\n    field: z.string().min(1),\n    op: z.enum([\"eq\", \"ne\", \"in\", \"gt\", \"gte\", \"lt\", \"lte\", \"contains\"]),\n    value: z.union([\n      z.string(),\n      z.number(),\n      z.boolean(),\n      z\n        .array(z.union([z.string(), z.number(), z.boolean()]))\n        .min(1)\n        .max(100),\n    ]),\n  })\n  .refine((cond) => (cond.op === \"in\") === Array.isArray(cond.value), {\n    message: \"`in` requires an array value (the allowed set); every other op requires a scalar value\",\n    path: [\"value\"],\n  });\n\nexport const QueryOrderZ = z.object({\n  /** A `groupBy` column or an aggregate alias — membership enforced by\n   *  the whole-query refine below. */\n  field: z.string().min(1),\n  dir: z.enum([\"asc\", \"desc\"]).optional(),\n});\n\n/** The whole query. At least one of `groupBy` / `aggregates` must be\n *  present: bare `groupBy` is a DISTINCT listing, bare `aggregates` a\n *  whole-file scalar row, together a grouped aggregation. */\nexport const CollectionQueryZ = z\n  .object({\n    // Case-insensitively unique: DuckDB treats `category` and `Category`\n    // as the SAME identifier, groups once, and auto-renames the duplicate\n    // SELECT column (`Category_1`) — silently breaking the result-key\n    // contract. Same rule as the alias refine below.\n    groupBy: z\n      .array(z.string().min(1))\n      .max(8)\n      .refine((columns) => new Set(columns.map((column) => column.toLowerCase())).size === columns.length, {\n        message: \"`groupBy` columns must be unique (case-insensitively — SQL identifiers ignore case)\",\n      })\n      .optional(),\n    aggregates: z\n      .record(z.string().regex(SAFE_ALIAS_PATTERN, \"aggregate aliases must be simple identifiers (letters/digits/underscore)\"), QueryAggregateZ)\n      .optional(),\n    where: z.array(QueryWhereZ).max(16).optional(),\n    orderBy: z.array(QueryOrderZ).max(4).optional(),\n    limit: z.number().int().min(1).max(MAX_QUERY_ROWS).optional(),\n  })\n  .refine((query) => (query.groupBy?.length ?? 0) > 0 || Object.keys(query.aggregates ?? {}).length > 0, {\n    message: \"declare at least one of `groupBy` (columns to bucket by) or `aggregates` (values to compute)\",\n    path: [\"groupBy\"],\n  })\n  .refine((query) => Object.keys(query.aggregates ?? {}).length <= MAX_QUERY_AGGREGATES, {\n    message: `\\`aggregates\\` supports at most ${MAX_QUERY_AGGREGATES} entries`,\n    path: [\"aggregates\"],\n  })\n  // An alias shadowing a groupBy column would make the SELECT list (and\n  // the result object) ambiguous. CASE-INSENSITIVE: DuckDB treats\n  // identifiers that differ only by case as the same name (quoting does\n  // not opt out), so `Total` vs `total` collide there even though they\n  // are distinct JSON keys here — reject both same-case and cross-case\n  // collisions, including between two aliases.\n  .refine(\n    (query) => {\n      const groupLower = new Set((query.groupBy ?? []).map((column) => column.toLowerCase()));\n      const seen = new Set<string>();\n      return Object.keys(query.aggregates ?? {}).every((alias) => {\n        const lower = alias.toLowerCase();\n        if (groupLower.has(lower) || seen.has(lower)) return false;\n        seen.add(lower);\n        return true;\n      });\n    },\n    {\n      message: \"aggregate aliases must be unique and must not collide with `groupBy` column names (case-insensitively — SQL identifiers ignore case)\",\n      path: [\"aggregates\"],\n    },\n  )\n  // `orderBy` can only sort what the result actually contains.\n  .refine(\n    (query) => {\n      const sortable = new Set([...(query.groupBy ?? []), ...Object.keys(query.aggregates ?? {})]);\n      return (query.orderBy ?? []).every((order) => sortable.has(order.field));\n    },\n    {\n      message: \"every `orderBy.field` must be a `groupBy` column or an aggregate alias\",\n      path: [\"orderBy\"],\n    },\n  );\n\nexport type CollectionQueryAggregate = z.infer<typeof QueryAggregateZ>;\nexport type CollectionQueryWhere = z.infer<typeof QueryWhereZ>;\nexport type CollectionQueryOrder = z.infer<typeof QueryOrderZ>;\nexport type CollectionQuery = z.infer<typeof CollectionQueryZ>;\n","// Compile a validated CollectionQuery (the structured aggregation DSL,\n// `core/queryZ.ts`) into DuckDB SQL over `read_csv`. Safety model:\n//   - column names / aliases → double-quoted identifiers (quoteIdent);\n//   - every comparison VALUE → a prepared-statement parameter;\n//   - the file path is bound by the EXECUTOR as the first parameter;\n//   - the DSL itself can't name a table function, so no query can reach\n//     any file other than the one the executor binds.\n// Pure + exported for unit tests — no filesystem, no DuckDB here.\n\nimport type { CollectionQuery, CollectionQueryAggregate, CollectionQueryWhere } from \"../core/queryZ\";\nimport { DEFAULT_QUERY_ROWS } from \"../core/queryZ\";\n\n/** One value bound to a `?` placeholder. The DSL only ever compares\n *  against JSON scalars, and these are exactly the `DuckDBValue`s the\n *  driver accepts — typing the chain end-to-end is what lets `queryCsv`\n *  hand them to `runAndReadAll` without coercion. */\nexport type CsvQueryParam = string | number | boolean;\n\n/** Double-quote a SQL identifier (CSV column name / result alias). */\nexport function quoteIdent(name: string): string {\n  return `\"${name.replaceAll('\"', '\"\"')}\"`;\n}\n\n/** Single-quote a SQL string literal (a `types={...}` struct key). */\nexport function quoteLiteral(value: string): string {\n  return `'${value.replaceAll(\"'\", \"''\")}'`;\n}\n\n/** The `read_csv` argument list shared by every CSV query: the (prepared)\n *  path plus a `types` pin forcing the key column to VARCHAR — without it\n *  DuckDB's sniffer turns `001` into BIGINT 1, so leading zeros vanish\n *  and distinct keys collapse. */\nexport function readCsvArgs(primaryKey: string): string {\n  return `?, types={${quoteLiteral(primaryKey)}: 'VARCHAR'}`;\n}\n\n/** One aggregate's SQL expression. `sum`/`avg` TRY_CAST to DOUBLE so a\n *  column the sniffer kept as VARCHAR (mixed values) aggregates over its\n *  numeric cells instead of erroring; non-numeric cells become NULL and\n *  are skipped — standard BI tolerance. `min`/`max` stay native (they are\n *  meaningful on strings and dates too). */\nfunction aggregateExpr(aggregate: CollectionQueryAggregate): string {\n  const { op, column } = aggregate;\n  if (op === \"count\") return column === undefined ? \"count(*)\" : `count(${quoteIdent(column)})`;\n  if (op === \"sum\" || op === \"avg\") return `${op}(TRY_CAST(${quoteIdent(column ?? \"\")} AS DOUBLE))`;\n  return `${op}(${quoteIdent(column ?? \"\")})`;\n}\n\n/** One where condition → SQL fragment + its bound parameters. String\n *  equality compares against `CAST(col AS VARCHAR)` so a sniffer-typed\n *  column still matches its textual value; numeric/boolean values compare\n *  natively (DuckDB coerces the column side). */\nfunction whereFragment(cond: CollectionQueryWhere): { sql: string; params: CsvQueryParam[] } {\n  const column = quoteIdent(cond.field);\n  const asText = `CAST(${column} AS VARCHAR)`;\n  if (cond.op === \"in\") {\n    const values = arrayValue(cond);\n    const textual = values.every((value) => typeof value === \"string\");\n    const lhs = textual ? asText : column;\n    return { sql: `${lhs} IN (${values.map(() => \"?\").join(\", \")})`, params: values };\n  }\n  // `String()` stays — `contains` matches against text, so a numeric needle is\n  // searched by its text form — but an array must fail here like it does on\n  // every other scalar op, not silently become the needle \"1,2\".\n  if (cond.op === \"contains\") return { sql: `contains(${asText}, ?)`, params: [String(scalarValue(cond))] };\n  const operator = { eq: \"=\", ne: \"<>\", gt: \">\", gte: \">=\", lt: \"<\", lte: \"<=\" }[cond.op];\n  const lhs = typeof cond.value === \"string\" && (cond.op === \"eq\" || cond.op === \"ne\") ? asText : column;\n  return { sql: `${lhs} ${operator} ?`, params: [scalarValue(cond)] };\n}\n\n/** Mirror of `scalarValue` for the one op that takes a set: a scalar under\n *  `in` also means the query skipped `CollectionQueryZ`. Left unchecked it\n *  failed as `values.every is not a function`, naming neither the field nor\n *  the op. */\nfunction arrayValue(cond: CollectionQueryWhere): CsvQueryParam[] {\n  if (!Array.isArray(cond.value)) {\n    throw new Error(`where condition on '${cond.field}' uses op 'in', which requires an array value, not a scalar`);\n  }\n  return cond.value;\n}\n\n/** `CollectionQueryZ` refines \"`in` ⇔ array value\", so an array reaching a\n *  scalar op means the query was compiled without being validated first —\n *  binding it would send an array to a single `?`. */\nfunction scalarValue(cond: CollectionQueryWhere): CsvQueryParam {\n  if (Array.isArray(cond.value)) {\n    throw new Error(`where condition on '${cond.field}' uses op '${cond.op}', which requires a scalar value, not an array`);\n  }\n  return cond.value;\n}\n\n/** Compile a validated query against `fromSql` (a table-function call\n *  whose FIRST placeholder is the source path — the executor binds it).\n *  Returns the SQL and the where-value parameters that follow the path.\n *  Callers MUST have run `CollectionQueryZ` first; this function trusts\n *  the shape (aliases already charset-checked, orderBy membership already\n *  enforced). */\nfunction compileQuery(query: CollectionQuery, fromSql: string): { sql: string; params: CsvQueryParam[] } {\n  const groupBy = query.groupBy ?? [];\n  const aggregates = Object.entries(query.aggregates ?? {});\n  const selectList = [...groupBy.map(quoteIdent), ...aggregates.map(([alias, aggregate]) => `${aggregateExpr(aggregate)} AS ${quoteIdent(alias)}`)];\n  const where = (query.where ?? []).map(whereFragment);\n  const clauses = [`SELECT ${selectList.join(\", \")}`, `FROM ${fromSql}`];\n  if (where.length > 0) clauses.push(`WHERE ${where.map((fragment) => fragment.sql).join(\" AND \")}`);\n  if (groupBy.length > 0) clauses.push(`GROUP BY ${groupBy.map(quoteIdent).join(\", \")}`);\n  const orderBy = (query.orderBy ?? []).map((order) => quoteIdent(order.field) + (order.dir === \"desc\" ? \" DESC\" : \" ASC\"));\n  if (orderBy.length > 0) clauses.push(`ORDER BY ${orderBy.join(\", \")}`);\n  clauses.push(`LIMIT ${query.limit ?? DEFAULT_QUERY_ROWS}`);\n  return { sql: clauses.join(\" \"), params: where.flatMap((fragment) => fragment.params) };\n}\n\n/** Compile against a CSV file (the dataSource store's engine). */\nexport function compileCsvQuery(query: CollectionQuery, primaryKey: string): { sql: string; params: CsvQueryParam[] } {\n  return compileQuery(query, `read_csv(${readCsvArgs(primaryKey)})`);\n}\n\n/** Compile against a JSONL file of ENRICHED records — the file-backed\n *  collections' engine (see `jsonlQuery.ts`). No VARCHAR key pin needed:\n *  enriched record ids are already strings. `sample_size=-1` makes the\n *  schema inference scan EVERY line — with the default sample, a sparse\n *  optional/derived field first appearing past the sample would not be\n *  inferred as a column and the query would binder-error on it (Codex P2\n *  on #2165). The full scan costs nothing extra here: aggregation reads\n *  the whole file anyway. */\nexport function compileJsonlQuery(query: CollectionQuery): { sql: string; params: CsvQueryParam[] } {\n  return compileQuery(query, `read_json(?, format='newline_delimited', sample_size=-1)`);\n}\n","// DuckDB-backed read-only store over an external CSV file (schema\n// `dataSource`, v1). The user's file is the source of truth and is NEVER\n// modified: non-UTF-8 files (Shift_JIS rosters are the primary use case)\n// are decoded to a content-addressed UTF-8 cache copy under os.tmpdir()\n// and DuckDB reads that copy.\n//\n// Row → record contract:\n//   - the schema's `primaryKey` names the CSV column whose value becomes\n//     the record id; CSV columns map to fields by NAME;\n//   - a key value that isn't a safe record id (Japanese, spaces, …) is\n//     hex-encoded (`id0x…`) so detail URLs / remote-view addressing keep\n//     working — `displayField` covers presentation;\n//   - duplicate key values: LAST row wins (a warn is logged); rows with an\n//     empty/missing key are skipped (warn);\n//   - `list()` is capped at MAX_CSV_ROWS — the whole existing UI\n//     materializes every record, so an uncapped 2M-row file would be a\n//     memory bomb. v2 replaces the cap with paging + native aggregation.\n//\n// SQL safety: the row-value comparison is a prepared-statement parameter,\n// never string concatenation; the key column is identifier-quoted. The\n// file path itself is host-resolved (workspace containment in discovery)\n// and also bound as a parameter.\n//\n// DuckDB is a native module; `import()` failures (unsupported platform,\n// broken install) degrade to a thrown, clearly-worded error so ONLY\n// dataSource collections break — see\n// packages/core/assets/helps/error-recovery.md.\n\nimport { fieldTextOrNull } from \"../core/fieldText\";\nimport { BackendUnavailableError } from \"./backendAvailability\";\nimport { lstat, mkdir, open, readdir, readFile, rename, stat, unlink, writeFile } from \"node:fs/promises\";\nimport { createHash, randomBytes } from \"node:crypto\";\nimport { tmpdir } from \"node:os\";\nimport path from \"node:path\";\nimport iconv from \"iconv-lite\";\nimport type { CollectionItem } from \"../core/schema\";\nimport type { CollectionQuery } from \"../core/queryZ\";\nimport type { DuckDBInstance } from \"@duckdb/node-api\";\nimport { compileCsvQuery, quoteIdent, readCsvArgs, type CsvQueryParam } from \"./csvQuery\";\nimport { getWorkspaceRoot, log } from \"./host\";\nimport { isContainedInRoot, safeRecordId } from \"./paths\";\nimport { isErrorWithCode } from \"@mulmoclaude/common\";\n\n/** `list()` row cap. Over-cap files are truncated with a warn — the v1\n *  contract is \"browse + per-record views\", not full-table analytics. */\nexport const MAX_CSV_ROWS = 5000;\n\n/** Record ids minted from non-safe key values: `id0x` + utf-8 hex. Raw key\n *  values that themselves match this pattern are ALSO encoded, so the\n *  encoded namespace never collides with a raw value (injective mapping). */\nconst ENCODED_ID_PATTERN = /^id0x([0-9a-f]+)$/;\n\n/** A CSV key value → the record id it's addressed by. Safe values pass\n *  through untouched; everything else (and anything shaped like an encoded\n *  id) becomes `id0x<hex>`. Pure + exported for unit tests. */\nexport function encodeCsvRecordId(rawKey: string): string {\n  if (safeRecordId(rawKey) === rawKey && !ENCODED_ID_PATTERN.test(rawKey)) return rawKey;\n  return `id0x${Buffer.from(rawKey, \"utf-8\").toString(\"hex\")}`;\n}\n\n/** A record id → the CSV key value to look up. Inverse of\n *  `encodeCsvRecordId` for encoded ids; anything else is already the raw\n *  value. Pure + exported for unit tests. */\nexport function decodeCsvRecordId(itemId: string): string {\n  const hex = ENCODED_ID_PATTERN.exec(itemId)?.[1];\n  if (hex === undefined) return itemId;\n  return Buffer.from(hex, \"hex\").toString(\"utf-8\");\n}\n\n/** Normalize one DuckDB JS value into a JSON-safe record value: BigInt →\n *  number (string beyond the safe range), DATE/TIMESTAMP → ISO string\n *  (date-only when the clock is exactly UTC midnight, matching the `date`\n *  field contract), exotic DuckDB values → their string form. Pure +\n *  exported for unit tests. */\n/** `JSON.stringify` restricted to what a CSV cell can survive. Returns the\n *  serialised value, or `String(value)` when serialisation is impossible —\n *  losing the content of one cell is bad, failing the entire query is worse. */\nfunction safeJsonCell(value: object): string {\n  try {\n    const json = JSON.stringify(value, (_key, entry: unknown) => (typeof entry === \"bigint\" ? entry.toString() : entry));\n    // eslint-disable-next-line @typescript-eslint/no-base-to-string -- last resort: JSON.stringify returned undefined (an object whose toJSON does). \"[object Object]\" at least says \"this cell held an object\"; \"\" would erase that it existed.\n    return json ?? String(value);\n  } catch {\n    // eslint-disable-next-line @typescript-eslint/no-base-to-string -- the serialiser threw (circular ref). Same reasoning as above: a placeholder beats losing the cell entirely.\n    return String(value);\n  }\n}\n\nexport function normalizeCsvValue(value: unknown): unknown {\n  if (typeof value === \"bigint\") {\n    return value <= BigInt(Number.MAX_SAFE_INTEGER) && value >= BigInt(-Number.MAX_SAFE_INTEGER) ? Number(value) : value.toString();\n  }\n  if (value instanceof Date) {\n    const iso = value.toISOString();\n    return iso.endsWith(\"T00:00:00.000Z\") ? iso.slice(0, 10) : iso;\n  }\n  // A DuckDB STRUCT / LIST / MAP arrives as an object. `String(...)` renders it\n  // \"[object Object]\" — the cell's content is simply gone. Serialise instead:\n  // this is a data cell, not an id or a matcher key, so preserving what the\n  // column holds beats reducing it to a placeholder.\n  //\n  // Guarded, because `JSON.stringify` is not total on what DuckDB can hand us:\n  // it throws on a BIGINT nested in a struct (the branch above only unwraps a\n  // top-level bigint) and on a circular ref, and returns undefined when an\n  // object's `toJSON` does. A cell that cannot be serialised falls back to the\n  // old rendering rather than aborting the whole read.\n  if (value !== null && typeof value === \"object\") return safeJsonCell(value);\n  return value;\n}\n\n/** One raw DuckDB row → a CollectionItem, or null when the key cell is\n *  missing/empty (the row can't be addressed). The primaryKey field is\n *  OVERWRITTEN with the (possibly encoded) record id so `item[primaryKey]`\n *  and the record's address never drift — same invariant the file store's\n *  write path enforces. Pure + exported for unit tests. */\nexport function csvRowToItem(row: Record<string, unknown>, primaryKey: string): CollectionItem | null {\n  const normalized = Object.fromEntries(Object.entries(row).map(([key, value]) => [key, normalizeCsvValue(value)]));\n  const rawKey = normalized[primaryKey];\n  // Identity: same rule as every other primaryKey read. `normalizeCsvValue` has\n  // already flattened objects to JSON, so anything without a text form here is\n  // genuinely unaddressable.\n  const keyText = fieldTextOrNull(rawKey);\n  if (keyText === null || keyText === \"\") return null;\n  return { ...normalized, [primaryKey]: encodeCsvRecordId(keyText) };\n}\n\n/** Dedupe by record id, LAST row wins (matches `csvRead`'s last-match\n *  pick). Returns the surviving items in first-seen order. Pure +\n *  exported for unit tests. */\nexport function dedupeByRecordId(items: CollectionItem[], primaryKey: string): { items: CollectionItem[]; duplicates: number } {\n  const byId = new Map<string, CollectionItem>();\n  for (const item of items) byId.set(String(item[primaryKey]), item);\n  return { items: [...byId.values()], duplicates: items.length - byId.size };\n}\n\n/** True when a thrown DuckDB error is the `types` pin naming a column the\n *  CSV doesn't have — the schema/file-mismatch case the caller downgrades\n *  to \"empty collection + warn\" instead of a 500. */\nfunction isMissingKeyColumnError(err: unknown): boolean {\n  return String(err).includes(\"do not exist in the CSV\");\n}\n\n// ---------------------------------------------------------------------------\n// Encoding: never touch the user's file — decode to a tmpdir cache copy\n// ---------------------------------------------------------------------------\n\n/** Bytes sniffed for UTF-8 validity. The trailing 3 bytes of the sample\n *  are dropped so a multibyte char split at the boundary can't produce a\n *  false negative on a valid file. */\nconst SNIFF_BYTES = 1024 * 1024;\n\nfunction isValidUtf8(buf: Buffer): boolean {\n  try {\n    new TextDecoder(\"utf-8\", { fatal: true }).decode(buf);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/** Detect the (best-effort) encoding of a non-UTF-8 buffer. BOMs decide\n *  UTF-16; otherwise cp932 (the Shift_JIS superset — Excel-exported\n *  Japanese CSVs are the primary non-UTF-8 case this feature serves). */\nfunction fallbackEncoding(buf: Buffer): string {\n  if (buf.length >= 2 && buf[0] === 0xff && buf[1] === 0xfe) return \"utf-16le\";\n  if (buf.length >= 2 && buf[0] === 0xfe && buf[1] === 0xff) return \"utf-16be\";\n  return \"cp932\";\n}\n\nexport function cacheDir(): string {\n  return path.join(tmpdir(), \"mulmoclaude-csv-utf8\");\n}\n\n/** Read only the first `bytes` of a file — the encoding sniff must not\n *  pull a multi-hundred-MB CSV into memory on the (common) UTF-8 path. */\nasync function readHead(absPath: string, bytes: number): Promise<Buffer> {\n  const handle = await open(absPath, \"r\");\n  try {\n    const { size } = await handle.stat();\n    const buf = Buffer.alloc(Math.min(bytes, size));\n    await handle.read(buf, 0, buf.length, 0);\n    return buf;\n  } finally {\n    await handle.close();\n  }\n}\n\n/** Decode the whole file into a UTF-8 cache copy and return its path.\n *  Cache key = (path, mtime, size), so a replaced CSV re-decodes and an\n *  unchanged one never does. */\nasync function pathExists(target: string): Promise<boolean> {\n  try {\n    await stat(target);\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/** Best-effort removal of older decode-cache entries for the same source\n *  path — a frequently-replaced large CSV would otherwise accumulate one\n *  full copy per (mtime, size) forever. Runs AFTER the current copy is\n *  published; a concurrent reader holding an old fd is unaffected\n *  (unlink-while-open is safe on POSIX). */\nasync function evictSupersededCache(key: string, keepBasename: string): Promise<void> {\n  try {\n    const entries = await readdir(cacheDir());\n    await Promise.all(\n      entries.filter((name) => name.startsWith(`${key}-`) && name !== keepBasename).map((name) => unlink(path.join(cacheDir(), name)).catch(() => undefined)),\n    );\n  } catch {\n    // cache dir missing / unreadable — nothing to evict\n  }\n}\n\n/** Decode the whole file into a UTF-8 cache copy and return its path.\n *  Cache key = (path, mtime, size), so a replaced CSV re-decodes and an\n *  unchanged one never does; superseded copies are evicted. The cache\n *  lives in the SHARED OS tmpdir, so the dir is 0700 and files 0600 —\n *  decoded rows must not be readable by other local users. */\nasync function decodeToCache(absPath: string, info: { mtimeMs: number; size: number }): Promise<string> {\n  const key = createHash(\"sha256\").update(absPath).digest(\"hex\").slice(0, 16);\n  const cached = path.join(cacheDir(), `${key}-${Math.trunc(info.mtimeMs)}-${info.size}.csv`);\n  if (!(await pathExists(cached))) {\n    const whole = await readFile(absPath);\n    const encoding = fallbackEncoding(whole);\n    const text = iconv.decode(whole, encoding);\n    await mkdir(cacheDir(), { recursive: true, mode: 0o700 });\n    // Unique tmp name + rename in the SAME dir — atomic publish, and a\n    // concurrent decode of the same file just wins/loses the rename cleanly.\n    const tmp = `${cached}.${randomBytes(4).toString(\"hex\")}.tmp`;\n    await writeFile(tmp, text, { encoding: \"utf-8\", mode: 0o600 });\n    await rename(tmp, cached);\n    log.info(\"collections\", \"decoded non-UTF-8 dataSource file to cache\", { path: absPath, encoding });\n    await evictSupersededCache(key, path.basename(cached));\n  }\n  return cached;\n}\n\n/** Re-validate the dataSource file at READ time, mirroring the JSON\n *  store's per-read defenses: realpath containment (a symlink swapped in\n *  after discovery must not walk out of the workspace) and an lstat\n *  regular-file check (a symlink leaf is refused outright, even one\n *  pointing inside the workspace — same rule as `isRegularFile` on\n *  record files). Returns the stat info, or null for \"no readable file\"\n *  (ENOENT / refused), which callers render as an empty collection. */\nasync function safeCsvStat(absPath: string, workspaceRoot: string): Promise<{ mtimeMs: number; size: number } | null> {\n  if (!isContainedInRoot(absPath, workspaceRoot)) {\n    log.warn(\"collections\", \"dataSource read refused: path escapes workspace\", { path: absPath });\n    return null;\n  }\n  let info;\n  try {\n    info = await lstat(absPath);\n  } catch (err) {\n    if (isErrorWithCode(err) && err.code === \"ENOENT\") return null;\n    throw err;\n  }\n  if (!info.isFile()) {\n    log.warn(\"collections\", \"dataSource read refused: not a regular file (symlink?)\", { path: absPath });\n    return null;\n  }\n  return info;\n}\n\n/** Return a path DuckDB can read as UTF-8: the original file when it\n *  already is UTF-8 (the cheap, common case — only the head is sniffed),\n *  else a decoded cache copy (see `decodeToCache`). Returns null when\n *  there is no readable file (missing, symlink, or containment-refused —\n *  see `safeCsvStat`), which callers render as an empty collection. */\nasync function ensureUtf8CsvPath(absPath: string, workspaceRoot: string): Promise<string | null> {\n  const info = await safeCsvStat(absPath, workspaceRoot);\n  if (info === null) return null;\n  const head = await readHead(absPath, SNIFF_BYTES);\n  // Drop the tail bytes of a full-length sample so a multibyte char split\n  // at the boundary can't read as invalid UTF-8.\n  const sample = head.length === SNIFF_BYTES ? head.subarray(0, SNIFF_BYTES - 3) : head;\n  const hasUtf16Bom = head.length >= 2 && ((head[0] === 0xff && head[1] === 0xfe) || (head[0] === 0xfe && head[1] === 0xff));\n  if (!hasUtf16Bom && isValidUtf8(sample)) return absPath;\n  return decodeToCache(absPath, info);\n}\n\n// ---------------------------------------------------------------------------\n// DuckDB plumbing\n// ---------------------------------------------------------------------------\n\nlet instancePromise: Promise<DuckDBInstance> | null = null;\n\n/** Lazily create one shared in-memory DuckDB instance. The dynamic import\n *  keeps the native module OUT of core's load path — a platform where the\n *  prebuilt binding is missing degrades to a per-query error on dataSource\n *  collections only, never a broken core. A failed init is retried on the\n *  next call (the promise is reset). */\nasync function duckDbInstance(): Promise<DuckDBInstance> {\n  if (instancePromise === null) {\n    instancePromise = import(\"@duckdb/node-api\").then((mod) => mod.DuckDBInstance.create(\":memory:\"));\n  }\n  try {\n    return await instancePromise;\n  } catch (err) {\n    instancePromise = null;\n    throw new BackendUnavailableError(\n      `DuckDB is unavailable on this host (@duckdb/node-api failed to load: ${String(err)}) — dataSource collections cannot be read`,\n    );\n  }\n}\n\nexport async function queryCsv(sql: string, params: CsvQueryParam[]): Promise<Record<string, unknown>[]> {\n  const instance = await duckDbInstance();\n  const connection = await instance.connect();\n  try {\n    const reader = await connection.runAndReadAll(sql, params);\n    return reader.getRowObjectsJS();\n  } finally {\n    connection.disconnectSync();\n  }\n}\n\n// ---------------------------------------------------------------------------\n// The store operations (consumed by storeFor in ./store)\n// ---------------------------------------------------------------------------\n\n/** Every row of the CSV as records — capped, deduped, id-encoded. The\n *  key column is pinned to VARCHAR (see `readCsvArgs`). `workspaceRoot`\n *  drives the per-read containment check; omitted, the configured host\n *  root is used. */\nexport interface CsvListResult {\n  items: CollectionItem[];\n  /** True when the scan stopped at `MAX_CSV_ROWS` — `items` is then a\n   *  row-capped prefix, not the whole file. Surfaced to `ListPage.truncated`\n   *  so callers see the cap instead of inferring it from a warn log. */\n  truncated: boolean;\n}\n\nexport async function csvList(absPath: string, primaryKey: string, workspaceRoot?: string): Promise<CsvListResult> {\n  const utf8Path = await ensureUtf8CsvPath(absPath, workspaceRoot ?? getWorkspaceRoot());\n  if (utf8Path === null) return { items: [], truncated: false };\n  let rows: Record<string, unknown>[];\n  try {\n    rows = await queryCsv(`SELECT * FROM read_csv(${readCsvArgs(primaryKey)}) LIMIT ${MAX_CSV_ROWS + 1}`, [utf8Path]);\n  } catch (err) {\n    // The VARCHAR pin names a column the CSV doesn't have — a schema/file\n    // mismatch, rendered as an empty collection with a warn (same outcome\n    // the pre-pin `primaryKey in row` check produced), not a 500.\n    if (!isMissingKeyColumnError(err)) throw err;\n    log.warn(\"collections\", \"dataSource CSV has no primaryKey column — every row is skipped\", { path: absPath, primaryKey });\n    return { items: [], truncated: false };\n  }\n  const truncated = rows.length > MAX_CSV_ROWS;\n  if (truncated) {\n    log.warn(\"collections\", \"dataSource CSV truncated to row cap\", { path: absPath, cap: MAX_CSV_ROWS });\n    rows.length = MAX_CSV_ROWS;\n  }\n  const items = rows.map((row) => csvRowToItem(row, primaryKey)).filter((item): item is CollectionItem => item !== null);\n  const skipped = rows.length - items.length;\n  if (skipped > 0) log.warn(\"collections\", \"dataSource CSV rows skipped (empty key cell)\", { path: absPath, skipped });\n  const deduped = dedupeByRecordId(items, primaryKey);\n  if (deduped.duplicates > 0)\n    log.warn(\"collections\", \"dataSource CSV has duplicate key values (last row wins)\", { path: absPath, duplicates: deduped.duplicates });\n  return { items: deduped.items, truncated };\n}\n\n/** The scan-order ordinal column the last-match read adds. Underscore\n *  prefix keeps it out of any plausible CSV header namespace; it is\n *  stripped from the returned record either way. */\nconst ROW_ORDINAL = \"__mc_row\";\n\n/** One record by id. The comparison value rides as a prepared-statement\n *  parameter, and the LAST matching row is selected IN DuckDB (scan-order\n *  ordinal + LIMIT 1) — a CSV with thousands of duplicate keys must not\n *  materialize them all for one detail read. Consistent with csvList's\n *  last-wins dedupe. */\nexport async function csvRead(absPath: string, primaryKey: string, itemId: string, workspaceRoot?: string): Promise<CollectionItem | null> {\n  const utf8Path = await ensureUtf8CsvPath(absPath, workspaceRoot ?? getWorkspaceRoot());\n  if (utf8Path === null) return null;\n  const rawKey = decodeCsvRecordId(itemId);\n  // Errors (missing key column, malformed CSV, DuckDB unavailable)\n  // propagate — a clear 500 with the DuckDB message beats a silent 404.\n  const sql =\n    `SELECT * FROM (SELECT *, row_number() OVER () AS ${quoteIdent(ROW_ORDINAL)} FROM read_csv(${readCsvArgs(primaryKey)})) ` +\n    `WHERE CAST(${quoteIdent(primaryKey)} AS VARCHAR) = ? ORDER BY ${quoteIdent(ROW_ORDINAL)} DESC LIMIT 1`;\n  const rows = await queryCsv(sql, [utf8Path, rawKey]);\n  const last = rows.at(0);\n  if (last === undefined) return null;\n  const { [ROW_ORDINAL]: __ordinal, ...record } = last;\n  return csvRowToItem(record, primaryKey);\n}\n\n/** Run a validated aggregation query (the structured DSL — see\n *  `core/queryZ.ts`) over the WHOLE file: no row cap on the scan (a\n *  capped aggregate would be a wrong number), only the result-row LIMIT\n *  the compiler emits. Values are normalized like list/read rows so a\n *  chart consumer gets plain JSON scalars. */\nexport async function csvRunQuery(absPath: string, primaryKey: string, query: CollectionQuery, workspaceRoot?: string): Promise<Record<string, unknown>[]> {\n  const utf8Path = await ensureUtf8CsvPath(absPath, workspaceRoot ?? getWorkspaceRoot());\n  if (utf8Path === null) return [];\n  const { sql, params } = compileCsvQuery(query, primaryKey);\n  const rows = await queryCsv(sql, [utf8Path, ...params]);\n  return rows.map((row) => Object.fromEntries(Object.entries(row).map(([key, value]) => [key, normalizeCsvValue(value)])));\n}\n","// Shared `fs.watch` plumbing for the filesystem-backed stores.\n//\n// Every filesystem backend hits the same three quirks, so they are solved\n// once here rather than in each store:\n//\n//   - `fs.watch` throws on a missing directory, so the watch has to be armed\n//     lazily and re-armed rather than assumed;\n//   - a filename arrives as `null` on some platforms (we then can't say which\n//     record changed, only that something did);\n//   - `filename` is typed `string` but can arrive as a Buffer, which has no\n//     `startsWith` — calling it directly throws inside the callback and takes\n//     the watcher down with it.\n//\n// Single-artifact backends (one CSV, one db file) additionally watch the\n// PARENT directory rather than the file: an atomic replace swaps the inode,\n// and a watch bound to the old one goes silently deaf.\n\nimport { realpathSync, watch, type FSWatcher } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { log } from \"./host\";\n\n/** An atomic file replace (editor save, `mv` over the target) surfaces as\n *  2-3 events. Collapse them so one user action reports one change. */\nconst REPLACE_DEBOUNCE_MS = 300;\n\n/** The path to hand `watch()`, with Windows 8.3 short names resolved away.\n *\n *  ReadDirectoryChangesW reports filenames against the LONG path, but a watch\n *  opened on a short path (`C:\\Users\\RUNNER~1\\…` — what `os.tmpdir()` returns\n *  on GitHub's Windows runners) keeps the short form. libuv's\n *  `assert(!_wcsnicmp(filename, dir, dirlen))` in `src/win/fs-event.c` then\n *  aborts the PROCESS on the first event — a native assert, so neither\n *  `watcher.on(\"error\")` nor a try/catch can contain it.\n *\n *  POSIX is deliberately left alone: `realpath` there also collapses symlinks\n *  (`/var` → `/private/var` on macOS), which we neither need nor want to\n *  change. A failure falls back to the original path — worst case we are no\n *  worse off than before. */\nfunction watchablePath(dir: string): string {\n  if (process.platform !== \"win32\") return dir;\n  try {\n    return realpathSync.native(dir);\n  } catch {\n    return dir;\n  }\n}\n\nexport interface FsWatchHandle {\n  close: () => void;\n}\n\n/** Watch `dir`, reporting each accepted filename. `accept` decides what is\n *  noise; a null filename always passes (the platform didn't tell us which\n *  file, so the caller must assume the worst). */\nexport async function watchDirectory(\n  dir: string,\n  accept: (filename: string) => boolean,\n  onHit: (filename: string | null) => void,\n): Promise<FsWatchHandle | null> {\n  try {\n    await mkdir(dir, { recursive: true });\n    const watcher: FSWatcher = watch(watchablePath(dir), { persistent: false }, (_eventType, rawFilename) => {\n      // Defensive stringify: the callback's `string` typing is a lie on some\n      // platforms, and a Buffer reaching `accept` would throw in here — which\n      // kills the watcher, not just the event.\n      const filename = rawFilename === null ? null : String(rawFilename);\n      if (filename !== null && !accept(filename)) return;\n      onHit(filename);\n    });\n    watcher.on(\"error\", (err) => {\n      log.warn(\"collections\", \"fs watch error\", { dir, error: String(err) });\n    });\n    return { close: () => watcher.close() };\n  } catch (err) {\n    log.warn(\"collections\", \"fs watch start failed\", { dir, error: String(err) });\n    return null;\n  }\n}\n\n/** Watch the single file `absPath` by watching its PARENT directory, so an\n *  atomic replace can't strand the watch on a dead inode. `alsoAccept`\n *  widens the filter beyond the exact basename (sqlite's `-wal`/`-journal`\n *  sidecars). Reports are debounced: one replace, one call. */\nexport async function watchSingleFile(\n  absPath: string,\n  alsoAccept: (basename: string, filename: string) => boolean,\n  onChange: () => void,\n): Promise<FsWatchHandle | null> {\n  const dir = path.dirname(absPath);\n  const base = path.basename(absPath);\n  let timer: ReturnType<typeof setTimeout> | null = null;\n  const fire = (): void => {\n    if (timer) clearTimeout(timer);\n    timer = setTimeout(() => {\n      timer = null;\n      onChange();\n    }, REPLACE_DEBOUNCE_MS);\n    timer.unref?.();\n  };\n  const handle = await watchDirectory(dir, (filename) => filename === base || alsoAccept(base, filename), fire);\n  if (!handle) return null;\n  return {\n    close: () => {\n      if (timer) clearTimeout(timer);\n      timer = null;\n      handle.close();\n    },\n  };\n}\n\n/** An `FsWatchHandle` as a bare unsubscribe — `null` straight through, so an\n *  unarmed watch stays distinguishable from an armed one. Lives here rather\n *  than beside the store contract so both `store.ts` and the backends it\n *  registers can reach it without importing each other. */\nexport function closerFor(handle: FsWatchHandle | null): (() => void) | null {\n  return handle === null ? null : () => handle.close();\n}\n","// Pure paging/projection primitives shared by the store implementations.\n// Split out of store.ts so a backend module (e.g. sqliteStore.ts, which\n// store.ts imports to register its factory) can use them without a\n// runtime import cycle. store.ts re-exports everything here, so the\n// public `@mulmoclaude/core/collection/server` surface is unchanged.\n\nimport type { CollectionItem } from \"../core/schema\";\nimport { projectRecordFields } from \"../core/project\";\n\n/** Options for `page`. STORED fields only — computed fields (`derived` /\n *  `toggle` / `embed` / rollups) never reach the store; project them at the\n *  engine level after enrichment. */\nexport interface ListOptions {\n  /** 0-based offset into the store's stable order (see the contract). */\n  offset?: number;\n  /** Max records returned. Absent = all remaining (subject to store caps). */\n  limit?: number;\n  /** Keep only these fields per record (the primary key is always kept). */\n  fields?: readonly string[];\n}\n\nexport interface ListPage {\n  items: CollectionItem[];\n  /** Records in the collection before offset/limit — a lower bound when\n   *  `truncated` (the store capped its scan, e.g. `MAX_CSV_ROWS`). */\n  total: number;\n  truncated: boolean;\n}\n\nexport interface WriteOptions {\n  /** Create semantics: fail with `kind: \"conflict\"` when the record\n   *  already exists (an O_EXCL open in the file store — race-safe). */\n  refuseOverwrite?: boolean;\n}\n\n/** Project `fields` (+ the primary key, always) out of each record. Thin\n *  server-typed alias over the shared isomorphic `projectRecordFields`\n *  (../core/project.ts) — kept as the store layer's exported name. */\nexport function projectItemFields(items: CollectionItem[], fields: readonly string[] | undefined, primaryKey: string): CollectionItem[] {\n  return projectRecordFields(items, fields, primaryKey);\n}\n\n/** Slice + project an already-ordered full read into a `ListPage` — the\n *  shared emulation for stores without native paging. Pure, exported for\n *  tests. `limit: 0` is a valid \"count only\" page. */\nexport function pageFromFullRead(items: CollectionItem[], opts: ListOptions, primaryKey: string, truncated: boolean): ListPage {\n  const offset = Math.max(0, opts.offset ?? 0);\n  const end = opts.limit === undefined ? items.length : offset + Math.max(0, opts.limit);\n  return { items: projectItemFields(items.slice(offset, end), opts.fields, primaryKey), total: items.length, truncated };\n}\n","// SQLite-backed WRITABLE store (schema `storage: { type: \"sqlite\" }`) —\n// the first non-file backend, added to validate the CollectionStore\n// abstraction (plans/done/refactor-storage-virtualization.md, Stage 4).\n//\n// Layout: one database file (`schema.storage.path`, workspace-contained at\n// discovery AND re-checked here), one table\n// `records(id TEXT PRIMARY KEY, record TEXT NOT NULL)` holding each\n// record's JSON. The schema's primaryKey value is the row id, gated by the\n// SAME `safeRecordId` rule as the file store's `<id>.json` names, so ids\n// stay portable across backends and remote-view preflights keep holding.\n//\n// Engine: `node:sqlite` — no native npm dependency (DuckDB pain avoided).\n// The app's engines floor is Node >= 20.12 but node:sqlite needs >= 22.5,\n// so the module is imported LAZILY and a failure degrades to a thrown,\n// clearly-worded error — ONLY sqlite-backed collections break, exactly the\n// DuckDB pattern in csvStore.ts. See\n// packages/core/assets/helps/error-recovery.md.\n//\n// Order contract: list/page walk `ORDER BY id` (BINARY collation =\n// codepoint order) — the same documented lexicographic-by-record-id order\n// as the file store. Paging is NATIVE (LIMIT/OFFSET + COUNT(*)), the\n// store's `nativePaging: true` flag is honest.\n//\n// Change events: io.ts is not involved here, so this store publishes its\n// own `publishCollectionChange` after each successful write/delete —\n// keeping the CollectionStore contract \"a successful write/delete\n// publishes\" true for every backend.\n\nimport { lstat, mkdir } from \"node:fs/promises\";\nimport { closerFor, watchSingleFile } from \"./watchFs\";\nimport { BackendUnavailableError } from \"./backendAvailability\";\nimport path from \"node:path\";\nimport { hasNumberProp, isErrorWithCode, isRecord } from \"@mulmoclaude/common\";\nimport type { CollectionItem } from \"../core/schema\";\nimport type { LoadedCollection } from \"./discoveredCollection\";\nimport type { DeleteItemResult, IoOptions, WriteItemResult } from \"./io\";\nimport { collectionChangePayload, getWorkspaceRoot, log, publishCollectionChange } from \"./host\";\nimport { isContainedInRoot, safeRecordId } from \"./paths\";\nimport { projectItemFields, type ListOptions, type ListPage, type WriteOptions } from \"./storePage\";\nimport type { CollectionStore } from \"./store\";\n\n// Minimal structural view of node:sqlite — typed locally so the build does\n// not depend on @types/node shipping the (still experimental) module types.\ninterface SqliteStatement {\n  all: (...params: (string | number)[]) => unknown[];\n  get: (...params: (string | number)[]) => unknown;\n  run: (...params: (string | number)[]) => { changes: number | bigint };\n}\ninterface SqliteDatabase {\n  prepare: (sql: string) => SqliteStatement;\n  exec: (sql: string) => void;\n  close: () => void;\n}\ninterface SqliteModule {\n  DatabaseSync: new (dbPath: string) => SqliteDatabase;\n}\n\n/** A constructor's parameter and return types are not observable at runtime,\n *  so the check stops at \"DatabaseSync is constructible\" — the only member of\n *  the module this store ever touches. */\nfunction isSqliteModule(mod: unknown): mod is SqliteModule {\n  return isRecord(mod) && typeof mod.DatabaseSync === \"function\";\n}\n\nlet sqliteModule: Promise<SqliteModule> | null = null;\n\n/** Drops the memo first so a later call can retry (e.g. tests stubbing the\n *  runtime), then reports why the backend is unusable. */\nfunction sqliteUnavailable(reason: string): never {\n  sqliteModule = null;\n  throw new BackendUnavailableError(`sqlite storage needs the node:sqlite module (Node.js >= 22.5) — this runtime cannot load it: ${reason}`);\n}\n\n/** Lazy-load node:sqlite once. A runtime without it (Node < 22.5) throws a\n *  clearly-worded error the caller surfaces — never a bare MODULE_NOT_FOUND. */\nfunction loadSqlite(): Promise<SqliteModule> {\n  sqliteModule ??= import(\"node:sqlite\").then(\n    (mod) => (isSqliteModule(mod) ? mod : sqliteUnavailable(\"the module exposes no DatabaseSync constructor\")),\n    (err: unknown) => sqliteUnavailable(String(err)),\n  );\n  return sqliteModule;\n}\n\n/** The db file's on-disk state. A symlink or non-regular file is refused\n *  (file-disclosure defense, same rule as io.ts record files); ENOENT is\n *  just \"no records yet\". Any OTHER lstat failure (EACCES, EIO, …) is\n *  rethrown so reads surface a real filesystem problem instead of\n *  silently reporting an empty collection. */\nasync function dbFileState(absPath: string): Promise<\"missing\" | \"file\" | \"refused\"> {\n  try {\n    const info = await lstat(absPath);\n    return info.isFile() ? \"file\" : \"refused\";\n  } catch (err) {\n    if (isErrorWithCode(err) && err.code === \"ENOENT\") return \"missing\";\n    throw err;\n  }\n}\n\nconst CREATE_TABLE = \"CREATE TABLE IF NOT EXISTS records (id TEXT PRIMARY KEY, record TEXT NOT NULL)\";\n\ntype DbHandle = { kind: \"ok\"; database: SqliteDatabase } | { kind: \"missing\" } | { kind: \"refused\" };\n\n/** Open the database for one operation, classifying the two unavailable\n *  states so callers can map them honestly (`refused` ⇒ path-escape,\n *  `missing` ⇒ empty / not-found — conflating them would misreport a\n *  containment escape as \"item not found\"). The containment pre-check runs\n *  BEFORE mkdir even when the file is missing — `isContainedInRoot`\n *  resolves through the closest existing ancestor, so a symlinked-away\n *  parent can never make the recursive mkdir create directories outside\n *  the workspace (same pre/post belt-and-suspenders as io.ts writes). */\nasync function openDb(absPath: string, workspaceRoot: string, mode: \"read\" | \"write\"): Promise<DbHandle> {\n  const state = await dbFileState(absPath);\n  if (state === \"refused\") {\n    log.warn(\"collections\", \"sqlite database refused: not a regular file\", { path: absPath });\n    return { kind: \"refused\" };\n  }\n  if (!isContainedInRoot(path.dirname(absPath), workspaceRoot)) {\n    log.warn(\"collections\", \"sqlite refused: database dir escapes workspace via symlink\", { path: absPath });\n    return { kind: \"refused\" };\n  }\n  if (mode === \"read\" && state === \"missing\") return { kind: \"missing\" };\n  if (mode === \"write\") {\n    await mkdir(path.dirname(absPath), { recursive: true });\n    if (!isContainedInRoot(path.dirname(absPath), workspaceRoot)) {\n      log.warn(\"collections\", \"sqlite write refused: database dir escapes workspace via symlink (post-mkdir)\", { path: absPath });\n      return { kind: \"refused\" };\n    }\n  }\n  const { DatabaseSync } = await loadSqlite();\n  const database = new DatabaseSync(absPath);\n  // Wait for a concurrent writer's lock instead of failing fast with\n  // SQLITE_BUSY. PRAGMA (not the constructor's `timeout` option, which\n  // only exists on Node >= 22.16 — our sqlite floor is 22.5).\n  database.exec(\"PRAGMA busy_timeout = 5000\");\n  database.exec(CREATE_TABLE);\n  return { kind: \"ok\", database };\n}\n\n/** Run `operation` against the database and always close it; unavailable\n *  states resolve through `onUnavailable` so each caller maps `missing`\n *  vs `refused` to its own result kind. */\nasync function withDb<T>(\n  absPath: string,\n  workspaceRoot: string,\n  mode: \"read\" | \"write\",\n  onUnavailable: (reason: \"missing\" | \"refused\") => T,\n  operation: (database: SqliteDatabase) => T | Promise<T>,\n): Promise<T> {\n  const handle = await openDb(absPath, workspaceRoot, mode);\n  if (handle.kind !== \"ok\") return onUnavailable(handle.kind);\n  try {\n    return await operation(handle.database);\n  } finally {\n    handle.database.close();\n  }\n}\n\n// SQLite extended result codes for the duplicate-id failure our INSERT hits:\n// a PRIMARY KEY clash, and the plain-UNIQUE-index equivalent.\nconst SQLITE_CONSTRAINT_PRIMARYKEY = 1555;\nconst SQLITE_CONSTRAINT_UNIQUE = 2067;\n\n/** node:sqlite throws ERR_SQLITE_ERROR with the SQLite extended result\n *  code on `errcode`. Checked structurally (message text kept only as a\n *  fallback for runtimes that don't expose `errcode`). */\nfunction isUniqueConstraintError(err: unknown): boolean {\n  if (hasNumberProp(err, \"errcode\")) return err.errcode === SQLITE_CONSTRAINT_PRIMARYKEY || err.errcode === SQLITE_CONSTRAINT_UNIQUE;\n  return String(err).includes(\"UNIQUE constraint\");\n}\n\nfunction parseRow(raw: unknown): CollectionItem | null {\n  if (typeof raw !== \"string\") return null;\n  try {\n    const parsed: unknown = JSON.parse(raw);\n    return isRecord(parsed) ? parsed : null;\n  } catch {\n    return null;\n  }\n}\n\n/** One column of a result row. node:sqlite types rows as `unknown`, so a\n *  value that is not a row object yields no column at all. */\nfunction readColumn(row: unknown, column: string): unknown {\n  return isRecord(row) ? row[column] : undefined;\n}\n\nfunction rowsToItems(rows: unknown[]): CollectionItem[] {\n  return rows.map((row) => parseRow(readColumn(row, \"record\"))).filter((item): item is CollectionItem => item !== null);\n}\n\n/** node:sqlite hands back an integer column as `number`, or as `bigint` once\n *  it leaves the safe-integer range — COUNT(*) can be either. */\nfunction countRecords(database: SqliteDatabase): number {\n  const count = readColumn(database.prepare(\"SELECT COUNT(*) AS n FROM records\").get(), \"n\");\n  if (typeof count === \"number\") return count;\n  if (typeof count === \"bigint\") return Number(count);\n  throw new Error(`sqlite COUNT(*) returned no numeric row count (got ${typeof count})`);\n}\n\nasync function sqliteList(absPath: string, workspaceRoot: string): Promise<CollectionItem[]> {\n  return withDb<CollectionItem[]>(\n    absPath,\n    workspaceRoot,\n    \"read\",\n    () => [],\n    (database) => rowsToItems(database.prepare(\"SELECT record FROM records ORDER BY id\").all()),\n  );\n}\n\nasync function sqlitePage(absPath: string, primaryKey: string, opts: ListOptions, workspaceRoot: string): Promise<ListPage> {\n  const emptyPage: ListPage = { items: [], total: 0, truncated: false };\n  return withDb(\n    absPath,\n    workspaceRoot,\n    \"read\",\n    () => emptyPage,\n    (database) => {\n      const total = countRecords(database);\n      const offset = Math.max(0, opts.offset ?? 0);\n      const limit = opts.limit === undefined ? -1 : Math.max(0, opts.limit); // LIMIT -1 = unbounded in SQLite\n      const rows = database.prepare(\"SELECT record FROM records ORDER BY id LIMIT ? OFFSET ?\").all(limit, offset);\n      return { items: projectItemFields(rowsToItems(rows), opts.fields, primaryKey), total, truncated: false };\n    },\n  );\n}\n\nasync function sqliteRead(absPath: string, itemId: string, workspaceRoot: string): Promise<CollectionItem | null> {\n  const safeId = safeRecordId(itemId);\n  if (safeId === null) return null;\n  return withDb<CollectionItem | null>(\n    absPath,\n    workspaceRoot,\n    \"read\",\n    () => null,\n    (database) => {\n      const row = database.prepare(\"SELECT record FROM records WHERE id = ?\").get(safeId);\n      return parseRow(readColumn(row, \"record\"));\n    },\n  );\n}\n\nasync function sqliteWrite(\n  absPath: string,\n  itemId: string,\n  item: CollectionItem,\n  opts: { workspaceRoot: string; publishRoot?: string | undefined; slug?: string | undefined; refuseOverwrite?: boolean | undefined },\n): Promise<WriteItemResult> {\n  const safeId = safeRecordId(itemId);\n  if (safeId === null) return { kind: \"invalid-id\", itemId };\n  const outcome = await withDb<WriteItemResult>(\n    absPath,\n    opts.workspaceRoot,\n    \"write\",\n    () => ({ kind: \"path-escape\", itemId: safeId }),\n    (database) => {\n      const payload = JSON.stringify(item);\n      if (opts.refuseOverwrite) {\n        // The PRIMARY KEY constraint is the race-safe create gate — the\n        // sqlite twin of the file store's O_EXCL open.\n        try {\n          database.prepare(\"INSERT INTO records (id, record) VALUES (?, ?)\").run(safeId, payload);\n        } catch (err) {\n          if (isUniqueConstraintError(err)) return { kind: \"conflict\", itemId: safeId };\n          throw err;\n        }\n      } else {\n        database.prepare(\"INSERT INTO records (id, record) VALUES (?, ?) ON CONFLICT(id) DO UPDATE SET record = excluded.record\").run(safeId, payload);\n      }\n      return { kind: \"ok\", itemId: safeId, item };\n    },\n  );\n  // Publish AFTER the write lands (same ordering rule as io.ts) so a live\n  // subscriber that refetches always sees the new record.\n  if (outcome.kind === \"ok\" && opts.slug) publishCollectionChange(collectionChangePayload({ slug: opts.slug, ids: [safeId], op: \"upsert\" }, opts.publishRoot));\n  return outcome;\n}\n\nasync function sqliteDelete(\n  absPath: string,\n  itemId: string,\n  opts: { workspaceRoot: string; publishRoot?: string | undefined; slug?: string },\n): Promise<DeleteItemResult> {\n  const safeId = safeRecordId(itemId);\n  if (safeId === null) return { kind: \"invalid-id\", itemId };\n  // `missing` db = nothing was ever written ⇒ not-found; `refused`\n  // (containment/symlink) must surface as path-escape, never as a 404.\n  const outcome = await withDb<DeleteItemResult>(\n    absPath,\n    opts.workspaceRoot,\n    \"read\",\n    (reason) => (reason === \"refused\" ? { kind: \"path-escape\", itemId: safeId } : { kind: \"not-found\", itemId: safeId }),\n    (database) => {\n      const { changes } = database.prepare(\"DELETE FROM records WHERE id = ?\").run(safeId);\n      return Number(changes) === 0 ? { kind: \"not-found\", itemId: safeId } : { kind: \"ok\", itemId: safeId };\n    },\n  );\n  if (outcome.kind === \"ok\" && opts.slug) publishCollectionChange(collectionChangePayload({ slug: opts.slug, ids: [safeId], op: \"delete\" }, opts.publishRoot));\n  return outcome;\n}\n\n/** Best-effort full WAL checkpoint so the MAIN db file alone is a\n *  complete snapshot (committed pages in `<db>-wal` are folded in and the\n *  WAL truncated). Used by `deleteCollection` before archiving. Returns\n *  false on any failure (runtime without node:sqlite, locked db, missing\n *  file) — the caller then archives the sidecar files alongside the db so\n *  no committed data is lost either way. */\nexport async function checkpointSqliteDatabase(absPath: string): Promise<boolean> {\n  try {\n    const { DatabaseSync } = await loadSqlite();\n    const database = new DatabaseSync(absPath);\n    try {\n      database.exec(\"PRAGMA wal_checkpoint(TRUNCATE)\");\n    } finally {\n      database.close();\n    }\n    return true;\n  } catch {\n    return false;\n  }\n}\n\n/** A `storage: sqlite` store over `collection.storageFile`. A schema whose\n *  `storageFile` failed to resolve yields a read-only EMPTY store rather\n *  than a writable one — same fail-closed rule as the CSV store. */\nexport function sqliteStoreFor(collection: LoadedCollection, opts: IoOptions): CollectionStore {\n  const file = collection.storageFile;\n  const key = collection.schema.primaryKey;\n  const slug = opts.slug ?? collection.slug;\n  const root = (): string => opts.workspaceRoot ?? getWorkspaceRoot();\n  // The EXPLICIT root (undefined when the caller relied on the host default) —\n  // `root()` has already collapsed that distinction, and the change payload\n  // must report only a root the caller actually named.\n  const publishRoot = opts.workspaceRoot;\n  if (file === undefined) {\n    return {\n      capabilities: { writable: false, nativeQuery: false, nativePaging: false },\n      list: () => Promise.resolve([]),\n      page: () => Promise.resolve({ items: [], total: 0, truncated: false }),\n      read: () => Promise.resolve(null),\n    };\n  }\n  return {\n    capabilities: { writable: true, nativeQuery: false, nativePaging: true },\n    list: () => sqliteList(file, root()),\n    page: (pageOpts = {}) => sqlitePage(file, key, pageOpts, root()),\n    read: (itemId: string) => sqliteRead(file, itemId, root()),\n    write: (itemId: string, item: CollectionItem, writeOpts: WriteOptions = {}) =>\n      sqliteWrite(file, itemId, item, { workspaceRoot: root(), publishRoot, slug, refuseOverwrite: writeOpts.refuseOverwrite }),\n    delete: (itemId: string) => sqliteDelete(file, itemId, { workspaceRoot: root(), publishRoot, slug }),\n    // One db file holds every record, so an event can't name a record. The\n    // sidecars count as hits: sqlite writes land in `<db>-wal` first, and a\n    // change that only touched the WAL is still a change.\n    watch: async (onChange) =>\n      closerFor(\n        await watchSingleFile(\n          file,\n          (base, name) => name.startsWith(base),\n          () => onChange({ kind: \"collection\" }),\n        ),\n      ),\n  };\n}\n","// Storage abstraction over a collection's records — the one seam where\n// \"where do the rows come from\" is decided. Implementations, selected by\n// the schema's storage kind (`storageKindFor`) through the factory\n// registry below:\n//\n//   - file store (\"file\"): the classic `<dataDir>/<itemId>.json` records\n//     (io.ts), writable through the governed write paths;\n//   - CSV store (\"csv\", csvStore.ts): the rows of an external `dataSource`\n//     file, queried through DuckDB — READ-ONLY by definition;\n//   - SQLite store (\"sqlite\", sqliteStore.ts): records in a single\n//     node:sqlite database file — writable, native paging.\n//\n// Reads AND writes go through `storeFor(...)`. Writability is encoded by\n// PRESENCE: `write`/`delete` exist only on writable stores, so \"write\n// through a read-only store\" is a type error, not a runtime surprise —\n// entry points refuse with `readOnlyRefusal` when the methods are absent.\n// (`io.ts#writeItem`/`deleteItem` remain the file-store implementation and\n// the change-event choke point; only callers WITHOUT a `LoadedCollection`\n// in hand — e.g. `spawn.ts` — still call them directly.)\n//\n// BACKWARD COMPATIBILITY — read before evolving this interface.\n// This store is INTERNAL and may change shape, but two user-facing\n// contracts built on top of it are effectively FROZEN, because they are\n// consumed by LLM-authored custom-view HTML files that already live in\n// users' workspaces (`data/skills/*/views/*.html`, `feeds/*/views/*.html`).\n// Those files cannot be migrated centrally — there is no registry of them,\n// and users expect a view authored months ago to keep working:\n//\n//   - the desktop view-data HTTP surface (`server/api/routes/collections.ts`:\n//     GET `?fields=`/`?ids=`, PUT items, POST /query, POST /actions/<id>,\n//     response shapes, error semantics) as documented in\n//     `packages/core/assets/helps/custom-view.md`;\n//   - the remote-view bridge (`../../remote-view/index.ts`: `__MC_VIEW`\n//     protocol, `getItems` page shape `{ items, total, offset, limit }`,\n//     mutate replies) as documented in\n//     `packages/core/assets/helps/custom-view-remote.md`.\n//\n// Any storage-virtualization work (new backends, paging, capability\n// changes) must be invisible at those two surfaces: evolve them by\n// ADDITIVE, backward-compatible supersets only — never rename/repurpose\n// params or message types, never change existing response shapes, never\n// let a new backend alter what an existing view observes.\n\nimport { fieldText } from \"../core/fieldText\";\nimport type { CollectionItem, CollectionStorageKind } from \"../core/schema\";\nimport type { CollectionQuery } from \"../core/queryZ\";\nimport { isReadOnlySchema, storageKindFor } from \"../core/schema\";\nimport type { LoadedCollection } from \"./discoveredCollection\";\nimport { deleteItem, listItems, readItem, writeItem, type DeleteItemResult, type IoOptions, type WriteItemResult } from \"./io\";\nimport { csvList, csvRead, csvRunQuery } from \"./csvStore\";\nimport { sqliteStoreFor } from \"./sqliteStore\";\nimport { pageFromFullRead, type ListOptions, type ListPage, type WriteOptions } from \"./storePage\";\nimport { closerFor, watchDirectory, watchSingleFile } from \"./watchFs\";\n\n// The pure paging/projection primitives live in storePage.ts (so backend\n// modules can share them without an import cycle); re-exported here to\n// keep the public surface where it has always been.\nexport { pageFromFullRead, projectItemFields, type ListOptions, type ListPage, type WriteOptions } from \"./storePage\";\n\nexport interface CollectionStoreCapabilities {\n  readonly writable: boolean;\n  /** Native aggregation engine for the structured DSL (`core/queryZ.ts`).\n   *  False ⇒ `query` is absent; the engine-level fallback (enrich →\n   *  JSONL → DuckDB, `queryRunner.ts`) answers aggregations instead. */\n  readonly nativeQuery: boolean;\n  /** True when `page` resolves offset/limit inside the backend. False ⇒\n   *  `page` is emulated (full read, then slice) — same result, no saving. */\n  readonly nativePaging: boolean;\n}\n\n/** The storage contract every backend must satisfy (verified by the shared\n *  contract test suite, `test/workspace/collections/test_storeContract.ts`):\n *\n *  1. STABLE ORDER — `page` walks a documented deterministic order (file\n *     store: lexicographic by record id; CSV store: file row order; SQLite\n *     store: `ORDER BY id`), so `offset`-paging never skips or repeats\n *     records between calls. Sorting by arbitrary fields is NOT the\n *     store's job.\n *  2. IDS — minting/resolving record ids is the store's job (the CSV\n *     store's `id0x…` encoding stays inside it); `read` resolves every id\n *     `list`/`page` returned.\n *  3. CONTAINMENT — a store never serves data from outside the workspace;\n *     symlink/realpath defenses are each implementation's obligation\n *     (io.ts is the reference).\n *  4. HONEST AGGREGATION — `query`, when present, is computed over the\n *     WHOLE data set, never from a capped read. */\n/** What a store reports when its records changed underneath it.\n *\n *  Two granularities, because backends genuinely differ: a per-record file\n *  store knows WHICH record changed, while a single-artifact backend (one\n *  CSV, one db file, one remote snapshot) only knows THAT something did.\n *  Naming the difference here is what lets the watcher react uniformly\n *  instead of branching on the backend. */\nexport type StoreChange = { readonly kind: \"item\"; readonly itemId: string } | { readonly kind: \"collection\" };\n\nexport type StoreChangeListener = (change: StoreChange) => void;\n\n/** Detaches a `watch` subscription. */\nexport type StoreUnsubscribe = () => void;\n\nexport interface CollectionStore {\n  readonly capabilities: CollectionStoreCapabilities;\n  /** Every record, in the store's stable order. CSV store: capped at\n   *  `MAX_CSV_ROWS` (see csvStore.ts). Prefer `page` in new code. */\n  list: () => Promise<CollectionItem[]>;\n  /** One page of records — offset/limit/projection over the stable order. */\n  page: (opts?: ListOptions) => Promise<ListPage>;\n  /** One record by id, or null when missing/invalid. */\n  read: (itemId: string) => Promise<CollectionItem | null>;\n  /** Aggregation over the WHOLE data set (the structured DSL,\n   *  `core/queryZ.ts`) — present only on stores with a native query\n   *  engine (the CSV store). Absent ⇒ use the engine-level fallback\n   *  (`runCollectionQuery`), never emulate ad hoc. */\n  query?: ((query: CollectionQuery) => Promise<Record<string, unknown>[]>) | undefined;\n  /** Present ONLY when `capabilities.writable` — absence IS the read-only\n   *  refusal (surface it with `readOnlyRefusal`). A successful write/delete\n   *  publishes a collection-change event: the store always threads the\n   *  collection's slug into the publish hook, so no writer can forget it. */\n  write?: ((itemId: string, item: CollectionItem, opts?: WriteOptions) => Promise<WriteItemResult>) | undefined;\n  delete?: ((itemId: string) => Promise<DeleteItemResult>) | undefined;\n  /** Subscribe to changes made OUTSIDE this process — a file edited by the\n   *  agent, a CSV replaced by the user, a db written by another tool.\n   *\n   *  Resolves to an unsubscribe function, or to `null` when the backend\n   *  could not arm the watch (no inotify watches left, an unreadable\n   *  directory). `null` is NOT the same as absent `watch`: absent means the\n   *  backend never reports changes and the caller should settle for its\n   *  periodic pass, while `null` means this attempt failed and mounting\n   *  should be retried. Swallowing the difference strands a collection on\n   *  stale data until the process restarts.\n   *\n   *  This is the store's job because only it knows where its bytes live and\n   *  what its own change events look like (which paths to watch, which\n   *  filenames are noise, whether an atomic replace surfaces as two events).\n   *  Absorbing that noise — debouncing a replace into one report — belongs\n   *  here too. What to DO about a change (reconcile bells, sweep, publish)\n   *  is the watcher's policy and is deliberately NOT expressed here.\n   *\n   *  Absent ⇒ the backend cannot report external changes; the caller falls\n   *  back to its periodic pass. */\n  watch?: (onChange: StoreChangeListener) => Promise<StoreUnsubscribe | null>;\n}\n\n/** The file store's stable order: lexicographic by record id (codepoint\n *  compare — locale-independent). `listItems` returns readdir order, which\n *  is filesystem-dependent; paging needs determinism. */\nfunction sortByRecordId(items: CollectionItem[], primaryKey: string): CollectionItem[] {\n  return [...items].sort((left, right) => {\n    const leftId = fieldText(left[primaryKey]);\n    const rightId = fieldText(right[primaryKey]);\n    if (leftId < rightId) return -1;\n    return leftId > rightId ? 1 : 0; // 0 on equality — a comparator that never ties breaks sort's contract\n  });\n}\n\n/** True when the collection accepts UI/tool writes. A `dataSource`\n *  collection is read-only: updates happen by editing/replacing the\n *  data file itself. Every write entry point checks this BEFORE calling\n *  `writeItem`/`deleteItem` — server-enforced, not just UI-hidden. */\nexport function collectionWritable(collection: Pick<LoadedCollection, \"schema\">): boolean {\n  return !isReadOnlySchema(collection.schema);\n}\n\n/** The one-line refusal write paths surface (HTTP 405 / MCP error text). */\nexport function readOnlyRefusal(slug: string): string {\n  return `collection '${slug}' is read-only (backed by an external dataSource) — update the data file itself instead`;\n}\n\n/** A `dataSource` store over `file` (CSV row order; DuckDB-native query).\n *  A schema whose `dataSourceFile` failed to resolve yields a read-only\n *  EMPTY store rather than falling back to the (writable) file store — a\n *  half-loaded read-only collection must never become writable. */\nfunction csvStoreFor(collection: LoadedCollection, opts: IoOptions): CollectionStore {\n  const file = collection.dataSourceFile;\n  const key = collection.schema.primaryKey;\n  const listAll = () => (file === undefined ? Promise.resolve({ items: [], truncated: false }) : csvList(file, key, opts.workspaceRoot));\n  return {\n    capabilities: { writable: false, nativeQuery: true, nativePaging: false },\n    list: () => listAll().then((result) => result.items),\n    page: (pageOpts = {}) => listAll().then((result) => pageFromFullRead(result.items, pageOpts, key, result.truncated)),\n    read: (itemId: string) => (file === undefined ? Promise.resolve(null) : csvRead(file, key, itemId, opts.workspaceRoot)),\n    query: (query: CollectionQuery) => (file === undefined ? Promise.resolve([]) : csvRunQuery(file, key, query, opts.workspaceRoot)),\n    // One file holds every row, so an event can't name a record — only that\n    // the set changed. `watchSingleFile` watches the parent dir (an atomic\n    // replace swaps the inode) and debounces the 2-3 events a replace emits.\n    ...(file === undefined\n      ? {}\n      : {\n          watch: async (onChange) =>\n            closerFor(\n              await watchSingleFile(\n                file,\n                () => false,\n                () => onChange({ kind: \"collection\" }),\n              ),\n            ),\n        }),\n  };\n}\n\n/** The classic file store over `<dataDir>/<itemId>.json` records. */\nfunction fileStoreFor(collection: LoadedCollection, opts: IoOptions): CollectionStore {\n  const key = collection.schema.primaryKey;\n  const ioOpts: IoOptions = { ...opts, slug: opts.slug ?? collection.slug };\n  return {\n    capabilities: { writable: true, nativeQuery: false, nativePaging: false },\n    list: () => listItems(collection.dataDir, opts),\n    page: async (pageOpts = {}) => pageFromFullRead(sortByRecordId(await listItems(collection.dataDir, opts), key), pageOpts, key, false),\n    read: (itemId: string) => readItem(collection.dataDir, itemId, opts),\n    write: (itemId: string, item: CollectionItem, writeOpts: WriteOptions = {}) =>\n      writeItem(collection.dataDir, itemId, item, { ...ioOpts, refuseOverwrite: writeOpts.refuseOverwrite }),\n    delete: (itemId: string) => deleteItem(collection.dataDir, itemId, ioOpts),\n    // One file per record, so an event names the record that changed —\n    // reported at `item` granularity, which is what lets the watcher\n    // reconcile just that record instead of the whole collection. Dot-\n    // prefixed names are skipped: atomic writes, OS metadata and editor swap\n    // files are not records.\n    watch: async (onChange) =>\n      closerFor(\n        await watchDirectory(\n          collection.dataDir,\n          (name) => name.endsWith(\".json\") && !name.startsWith(\".\"),\n          (filename) => onChange(filename === null ? { kind: \"collection\" } : { kind: \"item\", itemId: filename.slice(0, -\".json\".length) }),\n        ),\n      ),\n  };\n}\n\nexport type CollectionStoreFactory = (collection: LoadedCollection, opts: IoOptions) => CollectionStore;\n\n// The store factory registry (plans/done/refactor-storage-virtualization.md,\n// Stage 3): schema storage kind → implementation. Factories live in CORE\n// (dependency-direction rule — never plugin-registered); a new backend is\n// one factory + a `StorageZ` variant + a pass of the contract test suite.\nconst storeFactories = new Map<CollectionStorageKind, CollectionStoreFactory>([\n  [\"file\", fileStoreFor],\n  [\"csv\", csvStoreFor],\n  [\"sqlite\", sqliteStoreFor],\n]);\n\n/** Pick the store implementation for a discovered collection via the\n *  factory registry. An unknown kind cannot normally reach here (the\n *  schema's `StorageZ` union gates it), so the throw is a loud invariant\n *  breach, not a user-facing path. */\nexport function storeFor(collection: LoadedCollection, opts: IoOptions = {}): CollectionStore {\n  const kind = storageKindFor(collection.schema);\n  const factory = storeFactories.get(kind);\n  if (!factory) throw new Error(`no store factory registered for storage kind '${kind}'`);\n  return factory(collection, opts);\n}\n","// Pure helpers for `kind: \"mutate\"` actions (plan step ④ of\n// plans/done/collection-ontology.md): the `$params.<name>` reference syntax\n// used in a mutate action's `set` map. ONE parser shared by the schema\n// refines (`schemaZ.ts` validates that every reference names a declared\n// param) and the server executor (`server/mutate.ts` resolves them\n// against the submitted form values) — the two must agree on what a\n// reference is. Zod-free and I/O-free.\n\n/** A `set` value starting with this prefix reads the named form param\n *  instead of being written literally. */\nexport const PARAM_REF_PREFIX = \"$params.\";\n\n/** The param name a `set` value references, or null when the value is a\n *  literal (non-strings can never be references). A bare/empty prefix\n *  (`\"$params.\"`) returns the empty string — the schema refine rejects\n *  it as an undeclared param, never silently treats it as a literal. */\nexport function paramRefName(value: unknown): string | null {\n  if (typeof value !== \"string\" || !value.startsWith(PARAM_REF_PREFIX)) return null;\n  return value.slice(PARAM_REF_PREFIX.length);\n}\n\n/** Resolve a mutate action's `set` map against the submitted params:\n *  literals pass through, `$params.<name>` reads the param value. An\n *  ABSENT referenced param omits the key entirely (merge semantics —\n *  the stored value survives), mirroring how the record form omits\n *  empty optionals rather than writing empty strings. */\nexport function resolveMutateSet(set: Record<string, string | number | boolean>, params: Record<string, unknown>): Record<string, unknown> {\n  const resolved: Record<string, unknown> = {};\n  for (const [key, value] of Object.entries(set)) {\n    const ref = paramRefName(value);\n    if (ref === null) {\n      resolved[key] = value;\n      continue;\n    }\n    const paramValue = params[ref];\n    if (paramValue !== undefined && paramValue !== null && paramValue !== \"\") resolved[key] = paramValue;\n  }\n  return resolved;\n}\n","// Cross-field rules for the collection schema — the checks a single field\n// cannot make because it cannot see its siblings.\n//\n// Each one is a named predicate over a parsed-but-unvalidated schema, returning\n// true when the schema is ACCEPTABLE. They live here rather than as anonymous\n// lambdas inside `./schemaZ`'s refine chain so each rule can be unit-tested on\n// its own, and so the chain reads as a list of rule names instead of 500 lines\n// of inline logic.\n//\n// Every rule guards a misconfiguration that would otherwise fail SILENTLY at\n// runtime: a completion bell that never rings, a spawn that fans out forever, a\n// currency that mislabels amounts. That is why they are hard errors at load\n// rather than warnings.\n//\n// The type-only import of `CollectionSchemaInput` is erased at emit, so the\n// module graph stays acyclic at runtime: schemaZ → schemaRules → schema.\n\nimport { fieldTextOrNull } from \"./fieldText\";\nimport { isSafeSlug, isSafeRecordId } from \"./ids\";\nimport { paramRefName } from \"./mutateAction\";\nimport { COMPUTED_TYPES } from \"./schema\";\nimport type { CollectionSchemaInput } from \"./schemaZ\";\n\ntype Schema = CollectionSchemaInput;\ntype Fields = Schema[\"fields\"];\n\n// A field pointer names a REAL declared field only when it is an OWN key of\n// `fields`. A bare `fields[name]` reaches inherited Object.prototype members\n// (`constructor`, `__proto__`, `toString`), so an LLM-authored pointer like\n// `completionField: \"constructor\"` would resolve to a function and pass every\n// \"names a declared field\" check, then misfire silently at runtime (#2318).\nconst declaredField = (fields: Fields, name: string): Fields[string] | undefined => (Object.hasOwn(fields, name) ? fields[name] : undefined);\n\n// The calendar anchor/end fields accept either a date-only or a datetime\n// field (the latter carries the clock for the day view).\nconst isDateLike = (type: string | undefined): boolean => type === \"date\" || type === \"datetime\";\n\n// `calendarTimeField` parses a free-form time string, so it must name a\n// string-backed field — a number/enum/date column has no time-range text.\nconst isTimeStringField = (type: string | undefined): boolean => type === \"string\" || type === \"text\";\n\n// Field types that can hold a currency code string. A `currencyField`\n// pointer must resolve to one of these — pointing at a number / boolean\n// / table would never yield a usable ISO code.\nconst CODE_FIELD_TYPES = new Set([\"string\", \"text\", \"enum\"]);\n\nconst namesStoredField = (fields: Fields, name: string, primaryKey: string): boolean => {\n  const target = declaredField(fields, name);\n  return target !== undefined && !COMPUTED_TYPES.has(target.type) && name !== primaryKey;\n};\n\nconst hasUniqueIds = (entries: { id: string }[] | undefined): boolean =>\n  entries === undefined || new Set(entries.map((entry) => entry.id)).size === entries.length;\n\n// ---------------------------------------------------------------------------\n// Storage declaration\n// ---------------------------------------------------------------------------\n\n/** Exactly one storage declaration: native records need `dataPath`, an external\n *  data file needs `dataSource`, an alternative backend needs `storage`. Zero\n *  (nowhere to read) and several (ambiguous which wins) are equally\n *  meaningless — fail loudly at load instead of picking silently. */\nexport function declaresExactlyOneStore(schema: Schema): boolean {\n  return [schema.dataPath, schema.dataSource, schema.storage].filter((declared) => declared !== undefined).length === 1;\n}\n\n// NOTE: `storage` collections support the full write machinery (`spawn` /\n// `completionField` / `triggerField` / `singleton` / `ingest` / mutate actions)\n// — spawn and the watcher reconcilers go through the CollectionStore seam, and\n// a db-file watcher drives their reconciliation (collection-watchers/watcher.ts).\n/** A `dataSource` collection is read-only by definition, so schema-level write\n *  machinery can never fire: `singleton` pins CREATES, `ingest` REFILLS\n *  records, `spawn` WRITES successor records. Rejecting them at validation\n *  kills whole classes of writes before any runtime guard. */\nexport function dataSourceDeclaresNoWriteMachinery(schema: Schema): boolean {\n  if (schema.dataSource === undefined) return true;\n  return schema.singleton === undefined && schema.ingest === undefined && schema.spawn === undefined && schema.googleCalendar === undefined;\n}\n\n/** Same rule for declarative host writes: a mutate action writes the record\n *  it's invoked on, which a read-only collection has no business doing. */\nexport function dataSourceDeclaresNoMutateAction(schema: Schema): boolean {\n  if (schema.dataSource !== undefined) {\n    return [...(schema.actions ?? []), ...(schema.collectionActions ?? [])].every((action) => action.kind !== \"mutate\");\n  }\n  return true;\n}\n\n// ---------------------------------------------------------------------------\n// Actions\n// ---------------------------------------------------------------------------\n\n/** Action ids must be unique so the dispatch route resolves unambiguously. */\nexport function actionIdsAreUnique(schema: Schema): boolean {\n  return hasUniqueIds(schema.actions);\n}\n\n/** Collection-level action ids must likewise be unique. */\nexport function collectionActionIdsAreUnique(schema: Schema): boolean {\n  return hasUniqueIds(schema.collectionActions);\n}\n\n/** A mutate action's `set` writes real STORED fields: a typo'd key would write\n *  a stray value forever, a computed/projected field is never persisted, and\n *  the primaryKey is the filename (renaming is not a mutation). */\nexport function mutateSetKeysNameStoredFields(schema: Schema): boolean {\n  return (schema.actions ?? []).every(\n    (action) => action.kind !== \"mutate\" || Object.keys(action.set).every((key) => namesStoredField(schema.fields, key, schema.primaryKey)),\n  );\n}\n\n/** Every `$params.<name>` reference in `set` must name a declared param — an\n *  undeclared one would silently no-op the assignment. */\nexport function mutateParamRefsAreDeclared(schema: Schema): boolean {\n  return (schema.actions ?? []).every(\n    (action) =>\n      action.kind !== \"mutate\" ||\n      Object.values(action.set).every((value) => {\n        const ref = paramRefName(value);\n        return ref === null || (action.params ?? {})[ref] !== undefined;\n      }),\n  );\n}\n\n/** A collection-level action has no record to write. */\nexport function collectionActionsAreNotMutate(schema: Schema): boolean {\n  return (schema.collectionActions ?? []).every((action) => action.kind !== \"mutate\");\n}\n\n// ---------------------------------------------------------------------------\n// Singleton, currency\n// ---------------------------------------------------------------------------\n\n/** The singleton value becomes a record id (and thus a `<id>.json` filename),\n *  so it must satisfy the SAME record-id rule the write path enforces —\n *  otherwise the create form would lock the primary key to a value the POST\n *  route then rejects, making the collection impossible to initialize. */\nexport function singletonIsAValidRecordId(schema: Schema): boolean {\n  return schema.singleton === undefined || isSafeRecordId(schema.singleton);\n}\n\n// `type` is carried on both levels even though this helper never reads it:\n// without a required property these become weak types, and a field variant that\n// declares no `currencyField` would no longer be assignable.\ninterface CurrencyBearingField {\n  type: string;\n  currencyField?: string | undefined;\n  of?: Record<string, { type: string; currencyField?: string | undefined }> | undefined;\n}\n\n// Every `currencyField` declared anywhere in the schema — top-level fields and\n// a table's `of` sub-fields. Sub-field money cells resolve currency against the\n// TOP-LEVEL record (rows carry no currency), so their pointers are validated\n// against the top-level field set too.\nfunction collectCurrencyFieldRefs(fields: Record<string, CurrencyBearingField>): string[] {\n  const refs: string[] = [];\n  for (const field of Object.values(fields)) {\n    if (typeof field.currencyField === \"string\" && field.currencyField.length > 0) refs.push(field.currencyField);\n    for (const sub of Object.values(field.of ?? {})) {\n      if (typeof sub.currencyField === \"string\" && sub.currencyField.length > 0) refs.push(sub.currencyField);\n    }\n  }\n  return refs;\n}\n\n/** A `currencyField` pointer must name a real top-level field that holds a code\n *  string — a typo (`curreny`) would otherwise pass the per-field check, then\n *  silently fall back to the literal / USD at render and mislabel amounts. */\nexport function currencyFieldRefsNameCodeFields(schema: Schema): boolean {\n  return collectCurrencyFieldRefs(schema.fields).every((name) => CODE_FIELD_TYPES.has(declaredField(schema.fields, name)?.type ?? \"\"));\n}\n\n// ---------------------------------------------------------------------------\n// Completion tracking\n// ---------------------------------------------------------------------------\n\n/** The pair must be declared together — one without the other is meaningless:\n *  the host would either never fire (no done values to compare against) or\n *  never clear (no field to read).\n *\n *  EXCEPTION: when `completionField` names a `flag` field, done ⇔ the flag's\n *  `where` matches, so `completionDoneValues` carries no information and MUST\n *  be omitted (declaring it would invite a contradictory second source of\n *  truth). */\nexport function completionPairIsCoherent(schema: Schema): boolean {\n  if (schema.completionField !== undefined && declaredField(schema.fields, schema.completionField)?.type === \"flag\") {\n    return schema.completionDoneValues === undefined;\n  }\n  return (schema.completionField === undefined) === (schema.completionDoneValues === undefined);\n}\n\n/** `completionField` must name a real top-level field — a typo would silently\n *  disable the notification mechanism otherwise. */\nexport function completionFieldIsDeclared(schema: Schema): boolean {\n  return schema.completionField === undefined || declaredField(schema.fields, schema.completionField) !== undefined;\n}\n\n/** A flag named by `completionField` is evaluated against the RAW record — the\n *  reconciler (and spawn's fallback) read items straight off disk, BEFORE any\n *  `deriveAll` enrichment — so its `where` may only reference STORED fields. A\n *  condition over a computed sibling would see an absent key: `ne` matches\n *  vacuously, every other op reads false, and the bell would clear wrongly /\n *  never. General (non-completion) flags keep the full vocabulary — the UI\n *  evaluates them post-enrichment. */\nexport function completionFlagReadsOnlyStoredFields(schema: Schema): boolean {\n  const spec = schema.completionField === undefined ? undefined : declaredField(schema.fields, schema.completionField);\n  if (spec?.type !== \"flag\") return true;\n  return spec.where.every((cond) =>\n    [cond.field, ...(cond.valueFrom ? [cond.valueFrom.field] : [])].every((name) => {\n      const target = declaredField(schema.fields, name);\n      return target !== undefined && !COMPUTED_TYPES.has(target.type);\n    }),\n  );\n}\n\n// ---------------------------------------------------------------------------\n// Field pointers\n// ---------------------------------------------------------------------------\n\n/** `displayField`, like `completionField`, must name a real top-level field —\n *  a typo would silently fall back to the primaryKey forever. */\nexport function displayFieldIsDeclared(schema: Schema): boolean {\n  return schema.displayField === undefined || declaredField(schema.fields, schema.displayField) !== undefined;\n}\n\n/** A field's `when.field` gates its visibility against a sibling's value, so it\n *  must name a real top-level field — a typo would silently keep the field\n *  hidden forever (the gate never matches). */\nexport function fieldVisibilityGatesNameDeclaredFields(schema: Schema): boolean {\n  return Object.values(schema.fields).every((field) => field.when === undefined || declaredField(schema.fields, field.when.field) !== undefined);\n}\n\n/** A flag's `where` reads sibling fields (both `cond.field` and a same-record\n *  `valueFrom.field`), so each must name a real top-level field — a typo would\n *  silently pin the flag false forever (`ne`: true forever). */\nexport function flagConditionsNameDeclaredFields(schema: Schema): boolean {\n  return Object.values(schema.fields).every(\n    (field) =>\n      field.type !== \"flag\" ||\n      field.where.every(\n        (cond) =>\n          declaredField(schema.fields, cond.field) !== undefined &&\n          (cond.valueFrom === undefined || declaredField(schema.fields, cond.valueFrom.field) !== undefined),\n      ),\n  );\n}\n\n/** An `embed`'s `idField` resolves the target record id from a sibling's value,\n *  so it must name a real top-level field — and one whose stored value is a\n *  plain id string. Only `ref` / `string` qualify: the editor writes the picked\n *  id into that field, so a non-persisted or composite type would either not\n *  round-trip on save or hold no usable id. */\nexport function embedIdFieldsNameIdBearingFields(schema: Schema): boolean {\n  return Object.values(schema.fields).every((field) => {\n    if (field.type !== \"embed\" || field.idField === undefined) return true;\n    const target = declaredField(schema.fields, field.idField);\n    return target !== undefined && (target.type === \"ref\" || target.type === \"string\");\n  });\n}\n\n/** The sync writes each mapped value into a declared field, and puts the Google\n *  event id in the primary field — so a map key that names no field (or names\n *  the primary) would silently drop data or fight the id. */\nexport function googleCalendarMapNamesStoredFields(schema: Schema): boolean {\n  if (schema.googleCalendar === undefined) return true;\n  return Object.keys(schema.googleCalendar.map).every((key) => namesStoredField(schema.fields, key, schema.primaryKey));\n}\n\n// ---------------------------------------------------------------------------\n// Toggle projection\n// ---------------------------------------------------------------------------\n\n/** A `toggle` field projects an `enum` field: its `field` must name a real\n *  top-level enum, and `onValue` / `offValue` must be members of that enum's\n *  `values` — otherwise toggling would write a value outside the closed set\n *  (and never appear \"checked\"). */\nexport function togglesProjectValidEnums(schema: Schema): boolean {\n  const { fields } = schema;\n  for (const spec of Object.values(fields)) {\n    if (spec.type !== \"toggle\") continue;\n    const target = declaredField(fields, spec.field);\n    if (!target || target.type !== \"enum\") return false;\n    const allowed = new Set(target.values);\n    if (!allowed.has(spec.onValue) || !allowed.has(spec.offValue)) return false;\n  }\n  return true;\n}\n\n// ---------------------------------------------------------------------------\n// Trigger\n// ---------------------------------------------------------------------------\n\n/** `triggerField` requires the completion pair: the time gate only suppresses\n *  the *completion* bell until the date, and the bell still clears via\n *  `completionDoneValues`. Without completion there is no bell to gate. */\nexport function triggerFieldRequiresCompletion(schema: Schema): boolean {\n  return schema.triggerField === undefined || schema.completionField !== undefined;\n}\n\n/** `triggerField` must name a real `date` field — the gate parses its value as\n *  `YYYY-MM-DD`; any other type can't be compared to the clock. */\nexport function triggerFieldIsADateField(schema: Schema): boolean {\n  return schema.triggerField === undefined || declaredField(schema.fields, schema.triggerField)?.type === \"date\";\n}\n\n/** `triggerLeadDays` only means something relative to a trigger date. */\nexport function triggerLeadDaysRequiresTriggerField(schema: Schema): boolean {\n  return schema.triggerLeadDays === undefined || schema.triggerField !== undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Spawn\n// ---------------------------------------------------------------------------\n\n/** `spawn` advances `triggerField` to compute the successor's trigger date, so\n *  the schema must declare one. */\nexport function spawnRequiresTriggerField(schema: Schema): boolean {\n  return schema.spawn === undefined || schema.triggerField !== undefined;\n}\n\n/** `spawn.when.field` must name a real top-level field — a typo would silently\n *  never match. */\nexport function spawnWhenFieldIsDeclared(schema: Schema): boolean {\n  return schema.spawn?.when === undefined || declaredField(schema.fields, schema.spawn.when.field) !== undefined;\n}\n\n/** Every `spawn.carry` entry must name a real top-level field — a typo would\n *  silently never copy. */\nexport function spawnCarryEntriesAreDeclared(schema: Schema): boolean {\n  return (schema.spawn?.carry ?? []).every((name) => declaredField(schema.fields, name) !== undefined);\n}\n\n/** A successor must NOT be born already matching its own spawn predicate — it\n *  would re-spawn on its first reconcile, fanning out into an unbounded chain\n *  of records. The predicate field/values are `spawn.when` when given, else the\n *  completion-done pair. The successor's value for that field is `set[field]`\n *  if set, else the carried source value (which matched, by definition, when\n *  the spawn fired) if carried, else absent (safe). */\nexport function spawnSuccessorStartsInert(schema: Schema): boolean {\n  const { spawn } = schema;\n  if (!spawn) return true;\n  const field = spawn.when?.field ?? schema.completionField;\n  const values = spawn.when?.in ?? schema.completionDoneValues;\n  if (!field || !values) return true; // predicate not evaluable — other rules cover this\n  if (spawn.set && Object.prototype.hasOwnProperty.call(spawn.set, field)) {\n    return !values.includes(String(spawn.set[field])); // `set` wins over `carry`\n  }\n  return !(spawn.carry ?? []).includes(field); // carried ⇒ inherits the matching value\n}\n\n/** `spawnSuccessorStartsInert` cannot see through a flag's `where` (the\n *  predicate would need full record evaluation against `set`/`carry`). So a\n *  schema whose completion is flag-form may only spawn with an explicit\n *  `spawn.when` — which that check CAN evaluate. */\nexport function flagCompletionSpawnDeclaresWhen(schema: Schema): boolean {\n  return schema.spawn === undefined || schema.spawn.when !== undefined || declaredField(schema.fields, schema.completionField ?? \"\")?.type !== \"flag\";\n}\n\n// Resolve the field-driven arm of `spawn.every`, or null when spawn is absent\n// or its `every` is the literal arm. Lets each rule below short-circuit\n// (return valid) without re-checking the discriminant.\nfunction fieldDrivenSpawnEvery(schema: Schema) {\n  const every = schema.spawn?.every;\n  if (!every || !(\"fromField\" in every)) return null;\n  return every;\n}\n\n/** §4.1 — `fromField` must name a real top-level `enum` field. The `map` keys\n *  are only meaningful against a closed value set, and the field renders as a\n *  form `<select>`; a non-enum target has no finite values to validate. */\nexport function fieldDrivenFromFieldIsEnum(schema: Schema): boolean {\n  const driven = fieldDrivenSpawnEvery(schema);\n  if (!driven) return true;\n  return declaredField(schema.fields, driven.fromField)?.type === \"enum\";\n}\n\n/** §4.2 — `map` keys must EXACTLY cover the enum's `values` (no missing keys —\n *  a record could pick an unmapped frequency and silently stall; no extra keys\n *  — a stale map outliving an enum edit). */\nexport function fieldDrivenMapCoversValues(schema: Schema): boolean {\n  const driven = fieldDrivenSpawnEvery(schema);\n  if (!driven) return true;\n  const target = declaredField(schema.fields, driven.fromField);\n  if (target?.type !== \"enum\") return true; // §4.1 reports the type error\n  const values = new Set<string>(target.values);\n  const keys = Object.keys(driven.map);\n  return keys.length === values.size && keys.every((key) => values.has(key));\n}\n\n/** §4.5 — `fromField` must reach the successor (via `carry` or `set`);\n *  otherwise the successor loses its frequency and the NEXT spawn along the\n *  chain can't resolve an interval, silently halting the recurrence.\n *\n *  `set` writes a FIXED value, so it must itself be a key of `map` (else the\n *  successor is born with an unresolvable driver and `resolveEvery` skips it —\n *  the exact silent-halt §4.5 exists to prevent). `carry` copies the source's\n *  own value, which — for a record that matched the spawn — is one of the\n *  enum's values, all of which `map` covers by §4.2; so a carried driver is\n *  always resolvable and needs no value check here. */\nexport function fieldDrivenFromFieldCarried(schema: Schema): boolean {\n  const driven = fieldDrivenSpawnEvery(schema);\n  if (!driven) return true;\n  const { carry, set } = schema.spawn ?? {};\n  if (set && Object.prototype.hasOwnProperty.call(set, driven.fromField)) {\n    const raw = set[driven.fromField];\n    if (raw === undefined || raw === null || raw === \"\") return false;\n    const key = fieldTextOrNull(raw);\n    return key !== null && Object.prototype.hasOwnProperty.call(driven.map, key);\n  }\n  return (carry ?? []).includes(driven.fromField);\n}\n\n// ---------------------------------------------------------------------------\n// Calendar\n// ---------------------------------------------------------------------------\n\n/** `calendarField` must name a real `date`/`datetime` field — the calendar view\n *  parses its value to place records on the month grid (a `datetime` anchor\n *  also carries the clock for the day view). */\nexport function calendarFieldIsDateLike(schema: Schema): boolean {\n  return schema.calendarField === undefined || isDateLike(declaredField(schema.fields, schema.calendarField)?.type);\n}\n\n/** `calendarEndField` marks the end of a multi-day span, so it only means\n *  something alongside a start anchor. */\nexport function calendarEndFieldRequiresCalendarField(schema: Schema): boolean {\n  return schema.calendarEndField === undefined || schema.calendarField !== undefined;\n}\n\n/** `calendarEndField` must also name a real `date`/`datetime` field — same parse. */\nexport function calendarEndFieldIsDateLike(schema: Schema): boolean {\n  return schema.calendarEndField === undefined || isDateLike(declaredField(schema.fields, schema.calendarEndField)?.type);\n}\n\n/** `calendarTimeField` places records on the day view, so it only means\n *  something alongside a start anchor. */\nexport function calendarTimeFieldRequiresCalendarField(schema: Schema): boolean {\n  return schema.calendarTimeField === undefined || schema.calendarField !== undefined;\n}\n\n/** `calendarTimeField` must name a real top-level field (a free-form time\n *  string the day view parses). */\nexport function calendarTimeFieldIsDeclared(schema: Schema): boolean {\n  return schema.calendarTimeField === undefined || declaredField(schema.fields, schema.calendarTimeField) !== undefined;\n}\n\n/** …and that field must be string-backed — the day view parses its value as a\n *  time string, so a number/enum/date column can't drive it. */\nexport function calendarTimeFieldIsStringBacked(schema: Schema): boolean {\n  return schema.calendarTimeField === undefined || isTimeStringField(declaredField(schema.fields, schema.calendarTimeField)?.type);\n}\n\n/** `kanbanField` must name a real `enum` field — the board groups records into\n *  one column per declared enum value; any other type has no closed set of\n *  columns to group by. */\nexport function kanbanFieldIsAnEnum(schema: Schema): boolean {\n  return schema.kanbanField === undefined || declaredField(schema.fields, schema.kanbanField)?.type === \"enum\";\n}\n\n// ---------------------------------------------------------------------------\n// notifyWhen, custom views\n// ---------------------------------------------------------------------------\n\n/** `notifyWhen` narrows the completion bell, so it only means something with\n *  completion tracking. */\nexport function notifyWhenRequiresCompletion(schema: Schema): boolean {\n  return schema.notifyWhen === undefined || schema.completionField !== undefined;\n}\n\n/** `notifyWhen.field` must name a real top-level field. */\nexport function notifyWhenFieldIsDeclared(schema: Schema): boolean {\n  return schema.notifyWhen === undefined || declaredField(schema.fields, schema.notifyWhen.field) !== undefined;\n}\n\n/** Every custom view `id` must be a valid slug — it doubles as the view-mode\n *  selector key (`custom:<id>`) and the capability-token clamp key, both of\n *  which expect a path-safe token. */\nexport function viewIdsAreSlugs(schema: Schema): boolean {\n  return schema.views === undefined || schema.views.every((view) => isSafeSlug(view.id));\n}\n\n/** Custom view ids must be unique so the selector + token clamp resolve\n *  unambiguously. */\nexport function viewIdsAreUnique(schema: Schema): boolean {\n  return hasUniqueIds(schema.views);\n}\n","// The zod SINGLE SOURCE OF TRUTH for the collection schema contract.\n//\n// Every TypeScript type in `./schema` is derived from these definitions via\n// `z.infer` (type-only imports, erased at emit) — there is no hand-written\n// mirror to drift. Field specs are a DISCRIMINATED UNION on `type` (and\n// `ingest` on `kind`): each variant declares exactly the keys it owns, so an\n// unknown key is stripped per-variant, a new field type is a new union\n// member (not another optional key + refine on a flat bag), and error\n// messages name the variant that failed.\n//\n// This module is ISOMORPHIC — zod plus the pure predicates in `./ids` /\n// `./templatePath`, no node built-ins — but it is deliberately NOT exported\n// through the browser barrel (`../index`): browser code imports the derived\n// TYPES from `./schema` (type-only ⇒ no zod in the bundle) and the server\n// imports the validators through `../server` (discovery re-exports\n// `CollectionSchemaZ`). Runtime imports here point only at `./schema`'s\n// consts, so the module graph stays acyclic: schemaZ → schema.\n\nimport { isRecord, isUnknownArray } from \"@mulmoclaude/common\";\nimport { z } from \"zod\";\nimport { isSafeSlug } from \"./ids\";\nimport { isSafeActionTemplatePath, isSafeCustomViewI18nPath, isSafeCustomViewPath } from \"./templatePath\";\nimport { INGEST_KINDS, AGENT_INGEST_KIND, FEED_SCHEDULES } from \"./schema\";\nimport {\n  actionIdsAreUnique,\n  calendarEndFieldIsDateLike,\n  calendarEndFieldRequiresCalendarField,\n  calendarFieldIsDateLike,\n  calendarTimeFieldIsDeclared,\n  calendarTimeFieldIsStringBacked,\n  calendarTimeFieldRequiresCalendarField,\n  collectionActionIdsAreUnique,\n  collectionActionsAreNotMutate,\n  completionFieldIsDeclared,\n  completionFlagReadsOnlyStoredFields,\n  completionPairIsCoherent,\n  currencyFieldRefsNameCodeFields,\n  dataSourceDeclaresNoMutateAction,\n  dataSourceDeclaresNoWriteMachinery,\n  declaresExactlyOneStore,\n  displayFieldIsDeclared,\n  embedIdFieldsNameIdBearingFields,\n  fieldDrivenFromFieldCarried,\n  fieldDrivenFromFieldIsEnum,\n  fieldDrivenMapCoversValues,\n  fieldVisibilityGatesNameDeclaredFields,\n  flagCompletionSpawnDeclaresWhen,\n  flagConditionsNameDeclaredFields,\n  googleCalendarMapNamesStoredFields,\n  kanbanFieldIsAnEnum,\n  mutateParamRefsAreDeclared,\n  mutateSetKeysNameStoredFields,\n  notifyWhenFieldIsDeclared,\n  notifyWhenRequiresCompletion,\n  singletonIsAValidRecordId,\n  spawnCarryEntriesAreDeclared,\n  spawnRequiresTriggerField,\n  spawnSuccessorStartsInert,\n  spawnWhenFieldIsDeclared,\n  togglesProjectValidEnums,\n  triggerFieldIsADateField,\n  triggerFieldRequiresCompletion,\n  triggerLeadDaysRequiresTriggerField,\n  viewIdsAreSlugs,\n  viewIdsAreUnique,\n} from \"./schemaRules\";\n\n// ---------------------------------------------------------------------------\n// Shared predicate shapes\n// ---------------------------------------------------------------------------\n\n/** Optional visibility predicate shared by actions and fields: the target\n *  shows only when the open record's `field` (stringified) is one of `in`.\n *  Domain-free — `field` is any non-empty key, `in` a non-empty array of\n *  non-empty values; the host never interprets the meaning.\n *\n *  `trim().min(1)` rather than bare `min(1)` so a whitespace-only string\n *  (\"   \") fails validation — otherwise the cell formatter / dropdown would\n *  render visual blanks that look like missing data. Applied consistently to\n *  every \"non-empty string\" slot in this file (CodeRabbit PR #1497). */\nexport const WhenZ = z.object({\n  field: z.string().trim().min(1),\n  in: z.array(z.string().trim().min(1)).min(1),\n});\n\n// `where` — a richer AND-of-conditions predicate than the single-field\n// `WhenZ` above: typed comparison ops (`eq/ne/in/contains/gt/gte/lt/lte`)\n// evaluated by `./where`'s `matchesWhere`. Consumed by `flag` fields\n// (below) and `dynamicIcon` (further down). Defined here, ahead of the\n// field specs, so `FlagFieldZ` can reference it.\n//\n// A condition's comparison value is either a literal `value` or a\n// `valueFrom` reference to another record's field (e.g. a `_config`\n// singleton's `defaultCity`, resolved at compute time against the source\n// collection's own records — see the server's `dynamicIcon.ts`\n// `recordsById`). Exactly one of the two is required: neither (nothing to\n// compare against) and both (ambiguous which wins) are equally meaningless.\n// `record` omitted → the SAME record being matched (field-to-field compare,\n// e.g. `spent > budget`); set → another record by primaryKey (e.g. `_config`).\nexport const ValueRefZ = z.object({\n  record: z.string().trim().min(1).optional(),\n  field: z.string().trim().min(1),\n});\nexport const WhereCondZ = z\n  .object({\n    field: z.string().trim().min(1),\n    op: z.enum([\"eq\", \"ne\", \"in\", \"gt\", \"gte\", \"lt\", \"lte\", \"contains\"]),\n    value: z.union([z.string(), z.array(z.string())]).optional(),\n    valueFrom: ValueRefZ.optional(),\n  })\n  .refine((cond) => (cond.value !== undefined) !== (cond.valueFrom !== undefined), {\n    message: \"a where condition must declare exactly one of `value` (a literal) or `valueFrom` (a reference to another record's field), never both or neither\",\n    path: [\"value\"],\n  })\n  .refine((cond) => cond.value === undefined || (cond.op === \"in\") === Array.isArray(cond.value), {\n    message: \"`in` requires an array `value` (the allowed set); every other op requires a single string `value`\",\n    path: [\"value\"],\n  });\nexport const WhereZ = z.array(WhereCondZ);\n\n// ---------------------------------------------------------------------------\n// Field specs — a discriminated union on `type`\n// ---------------------------------------------------------------------------\n\n// Keys every field variant carries. `when` gates visibility (list cell,\n// edit form, detail view — purely presentational, a hidden field's stored\n// value is never cleared; only honoured on top-level fields). `primary`\n// marks the field whose value is the record's filename (exactly one per\n// schema — enforced by `acceptParsedSchema`, not here). The referenced\n// `when.field` is validated to be a real top-level field by a schema-level\n// refine below (a field can't see its siblings here).\nconst fieldBase = {\n  label: z.string().min(1),\n  primary: z.boolean().optional(),\n  required: z.boolean().optional(),\n  when: WhenZ.optional(),\n};\n\n// A field that renders as money must declare where its currency comes from —\n// otherwise the formatter silently falls back to USD and mislabels non-USD\n// amounts. Two ways to satisfy it: a literal `currency` (an ISO 4217 code,\n// fixed for every record) or a `currencyField` naming a sibling record field\n// that holds the code (per-record, e.g. an invoice's `currency` enum; resolved\n// against the TOP-LEVEL record even for money sub-fields inside a table). At\n// least one is required. The stored value is always a plain decimal number;\n// currency is presentation only.\nconst currencyKeys = {\n  currency: z.string().trim().min(1).optional(),\n  currencyField: z.string().trim().min(1).optional(),\n};\nconst hasCurrencySource = (spec: { currency?: string | undefined; currencyField?: string | undefined }): boolean =>\n  spec.currency !== undefined || spec.currencyField !== undefined;\nconst currencyMessage = {\n  message:\n    \"fields that render as money (type 'money', or 'derived' with display 'money') must declare either a literal `currency` (ISO 4217 code, e.g. 'USD', 'JPY') or a `currencyField` naming the record field that holds the code\",\n  path: [\"currency\"],\n};\n\nconst slugMessage = (key: string) => ({\n  message: `\\`${key}\\` must be a valid collection slug (alphanumeric / hyphen / underscore, no path separators)`,\n  path: [key],\n});\n\n/** The plain scalar field types. Stored and edited as primitive values; no\n *  variant-specific keys.\n *  - `image`: a workspace-relative image path (e.g. a `data/attachments/...`\n *    upload); rendered as an <img> in the detail view (not the list table —\n *    a per-row fetch is too expensive at scale). Stored as a plain string.\n *  - `file`: a workspace-relative file path as a plain string (e.g. an\n *    `artifacts/html/<name>.html` app). Rendered as a clickable link in both\n *    the list table and the detail view: HTML / SVG artifacts open their\n *    rendered form in a new tab; any other path opens in the File Explorer. */\nconst ScalarFieldZ = z.object({\n  type: z.enum([\"string\", \"text\", \"email\", \"number\", \"date\", \"datetime\", \"boolean\", \"markdown\", \"image\", \"file\"]),\n  ...fieldBase,\n});\n\n/** A link to another collection: the record stores the target item's\n *  primary-key slug and the host renders a clickable link + dropdown picker.\n *  `to` must be a real slug (not `../foo`, not `mc-clients/extra` — see\n *  Codex P2 on PR #1495); whether the target collection exists resolves\n *  fail-soft at render time, never here. */\nconst RefFieldZ = z\n  .object({\n    type: z.literal(\"ref\"),\n    ...fieldBase,\n    to: z.string().min(1),\n  })\n  .refine((spec) => isSafeSlug(spec.to), slugMessage(\"to\"));\n\n/** A money amount. See `currencyKeys` for the currency-source contract. */\nconst MoneyFieldZ = z\n  .object({\n    type: z.literal(\"money\"),\n    ...fieldBase,\n    ...currencyKeys,\n  })\n  .refine(hasCurrencySource, currencyMessage);\n\n/** A closed set of allowed string values. The form renders a `<select>`\n *  populated from `values`; storage is a plain string. */\nconst EnumFieldZ = z.object({\n  type: z.literal(\"enum\"),\n  ...fieldBase,\n  values: z.array(z.string().trim().min(1)).min(1),\n});\n\n// Sub-fields inside a `table.of` map: the regular field types minus `table`\n// (no nested tables) and `derived` (no computed columns inside a table —\n// would need the evaluator to walk the row context, defer until a real need\n// surfaces). Also no `when` / `primary` — rows have neither visibility\n// gating nor filenames.\nconst subFieldBase = {\n  label: z.string().min(1),\n  required: z.boolean().optional(),\n};\nconst SubScalarFieldZ = z.object({\n  type: z.enum([\"string\", \"text\", \"email\", \"number\", \"date\", \"datetime\", \"boolean\", \"markdown\"]),\n  ...subFieldBase,\n});\nconst SubRefFieldZ = z.object({ type: z.literal(\"ref\"), ...subFieldBase, to: z.string().min(1) }).refine((spec) => isSafeSlug(spec.to), slugMessage(\"to\"));\nconst SubMoneyFieldZ = z.object({ type: z.literal(\"money\"), ...subFieldBase, ...currencyKeys }).refine(hasCurrencySource, currencyMessage);\nconst SubEnumFieldZ = z.object({ type: z.literal(\"enum\"), ...subFieldBase, values: z.array(z.string().trim().min(1)).min(1) });\n\nexport const SubFieldSpecZ = z.discriminatedUnion(\"type\", [SubScalarFieldZ, SubRefFieldZ, SubMoneyFieldZ, SubEnumFieldZ]);\n\n/** A flat sub-table: each row is a record of `of`'s sub-schema (insertion\n *  order = column order). v0 disallows nested tables and derived columns to\n *  keep the editor + evaluator simple. */\nconst TableFieldZ = z\n  .object({\n    type: z.literal(\"table\"),\n    ...fieldBase,\n    of: z.record(z.string(), SubFieldSpecZ),\n  })\n  .refine((spec) => Object.keys(spec.of).length > 0, {\n    message: \"fields with type 'table' must declare a non-empty `of` (sub-schema for each row)\",\n    path: [\"of\"],\n  });\n\n/** A computed scalar: `formula` is a tiny expression evaluated against the\n *  record — `+ - * /`, parens, identifier refs to top-level fields,\n *  `sum(tableField[].col)`, and `sum(tableField[].col * tableField[].col)`\n *  (see `./derivedFormula`). `display` picks the inner type the value renders\n *  as (default `\"number\"`) — restricted to the non-composite display targets,\n *  since a derived value is a scalar. Never stored; computed by `deriveAll`\n *  on both server and client. */\nconst DerivedFieldZ = z\n  .object({\n    type: z.literal(\"derived\"),\n    ...fieldBase,\n    formula: z.string().trim().min(1),\n    display: z.enum([\"string\", \"number\", \"money\", \"date\"]).optional(),\n    ...currencyKeys,\n  })\n  .refine((spec) => spec.display !== \"money\" || hasCurrencySource(spec), currencyMessage);\n\n/** Pulls a record from another collection into the read-only detail view.\n *  Display-only — nothing is stored on this record, so it never appears in\n *  the list table or the edit form. Must declare a valid `to` slug (same\n *  path-traversal guard as `ref`) AND exactly one of `id` (a fixed target\n *  record, e.g. `me` for the singleton profile — same for every record) or\n *  `idField` (a sibling top-level field naming the per-record target, e.g.\n *  an invoice's `issuerId` selecting which profile to embed as the bill-from\n *  block; an absent/empty value resolves fail-soft to \"no record\"). The\n *  `idField` target is validated to be a real `ref`/`string` field by a\n *  schema-level refine below. */\nconst EmbedFieldZ = z\n  .object({\n    type: z.literal(\"embed\"),\n    ...fieldBase,\n    to: z.string().min(1),\n    id: z.string().trim().min(1).optional(),\n    idField: z.string().trim().min(1).optional(),\n  })\n  .refine((spec) => isSafeSlug(spec.to) && (spec.id !== undefined) !== (spec.idField !== undefined), {\n    message:\n      \"fields with type 'embed' must declare a `to` (valid collection slug) and exactly one of `id` (a fixed record's primary key) or `idField` (a sibling field naming the per-record target)\",\n    path: [\"id\"],\n  });\n\n/** Display-only REVERSE refs (plan step ② of plans/done/collection-ontology.md):\n *  a read-only sub-table of the records in collection `from` whose `via`\n *  ref field stores THIS record's primary key. Stores nothing (joins\n *  `COMPUTED_TYPES`); resolution is shared server/client via\n *  `core/backlinks.ts`. `display` names the `from` columns to show;\n *  `filter` (the standard `when` shape, matched against each SOURCE\n *  record) narrows the rows. Validation is shape-only, like `embed`:\n *  `from` must be a safe slug, but whether it exists — and whether `via` /\n *  `display` name real fields there — resolves fail-soft at render\n *  (empty sub-table). Do NOT add cross-schema existence checks here. */\nconst BacklinksFieldZ = z\n  .object({\n    type: z.literal(\"backlinks\"),\n    ...fieldBase,\n    from: z.string().min(1),\n    via: z.string().trim().min(1),\n    display: z.array(z.string().trim().min(1)).min(1),\n    filter: WhenZ.optional(),\n  })\n  .refine((spec) => isSafeSlug(spec.from), slugMessage(\"from\"));\n\n/** A cross-collection AGGREGATE over a backlink relation (plan step ⑤ of\n *  plans/done/collection-ontology.md): a computed number — never stored — that\n *  sums a source column (or counts rows) over the records in `from` whose\n *  `via` ref points at this record. Same `from`/`via`/`filter` vocabulary\n *  and reverse-loading machinery as `backlinks`; resolution shared\n *  server/client via `core/backlinks.ts`'s `rollupValue`. Deliberately a\n *  STRUCTURED field, not `sumOver(...)` formula syntax — the derived\n *  evaluator's no-string-literals boundary stays untouched — and\n *  deliberately just `sum` | `count`. Rollups resolve BEFORE the formula\n *  pass, so a sibling `derived` formula may read them as identifiers\n *  (`played = homePlayed + awayPlayed`). Fail-soft: an unresolvable\n *  `from` renders em-dash; an empty match set is a real 0. */\nconst RollupFieldZ = z\n  .object({\n    type: z.literal(\"rollup\"),\n    ...fieldBase,\n    from: z.string().min(1),\n    via: z.string().trim().min(1),\n    op: z.enum([\"sum\", \"count\"]),\n    column: z.string().trim().min(1).optional(),\n    filter: WhenZ.optional(),\n  })\n  .refine((spec) => isSafeSlug(spec.from), slugMessage(\"from\"))\n  .refine((spec) => (spec.op === \"sum\") === (spec.column !== undefined), {\n    message: 'a rollup\\'s `column` names the source column to aggregate: required for op \"sum\", meaningless for op \"count\"',\n    path: [\"column\"],\n  });\n\n/** A checkbox that is a pure PROJECTION of an `enum` field — it stores\n *  nothing of its own. Checked when the enum named by `field` equals\n *  `onValue`; toggling writes `onValue` / `offValue` back to that enum\n *  field. Lets a \"done\" checkbox front a kanban `status` field with the enum\n *  as the single source of truth (no separate stored boolean to keep in\n *  sync). `field` / `onValue` / `offValue` are validated against the target\n *  enum's `values` by a schema-level refine below. */\nconst ToggleFieldZ = z.object({\n  type: z.literal(\"toggle\"),\n  ...fieldBase,\n  field: z.string().trim().min(1),\n  onValue: z.string().trim().min(1),\n  offValue: z.string().trim().min(1),\n});\n\n/** A computed boolean: the record matched against a `where` predicate\n *  (the same AND-of-conditions shape `dynamicIcon` uses; evaluated by\n *  `./where`'s `matchesWhere`). Generic state summaries — isDone,\n *  isPassed, isQualified — become declared fields, so every\n *  field-driven mechanism (table cells, detail view, completion via\n *  `completionField`) picks them up without special cases. Never\n *  stored; computed by `deriveAll` on both server and client in the\n *  same saturation loop as `derived`, so a flag may read derived /\n *  rollup values — and other flags (`matchesWhere` stringifies, so\n *  `eq \"true\"` composes). Deliberately a STRUCTURED field, not formula\n *  syntax — the derived evaluator's no-string-literals boundary stays\n *  untouched (same reasoning as `rollup`). Cross-record\n *  `valueFrom.record` is rejected here: per-record evaluation has no\n *  `recordsById`, so it could only ever silently never-match.\n *  `where` condition fields are validated to exist by a schema-level\n *  refine below (a field can't see its siblings). */\nconst FlagFieldZ = z\n  .object({\n    type: z.literal(\"flag\"),\n    ...fieldBase,\n    where: WhereZ.min(1),\n  })\n  .refine((spec) => spec.where.every((cond) => cond.valueFrom?.record === undefined), {\n    message:\n      \"a flag's `where` cannot use `valueFrom.record` (cross-record references are unresolvable in per-record evaluation); use a literal `value` or a same-record `valueFrom` (field-to-field)\",\n    path: [\"where\"],\n  });\n\nexport const FieldSpecZ = z.discriminatedUnion(\"type\", [\n  ScalarFieldZ,\n  RefFieldZ,\n  MoneyFieldZ,\n  EnumFieldZ,\n  TableFieldZ,\n  DerivedFieldZ,\n  EmbedFieldZ,\n  BacklinksFieldZ,\n  RollupFieldZ,\n  ToggleFieldZ,\n  FlagFieldZ,\n]);\n\n// ---------------------------------------------------------------------------\n// Actions & custom views\n// ---------------------------------------------------------------------------\n\n// Keys every action variant carries.\nconst actionBase = {\n  id: z.string().trim().min(1),\n  label: z.string().trim().min(1),\n  icon: z.string().trim().min(1).optional(),\n};\n\n/** The LLM-seeded action kinds — same shape, different visibility:\n *  - `\"chat\"` — start a new VISIBLE chat in `role` with the templated\n *    seed prompt (judgment work: drafting, planning, conversation).\n *  - `\"agent\"` — dispatch a HIDDEN worker (origin `system`) with the SAME\n *    seed; it edits records via manageCollection and finishes silently\n *    (mechanical enrichment: refresh a price, fetch metadata). Spinner\n *    while running, deduped failure bell on error — see\n *    server/api/routes/collectionAgentActions.ts. */\nconst SeededActionZ = z.object({\n  kind: z.enum([\"chat\", \"agent\"]),\n  ...actionBase,\n  role: z.string().trim().min(1),\n  template: z\n    .string()\n    .trim()\n    .min(1)\n    .refine(isSafeActionTemplatePath, \"must be a safe path under `templates/` (e.g. `templates/invoice.md`; no `..`, no leading `/`, no backslash)\"),\n  when: WhenZ.optional(),\n});\n\n/** `kind: \"mutate\"` — a declarative, HOST-executed write; no LLM, no\n *  tokens (plan step ④ of plans/done/collection-ontology.md). Clicking the\n *  button (after an optional `params` mini-form) merges `set` into the\n *  record: values are literals or `$params.<name>` references. `require`\n *  is the state gate — the standard `when` shape, both the visibility\n *  rule AND the server-side authorization rule, exactly like `when` on\n *  the seeded kinds. `params` reuses the table sub-field DSL, and the\n *  form is validated by the SAME compiled record checks `putItems` uses\n *  (`recordFieldProblem`), not a third mechanism. Record-level only —\n *  a collection-level mutate has no record to write (schema refine\n *  below). Merge semantics make half-states unconstructible THROUGH\n *  THIS PATH; the raw file stays editable by design (lint, not lock). */\nconst MutateActionZ = z\n  .object({\n    kind: z.literal(\"mutate\"),\n    ...actionBase,\n    require: WhenZ.optional(),\n    params: z.record(z.string().trim().min(1), SubFieldSpecZ).optional(),\n    set: z.record(z.string().trim().min(1), z.union([z.string(), z.number(), z.boolean()])),\n  })\n  .refine((spec) => Object.keys(spec.set).length > 0, {\n    message: \"a mutate action's `set` must name at least one field to write\",\n    path: [\"set\"],\n  });\n\n/** A schema-declared record action, rendered as a button in the read-only\n *  detail view. Domain-free: the host validates the shape; the meaning\n *  (role + template prose, or the declarative `set`) is data. A\n *  discriminated union on `kind` — each kind declares only its own keys. */\nexport const ActionSpecZ = z.discriminatedUnion(\"kind\", [SeededActionZ, MutateActionZ]);\n\n/** A custom (LLM-authored) HTML view registration. Domain-free: the host\n *  validates the shape; the view's behaviour lives in the HTML file. `file`\n *  is constrained to `views/*.html` (path-safe) so the view-file reader can\n *  never reach the data folder or the schema/template files. `id` is\n *  validated to be a real slug + unique by schema-level refines below. */\nexport const CustomViewZ = z.object({\n  id: z.string().trim().min(1),\n  label: z.string().trim().min(1),\n  icon: z.string().trim().min(1).optional(),\n  // Where the view runs. Absent ⇒ \"desktop\" (the sandboxed iframe over the\n  // token/dataUrl contract), so every pre-existing view keeps its behavior.\n  // \"mobile\" ⇒ served to the phone remote via getRemoteView (postMessage\n  // contract, @mulmoclaude/core/remote-view) and phone-frame-previewed on\n  // desktop. See plans/done/feat-remote-custom-view.md.\n  target: z.enum([\"desktop\", \"mobile\"]).optional(),\n  file: z\n    .string()\n    .trim()\n    .min(1)\n    .refine(isSafeCustomViewPath, \"must be a safe path under `views/` ending in `.html` (e.g. `views/year.html`; no `..`, no leading `/`, no backslash)\"),\n  // A JSON translation dictionary co-located with the view (shape mirrors\n  // vue-i18n locale messages; the host injects only the active locale's flat\n  // string map into the iframe — see `CollectionCustomView.i18n`'s docs in\n  // ./schema's source history / docs/developer.md).\n  i18n: z\n    .string()\n    .trim()\n    .min(1)\n    .refine(\n      isSafeCustomViewI18nPath,\n      \"must be a safe path under `views/` ending in `.i18n.json` (e.g. `views/year.i18n.json`; no `..`, no leading `/`, no backslash)\",\n    )\n    .optional(),\n  // What the view may do with the data endpoint. Defaults to [\"read\"] (least\n  // privilege); the mint endpoint clamps any requested caps to this. There is\n  // deliberately no \"delete\" — a view can never do more than the agent's own\n  // manageCollection tool.\n  capabilities: z.array(z.enum([\"read\", \"write\"])).optional(),\n  // Mobile-only write policy (plans/done/feat-remote-writable-view.md). Default-deny:\n  // a `target: \"mobile\"` view may patch ONLY these fields via\n  // `__MC_VIEW.updateItem`, and may delete only when `allowDelete` is true. The\n  // host re-derives + enforces both on every mutate — never trusting the client.\n  // Ignored for desktop views (they use the token-scoped `capabilities` above).\n  editableFields: z.array(z.string().trim().min(1)).optional(),\n  allowDelete: z.boolean().optional(),\n  // Mobile-only image inlining (plans/done/feat-remote-view-images.md). A\n  // `target: \"mobile\"` view can't reach the host's localhost, so an `image`-type\n  // field's workspace path is unrenderable on the phone; listing it here makes\n  // the host inline it as a downscaled `data:` URL thumbnail in getItems pages.\n  // Opt-in (absent ⇒ none), projection- and budget-bounded host-side. Ignored\n  // for desktop views (they resolve via /api/files/raw).\n  imageFields: z.array(z.string().trim().min(1)).optional(),\n  imageMaxEdge: z.number().int().min(1).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Spawn (host-driven recurrence)\n// ---------------------------------------------------------------------------\n\n/** Recurrence advance for `spawn.every`. `interval` is a positive integer\n *  count of `unit`s (`interval: 3` + `unit: \"month\"` = quarterly);\n *  `dayOfMonth` (month/year only) is the CANONICAL day-of-month anchor\n *  (1-31, read from the rule and clamped per-month at compute time so \"31st\n *  of every month\" never drifts) or the `\"last\"` sentinel for end-of-month.\n *  `.strict()` so the union below cleanly rejects an object carrying BOTH\n *  `unit` and `fromField` (it fails this arm on the unknown `fromField`). */\nexport const EveryLiteralZ = z\n  .object({\n    unit: z.enum([\"day\", \"week\", \"month\", \"year\"]),\n    interval: z.number().int().min(1),\n    dayOfMonth: z.union([z.number().int().min(1).max(31), z.literal(\"last\")]).optional(),\n  })\n  .strict();\n\n/** Field-driven recurrence: pick the interval per-record by an `enum`\n *  field's value — one collection can mix daily / weekly / monthly\n *  obligations in a single list. `map` keys are validated to exactly cover\n *  that field's `values` by a `CollectionSchemaZ` refine (which can see the\n *  sibling `fields`); here each map value just has to be a well-formed\n *  literal `every`. `.strict()` mirrors the literal arm so a both-keys\n *  object fails this arm too. */\nexport const EveryFieldDrivenZ = z\n  .object({\n    fromField: z.string().trim().min(1),\n    map: z.record(z.string(), EveryLiteralZ),\n  })\n  .strict();\n\n/** Either a single literal interval (applied to every record) or the\n *  field-driven map. Two `.strict()` arms mean \"both keys\" and \"neither\n *  key\" both fail validation, with no extra refine. */\nexport const EveryZ = z.union([EveryLiteralZ, EveryFieldDrivenZ]);\n\n/** Host-driven recurrence: when a record satisfies `when` (default:\n *  \"`completionField` value ∈ `completionDoneValues`\"), the host creates the\n *  next record with a forward-advanced `triggerField` date. `carry` copies\n *  record fields verbatim onto the successor; `set` forces fixed values\n *  (typically resetting the status field to its pending value). The\n *  successor's id and contents are a pure function of (source record, this\n *  rule); creation is create-if-absent, so the mechanism stays convergent. */\nexport const SpawnZ = z.object({\n  when: WhenZ.optional(),\n  every: EveryZ,\n  carry: z.array(z.string().trim().min(1)).optional(),\n  set: z.record(z.string(), z.unknown()).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Ingest (scheduled retrieval)\n// ---------------------------------------------------------------------------\n\n/** Declarative retrieval config for a Feed (a collection that refills itself\n *  from the internet). `http-json` needs `itemsAt` (a path to the items\n *  array) only when the response body isn't itself the array; rss/atom yield\n *  items natively and ignore it — so no kind-specific requirement here. */\nexport const DeclarativeIngestZ = z.object({\n  kind: z.enum(INGEST_KINDS),\n  url: z.url(),\n  schedule: z.enum(FEED_SCHEDULES),\n  // Optional UTC hour (0–23) to anchor a `daily` schedule; ignored otherwise.\n  atHour: z.number().int().min(0).max(23).optional(),\n  itemsAt: z.string().trim().min(1).optional(),\n  map: z.record(z.string().trim().min(1), z.string().trim().min(1)),\n  idFrom: z.string().trim().min(1).optional(),\n  maxItems: z.number().int().min(0).optional(),\n});\n\n/** Agent-performed retrieval. Valid on any collection (the primary consumer\n *  is skill-backed collections — feeds keep their declarative kinds). No\n *  `url`/`map`: the worker owns retrieval and record shape, seeded by\n *  `template` + a summary of every record, run in `role`. `template` is\n *  validated the SAME way an action's template is (safe path under\n *  `templates/`), so the skill-bridge mirrors it identically. */\nexport const AgentIngestZ = z.object({\n  kind: z.literal(AGENT_INGEST_KIND),\n  schedule: z.enum(FEED_SCHEDULES),\n  // Optional UTC hour (0–23) to anchor a `daily` schedule; ignored otherwise.\n  atHour: z.number().int().min(0).max(23).optional(),\n  role: z.string().trim().min(1),\n  template: z\n    .string()\n    .trim()\n    .min(1)\n    .refine(isSafeActionTemplatePath, \"must be a safe path under `templates/` (e.g. `templates/refresh.md`; no `..`, no leading `/`, no backslash)\"),\n});\n\n/** The Google event fields a collection may pull from. `id` is absent on\n *  purpose — it always lands in the primary field, since upsert-by-event-id\n *  is what makes the sync idempotent. */\nexport const GOOGLE_CALENDAR_SOURCE_FIELDS = [\"summary\", \"start\", \"end\", \"htmlLink\", \"colorId\", \"status\", \"description\", \"location\"] as const;\n\n/** Marks a collection as the destination of the LLM-free Google Calendar\n *  sync (#2095). `map` is collectionField → Google event field, so the user's\n *  collection keeps whatever field names it already uses. */\nexport const GoogleCalendarSyncZ = z.object({\n  /** Calendar to pull from; defaults to the user's primary. */\n  calendarId: z.string().trim().min(1).optional(),\n  // An empty map is silently useless rather than harmless: the sync would run\n  // and write a record per event carrying ONLY the event id, so the user gets\n  // rows with no content. Fail at load instead.\n  map: z.record(z.string().trim().min(1), z.enum(GOOGLE_CALENDAR_SOURCE_FIELDS)).refine((map) => Object.keys(map).length > 0, {\n    message: \"map at least one field — a `googleCalendar` sync with an empty map writes records that carry only the event id\",\n  }),\n  /** Run the push on the sync schedule too, immediately before each pull, so\n   *  local edits reach Google without anyone pressing a button (#2620).\n   *\n   *  Opt-in and absent by default: a push writes to a calendar other people may\n   *  read, so turning it on is the user's decision, not a default. */\n  autoPush: z.boolean().optional(),\n});\n\n/** `ingest` is a discriminated union on `kind`: the three declarative\n *  retrievers fetch-and-map; `agent` dispatches a hidden worker. Optional on\n *  every schema — skill-backed collections usually omit it; only feeds\n *  discovered from `<workspace>/feeds/` are REQUIRED to carry it (gated by\n *  `acceptParsedSchema`). */\nexport const IngestZ = z.discriminatedUnion(\"kind\", [DeclarativeIngestZ, AgentIngestZ]);\n\n// ---------------------------------------------------------------------------\n// dynamicIcon (data-driven launcher icon) and its `where` predicate\n// ---------------------------------------------------------------------------\n\n// Data-driven launcher-icon override (see `CollectionSchema.dynamicIcon`).\n// `source.collection` may name ANY collection (self or cross-collection),\n// so — unlike `ref`/`embed`/`when.field` elsewhere in this file — its\n// shape is validated here without a cross-field refine against a specific\n// target schema (that collection may not even be loaded yet); a bad\n// `source.collection`/`orderBy`/condition `field` fails soft at compute\n// time instead (`computeCollectionIcon`), matching this feature's locked\n// design (see plans/done/feat-dynamic-collection-icons.md \"Open questions\").\n//\n// `where` is the AND-of-conditions predicate defined at the top of this\n// file (`WhereZ`, shared with `flag` fields) — see `./where` for the\n// evaluator.\nexport const DynamicIconSourceZ = z.object({\n  collection: z.string().trim().min(1),\n  from: z.enum([\"latest\", \"first\", \"when\"]).optional(),\n  orderBy: z.string().trim().min(1).optional(),\n  where: WhereZ.optional(),\n});\nexport const DynamicIconRuleZ = z.object({\n  where: WhereZ,\n  icon: z.string().trim().min(1),\n});\nexport const DynamicIconSpecZ = z.object({\n  source: DynamicIconSourceZ,\n  rules: z.array(DynamicIconRuleZ),\n  fallback: z.string().trim().min(1).optional(),\n});\n\n// ---------------------------------------------------------------------------\n// dataSource (external read-only data file)\n// ---------------------------------------------------------------------------\n\n/** External-data collection: the records ARE the rows of a user-supplied\n *  data file (v1: CSV), queried through DuckDB — never copied into\n *  `<dataDir>/<id>.json` files. Declaring `dataSource` makes the collection\n *  **read-only** in every UI/tool write path; updates happen by replacing /\n *  editing the file itself (file-watch republishes the views). `path` is\n *  workspace-relative and containment-checked exactly like `dataPath`. The\n *  row-id column is the schema's existing `primaryKey` — there is\n *  deliberately no second key concept here.\n *  See plans/done/feat-collection-csv-duckdb-source.md. */\nexport const DataSourceZ = z.object({\n  type: z.literal(\"csv\"),\n  path: z.string().min(1),\n});\n\n// ---------------------------------------------------------------------------\n// storage (alternative writable record backend)\n// ---------------------------------------------------------------------------\n\n/** Alternative WRITABLE storage backend for a collection's records —\n *  unlike `dataSource` (external read-only file), a `storage` collection\n *  behaves like a normal writable collection; only where the rows live\n *  changes. v1: `sqlite` — records in a single SQLite database file\n *  (`node:sqlite`, one JSON record per row keyed by the primaryKey).\n *  `path` is workspace-relative and containment-checked exactly like\n *  `dataPath`. The store factory registry (`server/store.ts`) picks the\n *  implementation by `type` (plans/done/refactor-storage-virtualization.md). */\nexport const StorageZ = z.object({\n  type: z.literal(\"sqlite\"),\n  path: z.string().min(1),\n});\n\n// ---------------------------------------------------------------------------\n// The whole schema\n// ---------------------------------------------------------------------------\n\n/** The schema's SHAPE, before any cross-field rule runs. Named so the rules in\n *  `./schemaRules` can type their argument against it — they receive whatever\n *  this parses to, and nothing narrower. */\nconst CollectionObjectZ = z.object({\n  title: z.string().min(1),\n  icon: z.string().min(1),\n  // Exactly one of `dataPath` (native JSON-file records), `dataSource`\n  // (external read-only data file), or `storage` (alternative writable\n  // backend) — enforced by a refine below.\n  dataPath: z.string().min(1).optional(),\n  dataSource: DataSourceZ.optional(),\n  storage: StorageZ.optional(),\n  primaryKey: z.string().min(1),\n  // When set, the collection holds at most one record whose primary\n  // key is this exact value (e.g. `me` for the business profile).\n  // The host fixes the create form's primary key to it and hides the\n  // Add button once the record exists.\n  singleton: z.string().trim().min(1).optional(),\n  fields: z.record(z.string(), FieldSpecZ),\n  actions: z.array(ActionSpecZ).optional(),\n  // Collection-level actions (header buttons). Same shape as `actions`;\n  // the `when` predicate is ignored (no record context). The seed\n  // prompt injects a progress summary of all records instead.\n  collectionActions: z.array(ActionSpecZ).optional(),\n  // Completion-tracking pair: when both are set, item-create fires a\n  // notification that clears once `completionField` transitions into\n  // `completionDoneValues`. The two are bound together — declaring\n  // one without the other is a misconfiguration the cross-field\n  // refine below rejects.\n  completionField: z.string().trim().min(1).optional(),\n  completionDoneValues: z.array(z.string().trim().min(1)).min(1).optional(),\n  // Optional human-readable label for the completion notification's\n  // title — names the field whose value reads better than the opaque\n  // primaryKey (e.g. a `name` field). Falls back to the primaryKey\n  // value at render time when unset or empty.\n  displayField: z.string().trim().min(1).optional(),\n  // Time gate: names a `date` field that delays the completion bell\n  // until the clock reaches it. Requires the completion pair (the bell\n  // still clears via the done value). Validated to name a real `date`\n  // field by refines below.\n  triggerField: z.string().trim().min(1).optional(),\n  // Lead time in whole days — fire the bell this many days before\n  // `triggerField`. Non-negative; requires `triggerField` (refine below).\n  triggerLeadDays: z.number().int().min(0).optional(),\n  // Host-driven recurrence; requires `triggerField`. See SpawnZ.\n  spawn: SpawnZ.optional(),\n  // Calendar view anchor: names a `date` field whose value places each\n  // record on a month grid. Validated to name a real `date` field by a\n  // refine below. Optional — the toggle auto-derives from any `date`\n  // field when this is unset.\n  calendarField: z.string().trim().min(1).optional(),\n  // Multi-day span end: a second `date` field the calendar record spans\n  // to. Requires `calendarField`; validated to name a real `date` field.\n  calendarEndField: z.string().trim().min(1).optional(),\n  // Day (time-allocation) view time source: names a string field holding a\n  // free-form time or time-range (e.g. \"14:00-17:00\", \"17:00-\", \"16:30\").\n  // Consulted only when the date fields are date-only. Requires\n  // `calendarField`; validated to name a real field by a refine below.\n  calendarTimeField: z.string().trim().min(1).optional(),\n  // Kanban board group: names an `enum` field whose value buckets each\n  // record into a column. Validated to name a real `enum` field by a\n  // refine below. Optional — the toggle auto-derives from any `enum`\n  // field when this is unset.\n  kanbanField: z.string().trim().min(1).optional(),\n  // Custom (LLM-authored) HTML views. Each renders in a sandboxed iframe\n  // over the records. Optional, so every existing schema validates\n  // unchanged. Ids validated to be valid + unique slugs by refines below.\n  views: z.array(CustomViewZ).optional(),\n  // Completion-bell gate: only notify for records matching this predicate\n  // (e.g. high-priority todos). Reuses the `when` shape; requires\n  // `completionField`; field validated to exist by refines below.\n  notifyWhen: WhenZ.optional(),\n  // Declarative retrieval config. Present only on Feeds (collections in\n  // the `<workspace>/feeds/` registry). Optional, so every existing\n  // skill schema validates unchanged.\n  ingest: IngestZ.optional(),\n  // Declares this collection as the destination of the LLM-free Google\n  // Calendar sync. Optional, so every existing schema validates unchanged.\n  googleCalendar: GoogleCalendarSyncZ.optional(),\n  // Data-driven launcher-icon override. Optional, so every existing\n  // schema validates unchanged; `source` is required within it.\n  dynamicIcon: DynamicIconSpecZ.optional(),\n});\n\nexport type CollectionSchemaInput = z.infer<typeof CollectionObjectZ>;\n\nconst BareCollectionSchemaZ = CollectionObjectZ\n  // Exactly one storage declaration: native records need `dataPath`, an\n  // external data file needs `dataSource`, an alternative backend needs\n  // `storage`. Zero (nowhere to read) and several (ambiguous which wins)\n  // are equally meaningless — fail loudly at load instead of picking\n  // silently.\n  .refine(declaresExactlyOneStore, {\n    message:\n      \"declare exactly one of `dataPath` (native JSON records), `dataSource` (external read-only data file), or `storage` (alternative writable backend)\",\n    path: [\"dataPath\"],\n  })\n  // NOTE: `storage` collections support the full write machinery\n  // (`spawn` / `completionField` / `triggerField` / `singleton` / `ingest`\n  // / mutate actions) — spawn and the watcher reconcilers go through the\n  // CollectionStore seam, and a db-file watcher drives their\n  // reconciliation (collection-watchers/watcher.ts).\n  // A `dataSource` collection is read-only by definition, so schema-level\n  // write machinery can never fire: `singleton` pins CREATES, `ingest`\n  // REFILLS records, `spawn` WRITES successor records. Rejecting them at\n  // validation kills whole classes of writes before any runtime guard.\n  .refine(dataSourceDeclaresNoWriteMachinery, {\n    message: \"a `dataSource` collection is read-only — it cannot declare `singleton`, `ingest`, `spawn`, or `googleCalendar` (all of them write records)\",\n    path: [\"dataSource\"],\n  })\n  // The sync writes each mapped value into a declared field, and puts the\n  // Google event id in the primary field — so a map key that names no field\n  // (or names the primary) would silently drop data or fight the id.\n  .refine(googleCalendarMapNamesStoredFields, {\n    message: \"a `googleCalendar` map key must name a declared, non-computed field, and never the primaryKey (that always holds the Google event id)\",\n    path: [\"googleCalendar\"],\n  })\n  // Same rule for declarative host writes: a mutate action writes the\n  // record it's invoked on.\n  .refine(dataSourceDeclaresNoMutateAction, {\n    message: 'a `dataSource` collection is read-only — its actions cannot use `kind: \"mutate\"` (a host write); use `chat`/`agent` actions instead',\n    path: [\"dataSource\"],\n  })\n  // The singleton value becomes a record id (and thus a `<id>.json`\n  // filename), so it must satisfy the SAME record-id rule the write path\n  // enforces — otherwise the create form would lock the primary key to a\n  // value the POST route then rejects as an invalid item id, making the\n  // collection impossible to initialize (Codex P1).\n  .refine(singletonIsAValidRecordId, {\n    message: \"schema `singleton` must be a valid item id (alphanumeric / hyphen / underscore / interior dot, no `..` or path separators)\",\n    path: [\"singleton\"],\n  })\n  // Action ids must be unique so the dispatch route resolves\n  // unambiguously.\n  .refine(actionIdsAreUnique, {\n    message: \"schema `actions` must have unique `id`s\",\n    path: [\"actions\"],\n  })\n  // Collection-level action ids must likewise be unique.\n  .refine(collectionActionIdsAreUnique, {\n    message: \"schema `collectionActions` must have unique `id`s\",\n    path: [\"collectionActions\"],\n  })\n  // A mutate action's `set` writes real STORED fields: a typo'd key\n  // would write a stray value forever, a computed/projected field is\n  // never persisted, and the primaryKey is the filename (renaming is\n  // not a mutation).\n  .refine(mutateSetKeysNameStoredFields, {\n    message: \"a mutate action's `set` keys must name declared, non-computed fields (and never the primaryKey)\",\n    path: [\"actions\"],\n  })\n  // Every `$params.<name>` reference in `set` must name a declared\n  // param — an undeclared one would silently no-op the assignment.\n  .refine(mutateParamRefsAreDeclared, {\n    message: \"a mutate action's `$params.<name>` references must name keys declared in its `params`\",\n    path: [\"actions\"],\n  })\n  // A collection-level action has no record to write.\n  .refine(collectionActionsAreNotMutate, {\n    message: '`collectionActions` cannot contain `kind: \"mutate\"` — a collection-level action has no record to write',\n    path: [\"collectionActions\"],\n  })\n  // A `currencyField` pointer must name a real top-level field that\n  // holds a code string — a typo (`curreny`) would otherwise pass the\n  // per-field check, then silently fall back to the literal / USD at\n  // render and mislabel amounts. Checked at the schema level because a\n  // field can't see its siblings.\n  .refine(currencyFieldRefsNameCodeFields, {\n    message: \"a money field's `currencyField` must name a top-level `string`, `text`, or `enum` field that holds the currency code\",\n    path: [\"fields\"],\n  })\n  // Completion-tracking pair must be declared together: declaring\n  // `completionField` without `completionDoneValues` (or vice-versa)\n  // is meaningless — the host would either never fire (no done values\n  // to compare against) or never clear (no field to read). Bound\n  // together so the misconfiguration fails loudly at load time.\n  // EXCEPTION: when `completionField` names a `flag` field, done ⇔ the\n  // flag's `where` matches, so `completionDoneValues` carries no\n  // information and MUST be omitted (declaring it would invite a\n  // contradictory second source of truth).\n  .refine(completionPairIsCoherent, {\n    message:\n      \"schema `completionField` and `completionDoneValues` must be declared together (both set, or both omitted) — unless `completionField` names a `flag` field, in which case `completionDoneValues` must be omitted (done ⇔ the flag matches)\",\n    path: [\"completionField\"],\n  })\n  // `completionField` must name a real top-level field — a typo would\n  // silently disable the notification mechanism otherwise.\n  .refine(completionFieldIsDeclared, {\n    message: \"schema `completionField` must name a top-level field declared in `fields`\",\n    path: [\"completionField\"],\n  })\n  // `displayField`, like `completionField`, must name a real top-level\n  // field — a typo would silently fall back to the primaryKey forever.\n  .refine(displayFieldIsDeclared, {\n    message: \"schema `displayField` must name a top-level field declared in `fields`\",\n    path: [\"displayField\"],\n  })\n  // A field's `when.field` gates its visibility against a sibling's\n  // value, so it must name a real top-level field — a typo would\n  // silently keep the field hidden forever (the gate never matches).\n  // Checked at the schema level because a field can't see its siblings.\n  .refine(fieldVisibilityGatesNameDeclaredFields, {\n    message: \"a field's `when.field` must name a top-level field declared in `fields`\",\n    path: [\"fields\"],\n  })\n  // A flag's `where` reads sibling fields (both `cond.field` and a\n  // same-record `valueFrom.field`), so each must name a real top-level\n  // field — a typo would silently pin the flag false forever (`ne`:\n  // true forever). Checked at the schema level because a field can't\n  // see its siblings.\n  .refine(flagConditionsNameDeclaredFields, {\n    message: \"a flag field's `where` conditions must name top-level fields declared in `fields` (both `field` and a same-record `valueFrom.field`)\",\n    path: [\"fields\"],\n  })\n  // A flag named by `completionField` is evaluated against the RAW\n  // record — the reconciler (and spawn's fallback) read items straight\n  // off disk, BEFORE any `deriveAll` enrichment — so its `where` may\n  // only reference STORED fields. A condition over a computed sibling\n  // (derived/rollup/toggle/flag/embed/backlinks) would see an absent\n  // key: `ne` matches vacuously, every other op reads false, and the\n  // bell would clear wrongly / never. General (non-completion) flags\n  // keep the full vocabulary — the UI evaluates them post-enrichment.\n  .refine(completionFlagReadsOnlyStoredFields, {\n    message:\n      \"a `flag` named by `completionField` may only reference STORED fields in its `where` — completion is evaluated against the raw record (before deriveAll), where computed values (derived/rollup/toggle/flag/embed/backlinks) are absent\",\n    path: [\"completionField\"],\n  })\n  // The spawn-inert guard (`spawnSuccessorStartsInert`) statically\n  // checks that a successor is not born already matching the spawn\n  // predicate; it cannot see through a flag's `where` (the predicate\n  // would need full record evaluation against `set`/`carry`). So a\n  // schema whose completion is flag-form may only spawn with an\n  // explicit `spawn.when` — which the guard CAN check.\n  .refine(flagCompletionSpawnDeclaresWhen, {\n    message:\n      \"a schema whose `completionField` names a `flag` field must declare an explicit `spawn.when` (the spawn-inert check cannot statically evaluate a flag's `where`)\",\n    path: [\"spawn\"],\n  })\n  // An `embed`'s `idField` resolves the target record id from a sibling's\n  // value, so it must name a real top-level field — and one whose stored\n  // value is a plain id string. Only `ref` / `string` qualify: the editor\n  // writes the picked id into that field, so a non-persisted or composite\n  // type (`embed` / `derived` / `toggle` / `table` / `number` / …) would\n  // either not round-trip on save or hold no usable id. Restricting it\n  // makes the misconfiguration fail at schema load, not silently at\n  // render. `idField` is ignored on non-`embed` fields, so only check\n  // there. Schema-level because a field can't see its siblings.\n  .refine(embedIdFieldsNameIdBearingFields, {\n    message: \"an embed field's `idField` must name a top-level `ref` or `string` field declared in `fields`\",\n    path: [\"fields\"],\n  })\n  // `triggerField` requires the completion pair: the time gate only\n  // suppresses the *completion* bell until the date, and the bell still\n  // clears via `completionDoneValues`. Without completion there is no\n  // bell to gate (or clear), so the declaration is meaningless.\n  .refine(triggerFieldRequiresCompletion, {\n    message: \"schema `triggerField` requires `completionField` / `completionDoneValues` (the gated bell still clears via the done value)\",\n    path: [\"triggerField\"],\n  })\n  // `triggerField` must name a real `date` field — the gate parses its\n  // value as `YYYY-MM-DD`; any other type can't be compared to the clock.\n  .refine(triggerFieldIsADateField, {\n    message: \"schema `triggerField` must name a top-level `date` field declared in `fields`\",\n    path: [\"triggerField\"],\n  })\n  // `triggerLeadDays` only means something relative to a trigger date.\n  .refine(triggerLeadDaysRequiresTriggerField, {\n    message: \"schema `triggerLeadDays` requires `triggerField` (it shifts when that field's bell fires)\",\n    path: [\"triggerLeadDays\"],\n  })\n  // `spawn` advances `triggerField` to compute the successor's trigger\n  // date, so the schema must declare one.\n  .refine(spawnRequiresTriggerField, {\n    message: \"schema `spawn` requires `triggerField` (the successor's trigger date is `triggerField` advanced by `spawn.every`)\",\n    path: [\"spawn\"],\n  })\n  // `spawn.when.field` and every `spawn.carry` entry must name real\n  // top-level fields — a typo would silently never match / never copy.\n  .refine(spawnWhenFieldIsDeclared, {\n    message: \"schema `spawn.when.field` must name a top-level field declared in `fields`\",\n    path: [\"spawn\"],\n  })\n  .refine(spawnCarryEntriesAreDeclared, {\n    message: \"every `spawn.carry` entry must name a top-level field declared in `fields`\",\n    path: [\"spawn\"],\n  })\n  // A successor must NOT be born already matching its own spawn predicate\n  // — it would re-spawn on its first reconcile, fanning out into an\n  // unbounded chain of records. The predicate field/values are `spawn.when`\n  // when given, else the completion-done pair (the default predicate). The\n  // successor's value for that field is `set[field]` if set, else the\n  // carried source value (which matched, by definition, when the spawn\n  // fired) if carried, else absent (safe). Reject the first two when they\n  // land on a matching value.\n  .refine(spawnSuccessorStartsInert, {\n    message:\n      \"`spawn` must leave the successor in a non-matching state (e.g. `set` the status to a pending value); seeding the predicate field to a matching value via `set`/`carry` would respawn forever\",\n    path: [\"spawn\"],\n  })\n  // Field-driven `spawn.every` (§4.1): `fromField` must name a top-level\n  // `enum` — the only field type with a closed, finite value set to drive\n  // the `map` and the form `<select>`.\n  .refine(fieldDrivenFromFieldIsEnum, {\n    message: \"`spawn.every.fromField` must name a top-level `enum` field declared in `fields`\",\n    path: [\"spawn\"],\n  })\n  // Field-driven `spawn.every` (§4.2): the `map` keys must exactly cover the\n  // enum's `values` — a missing key would stall a record at that frequency;\n  // an extra key signals a map left stale after an enum edit.\n  .refine(fieldDrivenMapCoversValues, {\n    message: \"`spawn.every.map` keys must exactly cover the `values` of the `enum` named by `fromField` (no missing or extra keys)\",\n    path: [\"spawn\"],\n  })\n  // Field-driven `spawn.every` (§4.5): `fromField` must be carried (or `set`)\n  // onto the successor, or the next spawn in the chain can't resolve an\n  // interval and the recurrence silently halts.\n  .refine(fieldDrivenFromFieldCarried, {\n    message:\n      \"`spawn.every.fromField` must appear in `spawn.carry`, or be written by `spawn.set` to a value present in `spawn.every.map`, so the successor keeps a resolvable recurrence interval\",\n    path: [\"spawn\"],\n  })\n  // `calendarField` must name a real `date`/`datetime` field — the calendar\n  // view parses its value to place records on the month grid (a `datetime`\n  // anchor also carries the clock for the day view); any other type can't be\n  // put on a calendar.\n  .refine(calendarFieldIsDateLike, {\n    message: \"schema `calendarField` must name a top-level `date` or `datetime` field declared in `fields`\",\n    path: [\"calendarField\"],\n  })\n  // `calendarEndField` marks the end of a multi-day span, so it only means\n  // something alongside a start anchor.\n  .refine(calendarEndFieldRequiresCalendarField, {\n    message: \"schema `calendarEndField` requires `calendarField` (it marks the end of the span that starts at `calendarField`)\",\n    path: [\"calendarEndField\"],\n  })\n  // `calendarEndField` must also name a real `date`/`datetime` field — same parse.\n  .refine(calendarEndFieldIsDateLike, {\n    message: \"schema `calendarEndField` must name a top-level `date` or `datetime` field declared in `fields`\",\n    path: [\"calendarEndField\"],\n  })\n  // `calendarTimeField` places records on the day view, so it only means\n  // something alongside a start anchor.\n  .refine(calendarTimeFieldRequiresCalendarField, {\n    message: \"schema `calendarTimeField` requires `calendarField` (it supplies the time-of-day for the calendar's day view)\",\n    path: [\"calendarTimeField\"],\n  })\n  // `calendarTimeField` must name a real top-level field (a free-form time\n  // string the day view parses).\n  .refine(calendarTimeFieldIsDeclared, {\n    message: \"schema `calendarTimeField` must name a top-level field declared in `fields`\",\n    path: [\"calendarTimeField\"],\n  })\n  // …and that field must be string-backed — the day view parses its value as a\n  // time string, so a number/enum/date column can't drive it.\n  .refine(calendarTimeFieldIsStringBacked, {\n    message: \"schema `calendarTimeField` must name a top-level `string` or `text` field declared in `fields`\",\n    path: [\"calendarTimeField\"],\n  })\n  // `kanbanField` must name a real `enum` field — the board groups records\n  // into one column per declared enum value; any other type has no closed\n  // set of columns to group by.\n  .refine(kanbanFieldIsAnEnum, {\n    message: \"schema `kanbanField` must name a top-level `enum` field declared in `fields`\",\n    path: [\"kanbanField\"],\n  })\n  // A `toggle` field projects an `enum` field: its `field` must name a real\n  // top-level enum, and `onValue` / `offValue` must be members of that\n  // enum's `values` — otherwise toggling would write a value outside the\n  // closed set (and never appear \"checked\").\n  .refine(togglesProjectValidEnums, {\n    message: \"a `toggle` field's `field` must name a top-level `enum` field, and its `onValue`/`offValue` must be values of that enum\",\n    path: [\"fields\"],\n  })\n  // `notifyWhen` narrows the completion bell, so it only means something with\n  // completion tracking, and its `field` must name a real top-level field.\n  .refine(notifyWhenRequiresCompletion, {\n    message: \"schema `notifyWhen` requires `completionField` (it narrows that bell)\",\n    path: [\"notifyWhen\"],\n  })\n  .refine(notifyWhenFieldIsDeclared, {\n    message: \"schema `notifyWhen.field` must name a top-level field declared in `fields`\",\n    path: [\"notifyWhen\"],\n  })\n  // Every custom view `id` must be a valid slug — it doubles as the\n  // view-mode selector key (`custom:<id>`) and the capability-token clamp\n  // key, both of which expect a path-safe token.\n  .refine(viewIdsAreSlugs, {\n    message: \"every `views[].id` must be a valid slug (alphanumeric / hyphen / underscore, no path separators)\",\n    path: [\"views\"],\n  })\n  // Custom view ids must be unique so the selector + token clamp resolve\n  // unambiguously.\n  .refine(viewIdsAreUnique, {\n    message: \"schema `views` must have unique `id`s\",\n    path: [\"views\"],\n  });\n\n// ---------------------------------------------------------------------------\n// Prototype-sensitive field names — checked on the RAW input\n// ---------------------------------------------------------------------------\n\n// A field name becomes a plain-object key THROUGHOUT the engine — record\n// JSON, edit drafts, enrichment output, view filter state — where a\n// prototype-sensitive name would read (or write) inherited prototype data\n// instead of field data (Codex review on PR #2176: a flag named\n// `__proto__` jams its filter chip). This check must run BEFORE zod\n// parses: zod's record builder silently SKIPS an own `__proto__` input\n// key (pollution safety), so a post-parse refine would never see it and\n// the author's field would just vanish. Hence the `z.preprocess` wrapper\n// below rather than another `.refine`.\nconst PROTOTYPE_KEYS = [\"__proto__\", \"constructor\", \"prototype\"] as const;\n\n/** The first own prototype-sensitive key of `value`, or null. */\nfunction ownPrototypeKey(value: unknown): string | null {\n  if (value === null || typeof value !== \"object\") return null;\n  for (const key of PROTOTYPE_KEYS) {\n    if (Object.hasOwn(value, key)) return key;\n  }\n  return null;\n}\n\n/** Own enumerable entries of an object (arrays keyed by index), none for\n *  anything else — the raw input is unvalidated, so `fields` may be junk. */\nfunction ownEntries(value: unknown): [string, unknown][] {\n  if (isUnknownArray(value)) return value.map((entry, index): [string, unknown] => [String(index), entry]);\n  return isRecord(value) ? Object.entries(value) : [];\n}\n\n/** The name-defining sub-record a raw field spec (`of`) or action (`params`)\n *  carries, or undefined when the holder isn't an object at all. */\nfunction nameDefiningSubRecord(holder: unknown, key: \"of\" | \"params\"): unknown {\n  return isRecord(holder) ? holder[key] : undefined;\n}\n\n/** Dotted path of the first prototype-sensitive `params` name across both\n *  action lists, or null. */\nfunction prototypeActionParamPath(input: Record<string, unknown>): string | null {\n  for (const [listName, list] of [\n    [\"actions\", input.actions],\n    [\"collectionActions\", input.collectionActions],\n  ] as const) {\n    for (const action of isUnknownArray(list) ? list : []) {\n      const badParam = ownPrototypeKey(nameDefiningSubRecord(action, \"params\"));\n      if (badParam !== null) return `${listName}.params.${badParam}`;\n    }\n  }\n  return null;\n}\n\n/** Dotted path of the first prototype-sensitive field name in the raw\n *  schema input — top-level `fields`, each table field's `of`, and each\n *  action's `params` (the three records that DEFINE names) — or null. */\nfunction prototypeFieldKeyPath(input: unknown): string | null {\n  if (!isRecord(input)) return null;\n  const bad = ownPrototypeKey(input.fields);\n  if (bad !== null) return `fields.${bad}`;\n  for (const [key, spec] of ownEntries(input.fields)) {\n    const badSub = ownPrototypeKey(nameDefiningSubRecord(spec, \"of\"));\n    if (badSub !== null) return `fields.${key}.of.${badSub}`;\n  }\n  return prototypeActionParamPath(input);\n}\n\nexport const CollectionSchemaZ = z.preprocess((input, ctx) => {\n  const bad = prototypeFieldKeyPath(input);\n  if (bad !== null) {\n    ctx.addIssue({ code: \"custom\", message: `'${bad}': field names must not be prototype-sensitive keys (\\`__proto__\\`, \\`constructor\\`, \\`prototype\\`)` });\n    return z.NEVER;\n  }\n  return input;\n}, BareCollectionSchemaZ);\n","// Discover schema-driven collections. A \"collection\" is a skill\n// directory that ships a `schema.json` alongside its `SKILL.md`.\n// Scans both user (`~/.claude/skills/`) and project\n// (`<workspace>/.claude/skills/`) scopes; project wins on slug\n// collision (mirrors the rule in\n// `server/workspace/skills/discovery.ts`).\n//\n// The schema validator itself lives in `../core/schemaZ` (the zod single\n// source of truth every `../core/schema` type derives from); this module\n// applies it, plus the post-Zod acceptance gates below.\n\nimport { readdir, readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { log, getWorkspaceRoot, userSkillsDir, projectSkillsDir, feedsRoot } from \"./host\";\nimport { CollectionSchemaZ } from \"../core/schemaZ\";\nimport { SCHEMA_FILE, resolveDataDir, safeSlugName } from \"./paths\";\nimport type { LoadedCollection } from \"./discoveredCollection\";\nimport type { CollectionDetail, CollectionSchema, CollectionSource, CollectionSummary } from \"../core/schema\";\nimport { isErrorWithCode, isRecord } from \"@mulmoclaude/common\";\n\n// Re-exported for the existing `collection/server` importers (manageCollection's\n// putSchema, the registry importWriter) that validate schemas the same way\n// discovery does.\nexport { CollectionSchemaZ };\n\n// The LoadedCollection shape now lives in @mulmoclaude/core/collection/server\n// (imported at the top, re-exported below) so discovery stays its producer and\n// the many `from \"./discovery.js\"` importers resolve it unchanged.\n\n// Normalize an agent-authored feed schema (no register tool to do it):\n// default `icon`, and **force** `dataPath` to the feed-owned namespace\n// `data/feeds/<slug>`. Forcing dataPath (rather than trusting the file) is\n// a safety boundary — a feed can only ever read/write/delete records under\n// its own folder, never another app's data (e.g. `data/wiki`). Non-object\n// input passes through so the Zod error stays clear.\nfunction applyFeedSchemaDefaults(parsed: unknown, slug: string): unknown {\n  if (!isRecord(parsed)) return parsed;\n  const icon = typeof parsed.icon === \"string\" && parsed.icon.trim().length > 0 ? parsed.icon : \"dynamic_feed\";\n  return { ...parsed, icon, dataPath: `data/feeds/${slug}` };\n}\n\n/** Result of the post-Zod acceptance gates: the resolved record dir (and,\n *  for a `dataSource` schema, the resolved data file) on success, or a\n *  one-line reason discovery would skip the schema. */\nexport type SchemaAcceptance = { ok: true; dataDir: string; dataSourceFile?: string; storageFile?: string } | { ok: false; reason: string };\n\n/** The conventional per-slug records dir a `dataSource` / `storage` collection\n *  gets as its `dataDir` (records never live there, but archive/delete paths\n *  stay well-defined — same shape the registry's R3 normalization uses).\n *\n *  INVARIANT — this is NOT a default `dataPath`, and must not be used as one.\n *  It applies only to the two backends whose records are not per-file JSON. A\n *  normal collection declares its own location and exactly one of `dataPath` /\n *  `dataSource` / `storage`; a schema with none of the three is REJECTED, not\n *  quietly pointed here. Handing a per-file collection this path would silently\n *  relocate its records away from the folder the user (and its SKILL.md) sees. */\nfunction conventionalDataPath(slug: string): string {\n  return `data/collections/${slug}/items`;\n}\n\n/** The declared field named by `primaryKey`, or `undefined` when the schema\n *  declares no such field. Own-property guarded: a `primaryKey` of `toString`\n *  / `constructor` / `__proto__` must miss here, not read an Object.prototype\n *  member and slip past the \"is it a declared field?\" gate into the wrong\n *  \"add `primary: true`\" advice. Shared with manageCollection's putSchema\n *  gate so both report the SAME reason. */\nexport function resolvePrimaryField(fields: CollectionSchema[\"fields\"], primaryKey: string): CollectionSchema[\"fields\"][string] | undefined {\n  return Object.hasOwn(fields, primaryKey) ? fields[primaryKey] : undefined;\n}\n\n/** The acceptance gates discovery applies AFTER `CollectionSchemaZ` parses,\n *  before a schema becomes a live collection:\n *\n *  - the `primaryKey` must be a declared field flagged `primary: true` —\n *    without the flag CollectionView renders the field editable, and a\n *    rename is silently pinned back to the URL itemId on save, so the user's\n *    edit is dropped with no error;\n *  - a `feed` schema must declare an `ingest` block (else it's a dead,\n *    non-refreshable card);\n *  - `dataPath` — or a `dataSource`'s `path` — must resolve INSIDE the\n *    workspace (same realpath containment for both).\n *\n *  Exported so `manageCollection`'s `putSchema` can run the SAME gates before\n *  it reports success — a schema that passes `CollectionSchemaZ` but fails one\n *  of these would otherwise write cleanly yet be skipped on the next discovery,\n *  hiding the collection (the exact failure that tool exists to prevent). */\nexport function acceptParsedSchema(schema: CollectionSchema, opts: { source: CollectionSource; workspaceRoot: string; slug: string }): SchemaAcceptance {\n  const primaryField = resolvePrimaryField(schema.fields, schema.primaryKey);\n  if (!primaryField) return { ok: false, reason: `primaryKey '${schema.primaryKey}' is not one of the declared fields` };\n  if (primaryField.primary !== true) return { ok: false, reason: `the primaryKey field '${schema.primaryKey}' must be flagged \\`primary: true\\`` };\n  if (opts.source === \"feed\" && !schema.ingest) return { ok: false, reason: \"a feed schema must declare an `ingest` block\" };\n  if (schema.dataSource !== undefined) {\n    // Same containment math as dataPath — resolveDataDir doesn't require\n    // the target to exist, so it validates a file path just as well.\n    const dataSourceFile = resolveDataDir(schema.dataSource.path, opts.workspaceRoot);\n    if (dataSourceFile === null) return { ok: false, reason: `dataSource.path '${schema.dataSource.path}' escapes the workspace` };\n    const dataDir = resolveDataDir(conventionalDataPath(opts.slug), opts.workspaceRoot);\n    if (dataDir === null) return { ok: false, reason: `slug '${opts.slug}' yields no workspace-contained data dir` };\n    return { ok: true, dataDir, dataSourceFile };\n  }\n  if (schema.storage !== undefined) {\n    // An alternative-backend data file (e.g. the SQLite db): same\n    // containment as dataSource, same conventional phantom dataDir.\n    const storageFile = resolveDataDir(schema.storage.path, opts.workspaceRoot);\n    if (storageFile === null) return { ok: false, reason: `storage.path '${schema.storage.path}' escapes the workspace` };\n    const dataDir = resolveDataDir(conventionalDataPath(opts.slug), opts.workspaceRoot);\n    if (dataDir === null) return { ok: false, reason: `slug '${opts.slug}' yields no workspace-contained data dir` };\n    return { ok: true, dataDir, storageFile };\n  }\n  const dataDir = resolveDataDir(schema.dataPath ?? \"\", opts.workspaceRoot);\n  if (dataDir === null) return { ok: false, reason: `dataPath '${schema.dataPath}' escapes the workspace` };\n  return { ok: true, dataDir };\n}\n\nasync function loadOneCollection(skillsRoot: string, slug: string, source: CollectionSource, workspaceRoot: string): Promise<LoadedCollection | null> {\n  const safeName = safeSlugName(slug);\n  if (safeName === null) return null;\n  const schemaPath = path.join(skillsRoot, safeName, SCHEMA_FILE);\n  let raw: string;\n  try {\n    const fileStat = await stat(schemaPath);\n    if (!fileStat.isFile()) return null;\n    raw = await readFile(schemaPath, \"utf-8\");\n  } catch (err) {\n    if (!isErrorWithCode(err) || err.code !== \"ENOENT\") {\n      log.warn(\"collections\", \"failed to read schema.json, skipping\", { slug: safeName, path: schemaPath, error: String(err) });\n    }\n    return null;\n  }\n\n  let parsedJson: unknown;\n  try {\n    parsedJson = JSON.parse(raw);\n  } catch (err) {\n    log.warn(\"collections\", \"schema.json is not valid JSON, skipping\", { slug: safeName, error: String(err) });\n    return null;\n  }\n\n  // Feeds are authored by the agent as plain files (no register tool), so\n  // fill the boilerplate icon / dataPath if omitted before validation.\n  const candidate = source === \"feed\" ? applyFeedSchemaDefaults(parsedJson, safeName) : parsedJson;\n  const parsed = CollectionSchemaZ.safeParse(candidate);\n  if (!parsed.success) {\n    log.warn(\"collections\", \"schema.json failed validation, skipping\", { slug: safeName, issues: parsed.error.issues });\n    return null;\n  }\n\n  // Post-Zod acceptance gates (primaryKey flagged primary, feed ingest,\n  // workspace-contained dataPath) — shared with manageCollection's putSchema\n  // so a validated write and discovery agree on what's a live collection.\n  const schema = parsed.data;\n  const acceptance = acceptParsedSchema(schema, { source, workspaceRoot, slug: safeName });\n  if (!acceptance.ok) {\n    log.warn(\"collections\", \"schema.json rejected after validation, skipping\", { slug: safeName, reason: acceptance.reason });\n    return null;\n  }\n\n  return {\n    slug: safeName,\n    source,\n    schema,\n    dataDir: acceptance.dataDir,\n    ...(acceptance.dataSourceFile !== undefined ? { dataSourceFile: acceptance.dataSourceFile } : {}),\n    ...(acceptance.storageFile !== undefined ? { storageFile: acceptance.storageFile } : {}),\n    skillDir: path.join(skillsRoot, safeName),\n  };\n}\n\nasync function collectFromDir(skillsRoot: string, source: CollectionSource, workspaceRoot: string): Promise<LoadedCollection[]> {\n  let entries: string[];\n  try {\n    entries = await readdir(skillsRoot);\n  } catch (err) {\n    if (isErrorWithCode(err) && err.code === \"ENOENT\") return [];\n    log.warn(\"collections\", \"failed to list skills dir, returning empty\", { root: skillsRoot, error: String(err) });\n    return [];\n  }\n\n  const results: LoadedCollection[] = [];\n  for (const name of entries) {\n    if (name.startsWith(\".\")) continue;\n    const safeName = safeSlugName(name);\n    if (safeName === null) continue;\n    const dirPath = path.join(skillsRoot, safeName);\n    let dirStat;\n    try {\n      dirStat = await stat(dirPath);\n    } catch {\n      continue;\n    }\n    if (!dirStat.isDirectory()) continue;\n    const collection = await loadOneCollection(skillsRoot, safeName, source, workspaceRoot);\n    if (collection) results.push(collection);\n  }\n  return results;\n}\n\nexport interface DiscoveryOptions {\n  /** Override the workspace root for project-scope skill discovery.\n   *  Default: the live `workspacePath`. Tests point this at a\n   *  `mkdtempSync` tree so they don't touch the user's real\n   *  `~/mulmoclaude/`. Mirrors the pattern in\n   *  `server/workspace/skills/catalog.ts#CatalogOptions`. */\n  workspaceRoot?: string | undefined;\n  /** Override `~/.claude/skills/` for tests. Production callers\n   *  leave this unset. Without an override, even a test-scoped\n   *  workspaceRoot still scans the real user home — which can leak\n   *  unrelated skills into the result. */\n  userSkillsDir?: string | undefined;\n}\n\n/** Discover every schema-driven collection available to this\n *  workspace. Project-scope collections override user-scope on slug\n *  collision. The `workspaceRoot` override also flows into each\n *  collection's dataDir resolution so a tmpdir-scoped test gets\n *  dataDirs under the same tmpdir (Codex P1 review on PR #1489 —\n *  previously dataDir was always rooted at the live workspacePath\n *  regardless of override). */\nexport async function discoverCollections(opts: DiscoveryOptions = {}): Promise<LoadedCollection[]> {\n  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();\n  const userDir = opts.userSkillsDir ?? userSkillsDir();\n  const projectDir = projectSkillsDir(workspaceRoot);\n  // Feeds (the non-skill `<workspace>/feeds/` registry) are scanned as a\n  // third root. They merge FIRST so a real skill collection (user or\n  // project) always overrides a feed on slug collision — a feed must\n  // never shadow a genuine skill-backed collection.\n  const feedCollections = await collectFromDir(feedsRoot(workspaceRoot), \"feed\", workspaceRoot);\n  const userCollections = await collectFromDir(userDir, \"user\", workspaceRoot);\n  const projectCollections = await collectFromDir(projectDir, \"project\", workspaceRoot);\n  const merged = new Map<string, LoadedCollection>();\n  for (const entry of feedCollections) merged.set(entry.slug, entry);\n  for (const entry of userCollections) merged.set(entry.slug, entry);\n  for (const entry of projectCollections) merged.set(entry.slug, entry);\n  return [...merged.values()].sort((left, right) => left.slug.localeCompare(right.slug));\n}\n\n/** Load one collection by slug. Returns null if the slug is invalid,\n *  no matching skill exists, or the schema is malformed. */\nexport async function loadCollection(slug: string, opts: DiscoveryOptions = {}): Promise<LoadedCollection | null> {\n  const safeName = safeSlugName(slug);\n  if (safeName === null) return null;\n  const workspaceRoot = opts.workspaceRoot ?? getWorkspaceRoot();\n  const userDir = opts.userSkillsDir ?? userSkillsDir();\n  const projectDir = projectSkillsDir(workspaceRoot);\n  // Project first (overrides user), then user, then the feeds registry\n  // last — mirroring the merge precedence in `discoverCollections` so a\n  // skill collection always wins over a feed of the same slug.\n  const projectCollection = await loadOneCollection(projectDir, safeName, \"project\", workspaceRoot);\n  if (projectCollection) return projectCollection;\n  const userCollection = await loadOneCollection(userDir, safeName, \"user\", workspaceRoot);\n  if (userCollection) return userCollection;\n  return loadOneCollection(feedsRoot(workspaceRoot), safeName, \"feed\", workspaceRoot);\n}\n\nexport function toSummary(collection: LoadedCollection): CollectionSummary {\n  return {\n    slug: collection.slug,\n    title: collection.schema.title,\n    icon: collection.schema.icon,\n    source: collection.source,\n    ...(collection.schema.dataSource !== undefined ? { readonly: true as const } : {}),\n  };\n}\n\nexport function toDetail(collection: LoadedCollection): CollectionDetail {\n  return { ...toSummary(collection), schema: collection.schema };\n}\n"],"mappings":"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"}