22 KiB
Changelog
All notable changes to ontoref are documented here.
ADRs referenced below live in adrs/ as typed Nickel records.
[Unreleased]
Personal Ontology Schemas & Content Modes
Three new typed NCL schema families added to ontology/schemas/ and ontology/defaults/:
| Schema | Types exported |
|---|---|
career.ncl |
Skill, WorkExperience, Talk, Positioning, CompanyTarget, PublicationCard, CareerConfig |
personal.ncl |
Content (BlogPost / ConferenceProposal / CV / Application / Email / Thread), Opportunity (Job / Conference / Grant / Collaboration / Podcast), PersonalConfig |
project-card.ncl |
ProjectCard — canonical display metadata (name, tagline, status, tags, tools, features, sort_order) for portfolio and cv_repo publication |
All types carry linked_nodes | Array String referencing .ontology/core.ncl node IDs.
PublicationCard is a career overlay referencing a canonical project_node from the portfolio repo.
Five NCL DAG reflection modes added to reflection/modes/:
| Mode | Purpose |
|---|---|
draft-application |
Job/grant/collaboration application anchored in personal ontology — gate alignment check, node selection, career trajectory render, status update |
draft-email |
Context-grounded email composition using ontology nodes as evidence |
generate-article |
Blog post / thread generation from project nodes and tensions |
update-cv |
CV refresh loop querying current career.ncl and core.ncl state |
write-cfp |
Conference proposal from Practice/Project nodes with gate alignment check |
Search Bookmarks
Bookmark persistence for search results over the ontology graph. Mirrors Q&A NCL pattern (ADR-003).
reflection/schemas/search_bookmarks.ncl—BookmarkEntry(id, node_id, kind, title, level, term, actor, created_at, tags) andBookmarkStorecontractsreflection/search_bookmarks.ncl— typed store file; conforms toBookmarkStorecontractcrates/ontoref-daemon/src/ui/search_bookmarks_ncl.rs—add_entry/remove_entryvia line-level NCL surgery; auto-incrementedsb-NNNids; concurrency-safe viaNclWriteLock
Tests: next_id_empty, next_id_increments, insert_into_empty_store, delete_first_entry,
delete_second_entry, delete_missing_id_errors, escape_quotes_and_backslashes,
concurrent_add_produces_unique_ids (tokio, 6 concurrent tasks, asserts unique ids).
Protocol
- ADR-006 accepted: Nushell 0.111 string interpolation compatibility fix. Four print statements in
reflection/bin/ontoref.nuused(identifier: expr)patterns inside$"..."— parsed as command calls by Nu 0.111 parser. Fix: bareidentifier: (expr)for label-value pairs; plain strings (no$) for zero-interpolation prints. Hard constraint: no(label: expr)inside$"..."in any.nufile. Soft constraint: zero-interpolation strings must not use$"...". (adr-006)
Self-Description — on+re Update
.ontology/core.ncl — 3 new Practice nodes, updated adr-lifecycle and ontoref-daemon nodes:
| Change | Detail |
|---|---|
New node personal-ontology-schemas |
Yin — career/personal/project-card typed NCL schemas with linked_nodes DAG bridges |
New node content-modes |
Yang — 5 NCL DAG modes for personal content and career operations |
New node search-bookmarks |
Yin — bookmark persistence layer; NCL surgery via search_bookmarks_ncl.rs |
adr-lifecycle |
ADR-006 added to artifact_paths and adrs list |
ontoref-daemon |
search_bookmarks_ncl.rs added to artifact_paths |
New edges: personal-ontology-schemas → dag-formalized (ManifestsIn/High),
personal-ontology-schemas → self-describing (Complements/Medium),
content-modes → reflection-modes (ManifestsIn/High),
content-modes → personal-ontology-schemas (DependsOn/High),
search-bookmarks → qa-knowledge-store (Complements/High),
search-bookmarks → ontoref-daemon (ManifestsIn/High),
ontoref-daemon → search-bookmarks (Contains/High).
.ontology/state.ncl — self-description-coverage catalyst updated to include 2026-03-15 session
additions. protocol-maturity blocker updated to reflect Nu 0.111 fix and personal schema layer
completion.
Previous: 4 axioms, 2 tensions, 17 practices. Current: 4 axioms, 2 tensions, 20 practices.
ADR–Node Declared Linkage
Nodeschema extended withadrs | Array String | default = [](Nickelontology/schemas/core.ncland inlineCoreConfigtype).- Rust
Nodestruct gainsartifact_paths: Vec<String>andadrs: Vec<String>, both#[serde(default)]— zero migration cost for existing nodes that omit the fields. describe.nubuild-howtopopulatesadrsfrom the node record;render-howto(ANSI),render-howto-md, andhowto-to-md-string(clipboard) all emit a Validated by section whenadrsis non-empty.- New
GET /api/adr/{id}?slug=<slug>endpoint — readsadrs/<stem>.ncl, exports via NCL cache, returns JSON. No auth required (read-only, loopback boundary). - Graph UI (
graph.html):adrsfield passed into Cytoscape node data. Detail panel renders "Validated by" section with clickable◆ <adr-id>buttons that open a DaisyUI modal fetching full ADR content via the new endpoint. - Fixed glob pattern error in
describe.nu:build-howto:glob $"($full)/*.rs"replaced withglob ($full | path join "*.rs")— eliminates//in pattern when path has trailing separator.
Self-Description — on+re Update
.ontology/core.ncl — new node, updated nodes, new edges:
| Change | Detail |
|---|---|
New node adr-node-linkage |
Practice: declares adrs field pattern, lists all 5 modified artifacts |
adr-lifecycle |
Description updated; adrs = ["adr-001"…"adr-005"] declared |
describe-query-layer |
Description updated to mention Validated by rendering |
ontoref-ontology-crate |
Description updated to mention artifact_paths + adrs fields; adrs = ["adr-001"] |
New edge adr-node-linkage → adr-lifecycle |
ManifestsIn/High |
New edge adr-node-linkage → describe-query-layer |
Complements/High |
Previous: 4 axioms, 2 tensions, 16 practices. Current: 4 axioms, 2 tensions, 17 practices.
Ontology Three-File Split
- New Practice node
ontology-three-file-splitin.ontology/core.ncl: documents thecore.ncl(what IS) /state.ncl(where we ARE vs want to BE) /gate.ncl(when READY to cross a boundary) separation and the role ofreflection/in answering self-knowledge queries without reading code. assets/presentation/slides.mdspeaker note updated to English with reflection mention.assets/web/src/index.html"Scattered Project Knowledge" solution bullets updated (bilingual) to express the three-file split andreflection/self-knowledge layer.
Auth & Session Model (ADR-005)
Unified key-to-session token exchange across all surfaces. All work gated on #[cfg(feature = "ui")].
KeyEntrygainslabel: String(#[serde(default)]) — audit trail for key-based sessions. NCL schemainstall/resources/schemas/ontoref-project.nclupdated accordingly.verify_keys_listreturnsOption<KeyMatch { role, label }>instead ofOption<Role>.SessionEntrygainsid: String— stable public identifier distinct from the bearer token, safe to expose in list responses. Prevents session enumeration by admins.SessionStoregains secondaryid_index: DashMap<id, token>for O(1)revoke_by_id.- New
SessionStoremethods:list_for_slug,list_all,revoke_all_for_slug,revoke_by_id(id, acting_slug, acting_role) -> RevokeResult. POST /sessions— key → UUID v4 token exchange. Accepts project keys or daemon admin password (project: "_daemon"). Rate-limited. No authentication required (it is the credential exchange endpoint).GET /sessions?project=slug— list active sessions for a project (viewer+). Without?project=param requires daemon admin and returns all sessions.DELETE /sessions/{id}— revoke by public id. Project admin: own project only. Daemon admin: any.require_session()helper: validates UUID v4 Bearer →SessionEntry, error boxed (Box<Response>) to satisfyclippy::result_large_err.check_primary_authfast-path: UUID v4 bearer → session lookup (O(1)) before argon2 fallback (~100ms).project_update_keys(PUT /projects/{slug}/keys) now callssessions.revoke_all_for_slugin addition to actor deregistration. All in-flight UI sessions for the rotated project are immediately invalidated.- Daemon admin:
ONTOREF_ADMIN_TOKEN_FILE(preferred — hash not visible inps aux) orONTOREF_ADMIN_TOKEN. Sessions use virtual slug"_daemon". manage_login_page/manage_login_submit/manage_logouthandlers for/ui/manage/loginand/ui/manage/logout.AdminGuardredirects to/ui/manage/loginwhendaemon_admin_hashis set.
CLI Bearer Token
bearer-argsexported fromreflection/modules/store.nu: returns["-H" "Authorization: Bearer $token"]whenONTOREF_TOKENis set,[]otherwise.http-get,http-post-json,http-delete(new) instore.nuuse...$auth— Bearer injected transparently, no behavior change whenONTOREF_TOKENis unset.notify-daemon-project-addandnotify-daemon-project-removeinreflection/bin/ontoref.nuusebearer-args.
Project Setup & Onboarding
ontoref setupis now the primary onboarding command (replaces manualcp templates/pattern).--kind <K>flag:Service(default) |Library|DevWorkspace|PublishedCrate|AgentResource|Mixed.--parent <path>flag: generates manifest withimplementationlayer +<slug>-frameworklayer and<slug>-browseop mode for implementation children.- Logo auto-detection:
setupscansassets/for<slug>-logo.svg,<slug>.svg,logo.svg(and.pngvariants); insertsui.logointo generatedconfig.nclwhen found. --gen-keys ["admin:label" "viewer:label"]flag: idempotent bootstrap — skips ifrole =already present inproject.ncl. Hashes viaontoref-daemon.bin --hash-password; prints passwords once to stdout.- All
mkdircalls in setup guarded byif not (path | path exists).
Self-Description — on+re Update
.ontology/core.ncl — 3 new Practice nodes, updated ontoref-daemon description and artifact_paths:
| Node | Pole | Description |
|---|---|---|
unified-auth-model |
Yang | Key→session token exchange; session.id ≠ bearer; revoke on key rotation |
project-onboarding |
Yang | ontoref setup — idempotent scaffold, --kind, --parent, --gen-keys |
ontoref-daemon node updated: auth/session management added to description and artifact_paths
(session.rs, auth.rs, login.rs).
New edges: unified-auth-model → ontoref-daemon, unified-auth-model → no-enforcement
(Contradicts/Low — auth is opt-in), ontoref-daemon → unified-auth-model (Contains),
project-onboarding → unified-auth-model (DependsOn),
project-onboarding → adopt-ontoref-tooling (Complements),
project-onboarding → daemon-config-management (DependsOn).
.ontology/state.ncl — self-description-coverage transitions to current_state = "fully-self-described".
Blocker resolved: reflection/backlog.ncl created, ADR-005 recorded.
reflection/backlog.ncl — created with 6 items: bl-001 (ontoref.dev), bl-002 (first external project),
bl-003 (ontoref keys CLI), bl-004 (session UI views), bl-005 (Syntaxis migration),
bl-006 (ADR-001 acceptance).
Previous state: 4 axioms, 2 tensions, 13 practices. Current: 4 axioms, 2 tensions, 15 practices.
Protocol
- ADR-004 accepted: NCL pipe bootstrap pattern —
nickel export config.ncl | ontoref-daemon.bin --config-stdin. Stages: Nickel evaluation → optional SOPS/Vault merge → binary via stdin. (adr-004) - ADR-005 accepted: unified key-to-session auth model across CLI, UI, and MCP. Opaque UUID v4 tokens,
session.id ≠ bearer, revocation on key rotation, daemon admin via_daemonslug. (adr-005)
Install Infrastructure
install/directory reorganized: binaries, bootstrapper, global CLI, resources, and validation scripts co-located.- Binary installed as
ontoref-daemon.bin; public entrypoint bootstrapper installed asontoref-daemon. Users never call.bindirectly. install/ontoref-daemon-boot(renamed fromontoref-daemon-start) — NCL pipe bootstrapper implementing ADR-004. Stages:nickel export config.ncl→ optional SOPS/Vault secret merge →ontoref-daemon.bin --config-stdin. Supports--dry-run,--sops,--vault.install/ontoref-daemon-bootsetsNICKEL_IMPORT_PATH(config dir + platform data dir schemas) andNATS_STREAMS_CONFIG(default~/.config/ontoref/streams.json) before launching the binary.install/install.nu— installs binary, bootstrapper, globalontorefCLI (ONTOREF_ROOT baked in at install time), UI assets, config skeleton, and global NATS topology. Hash-checked — unchanged files are not overwritten.install/config-setup.nu— standalone validation script:nickel typecheck+nickel export, path existence checks, DB and NATS liveness probes (nc -z -w2).install/check-config-sync.nu— CI guard asserting that everynickel_path-bearing field inreflection/forms/config.nclhas a matching{{ name }}reference inreflection/forms/config.ncl.j2, and vice versa. Wired intojust ci-lintandjust ci-full.
Config Management
install/resources/config.ncl— default global config skeleton with full Nickel contracts (Port,LogLevel,Rotation,Actor,Severity). Covers: daemon, db, nats_events, log, cache, ui, mode_run, actor_init, quick_actions, nickel_import_paths.install/resources/streams.json— global default NATS JetStream topology: ECOSYSTEM stream (ecosystem.>, 30-day retention), no project-specific consumers. Installed to~/.config/ontoref/streams.json.nats/streams.json— ontoref project-local topology withdaemon-ontorefandcli-notificationsconsumers on ECOSYSTEM stream.reflection/forms/config.ncl+reflection/forms/config.ncl.j2— typedialog roundtrip for browser-based config editing (ontoref config-edit). Form populates fields from existing config vianickel_path; Tera template reconstructs full NCL with all contracts on save.reflection/nulib/bootstrap.nu— Nu bootstrapper helper updated:nats-streams-configfunction resolvesNATS_STREAMS_CONFIGdefault; env var passed to daemon process viawith-env.- Daemon
nats.rs: emptystreams_configstring →None, activatingTopologyConfig::loadfallback toNATS_STREAMS_CONFIGenv var. Projects with a localnats/streams.jsonsetstreams_configexplicitly in their config.
Daemon Fixes
--config-stdinnow exclusively skips.ontoref/config.ncl— project config is never loaded when stdin config is active. Previously both paths could run.- DB connection (
stratum-db) only established whendb.enabled = truein config. Previously connected regardless ofenabledflag. - NATS connection (
platform-nats) only established whennats_events.enabled = true. "Connecting to NATS..." log moved after the enabled check. NatsPublisher::connectsignature changed fromconfig_path: &PathBuf(re-read file) toconfig: Option<&serde_json::Value>(already-loaded JSON). Eliminates double file read and ensures NATS uses the same config source as the rest of the daemon.load_config_overridesreturns(Option<String>, Option<serde_json::Value>)— nickel import path and parsed config JSON returned together.apply_stdin_configreturnsserde_json::Valuedirectly.
Self-Description — on+re Update
.ontology/core.ncl — 1 new Practice node, updated ontoref-daemon description and artifact_paths:
| Node | Pole | Description |
|---|---|---|
daemon-config-management |
Yang | Install + config form/template roundtrip, CI guard, global NATS topology, config-setup validation |
New edges: daemon-config-management → ontoref-daemon (DependsOn),
daemon-config-management → adopt-ontoref-tooling (Complements).
.ontology/state.ncl — operational-mode dimension description updated to reference ADR-004 bootstrap
and NATS_STREAMS_CONFIG mechanism. protocol-maturity blocker updated to reflect install pipeline
completion.
.ontology/manifest.ncl — tooling layer paths updated to include install/ and nats/.
Previous state: 4 axioms, 2 tensions, 12 practices. Current: 4 axioms, 2 tensions, 13 practices.
Protocol
- ADR-001 accepted: ontoref extracted as a standalone protocol project, independent of
stratumiops versioning and release cycle. Consumer projects adopt via
scripts/ontorefwrapper +.ontoref/config.ncl. (adr-001) - ADR-002 accepted:
ontoref-daemonintroduced as optional persistent daemon for NCL export caching (keyed by path+mtime), actor registry (developer/agent/CI), and notification barrier (pre-commit hook, fail-open). Supersedes stratumiops ADR-007. (adr-002) - ADR-003 accepted: Q&A and accumulated operational knowledge persist to
reflection/qa.ncl— typed NCL, git-versioned, accessible via MCP tools and HTTP endpoints. localStorage eliminated. Q&A entries survive session boundaries and are queryable by any actor. (adr-003)
Crates
ontoref-ontology: Rust crate for loading.ontology/*.nclas typed structs (Core,Gate,State). Zero stratumiops dependencies.ontoref-reflection: Rust crate for loading, validating, and executing Reflection modes as NCL DAG contracts. Optionalnatsfeature (path dep:platform-nats).ontoref-daemon: Rust crate providing HTTP API (axum), DashMap-backed NCL export cache, notify-based file watcher, and actor registry. Optionaldbfeature (path dep:stratum-db) andnatsfeature (path dep:platform-nats).
Daemon — Q&A NCL Persistence (crates/ontoref-daemon/src/ui/qa_ncl.rs)
Line-level NCL surgery for reflection/qa.ncl — same pattern as backlog_ncl.rs. No AST parsing,
no nickel-lang-core dependency.
add_entry— appends a typedQaEntryblock before],array close; generates sequentialqa-NNNidsupdate_entry— in-place field mutation via bidirectional scan (question + answer fields)remove_entry— removes the full block by id using backward scan for{and forward scan for},
HTTP endpoints (all under #[cfg(feature = "ui")] except read-only GET):
GET /qa-json— export all Q&A entries as JSON (read-only, always enabled)POST /qa/add— append new entry; returns generated idPOST /qa/delete— remove entry by id; invalidates NCL cachePOST /qa/update— mutate question + answer fields by id; invalidates NCL cacheGET /actions/run/POST /actions/run— execute a quick action by id; spawns./ontoref <mode>
Server-side hydration: qa.html receives entries as Tera context variable, embeds
SERVER_ENTRIES JSON literal in the page <script> — no fetch round-trip on load.
Daemon — MCP Tools (crates/ontoref-daemon/src/mcp/mod.rs)
Four new MCP tools exposed to AI agents:
| Tool | Description |
|---|---|
ontoref_qa_list |
List Q&A entries with optional filter substring match. Never triggers ontology sync. |
ontoref_qa_add |
Append a new Q&A entry to reflection/qa.ncl; invalidates NCL cache. |
ontoref_action_list |
List all quick actions from .ontoref/config.ncl export. |
ontoref_action_add |
Create a new reflection mode at reflection/modes/<id>.ncl and register it as a quick action. |
Constraint: ontoref_qa_list and ontoref_qa_add never trigger apply steps or modify .ontology/
files (enforced by ADR-003).
Daemon — Passive Drift Observation (crates/ontoref-daemon/src/ui/drift_watcher.rs)
Background observer bridging Yang code artifacts with Yin ontology declarations:
- Watches
crates/,.ontology/,adrs/,reflection/modes/vianotifyfile watcher - 15-second debounce window before triggering scan
- Spawns
./ontoref sync scan && ./ontoref sync diffas read-only subprocesses - Parses stdout for MISSING / STALE / DRIFT / BROKEN markers
- Emits
ontology_driftnotification viapush_customwhen any drift is found - Never applies changes automatically —
applyremains a deliberate human or agent act
Started from main.rs under #[cfg(feature = "ui")]; failure to start is non-fatal (logged as warning).
Tooling
reflection/modules/: 16 Nushell operational modules (adr.nu,backlog.nu,coder.nu,describe.nu,sync.nu, etc.)reflection/modes/: 10 NCL DAG operational modes includingadopt_ontoref,sync-ontology,coder-workflow,create-prreflection/forms/: 7 interactive NCL forms for ADR lifecycle, backlog, and adoptiontemplates/: Consumer-facing adoption templates (ontoref-config.ncl,ontology/,scripts-ontoref)./ontoref: Bash entry point with actor auto-detection, advisory file locking, and Nushell version guard (>= 0.110.0)
Self-Description — on+re Update
.ontology/core.ncl updated with 3 new Practice nodes and 9 new edges:
| Node | Pole | Description |
|---|---|---|
qa-knowledge-store |
Yin | Q&A entries as typed NCL — accumulated knowledge queryable by any actor |
quick-actions |
Yang | Runnable shortcuts over reflection modes; configured in .ontoref/config.ncl |
drift-observation |
Spiral | Passive bridge between Yang code artifacts and Yin ontology declarations |
New edges: qa-knowledge-store → dag-formalized, qa-knowledge-store → coder-process-memory,
ontoref-daemon → qa-knowledge-store, quick-actions → reflection-modes,
quick-actions → ontoref-daemon, describe-query-layer → quick-actions,
drift-observation → ontoref-daemon, drift-observation → ontology-vs-reflection,
drift-observation → reflection-modes.
Previous state: 4 axioms, 2 tensions, 9 practices. Current: 4 axioms, 2 tensions, 12 practices.
reflection/schemas/qa.ncl — QaStore and QaEntry types (id, question, answer, actor, created_at,
tags, related, verified).
reflection/qa.ncl — typed store file, conforms to QaStore contract, Nickel typecheck must pass.
ontoref uses its own ADR system to track decisions. Architectural rationale lives in adrs/, not in this file.