Ontoref Daemon for NCL Caching, File Watching, and Actor Notification Barrier

Accepted

ontoref
Nushell reflection modules invoke `nickel export` as a subprocess ~39 times per full sync scan, taking 2m42s. Each invocation forks a new process (~100ms). There is no shared state between developers a

Context

Nushell reflection modules invoke `nickel export` as a subprocess ~39 times per full sync scan, taking 2m42s. Each invocation forks a new process (~100ms). There is no shared state between developers and agents working on the same project, no notification when a peer changes an ontology or ADR file mid-session, and no persistent store for scan results. The protocol-not-runtime axiom forbids required runtime services — any daemon must be optional with full subprocess fallback. This ADR supersedes stratumiops adr-007, which designed this system inside stratumiops before the protocol was extracted to ontoref.

Decision

ontoref-daemon is an optional persistent daemon providing: (1) NCL export caching — results keyed by (path, mtime, import_path) served via HTTP, file watcher invalidates on change; (2) actor registry — developers, agents, CI register on startup with deterministic tokens (type:hostname:pid), sweep reaps stale sessions every 30s; (3) notification barrier — file changes in .ontology/, adrs/, reflection/ generate typed notifications stored in a per-project ring buffer; pre-commit hook queries pending notifications and blocks commits until acknowledged (fail-open: daemon down = commit allowed). Consumer projects configure the daemon via `.ontoref/config.ncl` (daemon.enabled, daemon.port, db.enabled, db.url). All Nushell modules fall back to direct `nickel export` subprocess when the daemon is unavailable. stratum-db (optional feature, path dep on stratumiops) handles SurrealDB persistence. platform-nats (optional feature, path dep on stratumiops) handles NATS event publishing.

Constraints

  • Hard No Nushell module or bash script may fail when ontoref-daemon is unavailable
  • Hard ontoref-daemon must bind to 127.0.0.1, never to 0.0.0.0 or a public interface
  • Hard The attribution hook must never make a commit depend on ontoref-daemon being reachable: it runs after the commit exists, and a failed or refused daemon call is captured, never propagated
  • Soft All daemon HTTP requests from consumer wrappers must include X-Ontoref-Project header or equivalent project scoping

Alternatives considered

  • In-process Nickel evaluation via nickel-lang-core libraryrejected: nickel-lang-core has an unstable Rust API and resolves import paths differently from the CLI. The subprocess approach with caching is simpler, more stable, and already <1ms cached.
  • Required daemon (always running, no fallback)rejected: Violates the protocol-not-runtime axiom. Consumer projects must function without any ontoref process running. The daemon is an optimization and awareness layer, not infrastructure.
  • Filesystem-based JSON cache without a daemon processrejected: File-based caching requires lock management, cannot serve concurrent requests from multiple actors, and does not provide file watching or the actor registry. A daemon centralizes these concerns cleanly.
  • Bundle SurrealDB and NATS clients directly in ontoref, not as path depsrejected: Duplicating stratum-db and platform-nats creates divergence. Both crates are general-purpose infrastructure; ontoref consumers already have stratumiops checked out for other reasons. Path deps preserve the single canonical implementation.

Related ADRs

ADR-001

Was this useful? Rate it
Got something to add? Tell me what you think, what you'd suggest, or whether we should keep exploring this topic.
· reads

We use cookies to help this site function, understand service usage, and support marketing efforts. Cookie Policy for more info.