graph, search, api_catalog pages: back/forward history stack (PanelNav/dpNav). File artifact paths open in external tabs via card.repo (Gitea source URL) or card.docs (cargo docs for .rs) — openFile/openFileInPanel removed from all pages. Tera | safe required for URL values inside <script> blocks (auto-escape of slashes). card.ncl: repo field added. insert_brand_ctx: injects card_repo/card_docs into Tera context. #[onto_api] proc-macro: source_file = file!() emitted; ApiRouteEntry.source_file populated in primary catalog handler. migration 0007-card-repo-field: check card.ncl for repo field; skip if absent.
42 KiB
Changelog
All notable changes to ontoref are documented here.
ADRs referenced below live in adrs/ as typed Nickel records.
[Unreleased]
Browser-style panel navigation + repo file routing
Graph, search, and api_catalog pages now share a uniform browser-style navigation model: back/forward history stack with cursor-into-array semantics. File artifact paths open in external browser tabs rather than being loaded inline.
crates/ontoref-daemon/templates/pages/graph.html
.artifact-linkclick handler changed from removedopenFile()tosrcOpen().panelNav._replaytype: "file"case changed tosrcOpen(e.id).
crates/ontoref-daemon/templates/pages/search.html
openFileInPanelasync function removed entirely (was loading file content inline via/api/file).srcOpen(path)function added: opens{card_repo}/src/branch/main/{path}for most files; openscard_docsfor.rsfiles when configured.CARD_REPO/CARD_DOCSJS constants injected via Tera (| safefilter required — Tera auto-escapes all{{ }}interpolations regardless of<script>context)..s-file-linkclick delegation updated to callsrcOpen.dpNav._replaytype: "file"case callssrcOpen.
crates/ontoref-daemon/templates/pages/api_catalog.html
ensureFileModalandopenFileremoved.srcOpenadded with same logic as graph and search pages.CARD_REPO/CARD_DOCSconstants injected.#detail-source-btnclick delegation callssrcOpen.
crates/ontoref-daemon/src/ui/handlers.rs
insert_brand_ctxreadscard.repoandcard.docsfrom config NCL and injectscard_repo/card_docsinto the Tera context for all pages.
card.ncl
repo = "https://repo.jesusperez.pro/jesus/ontoref"added.
reflection/migrations/0007-card-repo-field.ncl — new migration
- Check:
card.nclabsent → pass (not applicable); present +repo =found → pass; present withoutrepo =→ pending. - Instructions: add
repoand optionallydocsfields; explains howsrcOpenuses both.
on+re update
| Artifact | Change |
|---|---|
.ontology/core.ncl |
ontoref-daemon description updated — browser-style panel nav, file routing via card.repo/card.docs |
.ontology/state.ncl |
self-description-coverage catalyst updated with session 2026-03-29 |
Protocol Migration System — progressive NCL checks for consumer project upgrades (ADR-010)
Replaces the template-prompt approach with an ordered, idempotent migration system. Applied state
determined by check result alone — no state file. 6 migrations shipped; runtime ships
migrate list/pending/show with interactive group dispatch.
reflection/migrations/ — 6 ordered migrations
0001-ontology-infrastructure—.ontology/manifest.nclandconnections.nclpresent.0002-adr-typed-checks— nocheck_hintin ADR instance files (adr-[0-9][0-9][0-9]-*.ncl); check narrowed fromadrs/broad scan to exclude schema/template infrastructure files.0003-manifest-self-interrogation—capabilities[]andrequirements[]non-empty in manifest.ncl.0004-just-convention— justfile validates against canonical module convention (pending in this repo — documented gap).0005-mode-step-schema— all reflection mode steps declareactor,on_error,depends_on.0006-claude-agent-entrypoint—Agent Entry-Point Protocolsection present in.claude/CLAUDE.md.
All NuCmd checks rewritten from bash to valid Nushell: && removed, $env.VAR replacing $VAR,
no bash-style redirects. Grep checks on ADR files use adr-[0-9][0-9][0-9]-*.ncl glob.
reflection/modules/migrate.nu — new module
migrate list [--fmt] [--actor]— all migrations with applied/pending status; JSON for agents.migrate pending [--fmt] [--actor]— pending only.migrate show <id> [--fmt]— runtime-interpolated instructions; accepts short ids (002→0002).- Applied state:
run-migration-checkdispatches overFileExists | Grep | NuCmd. - No state file — idempotent by construction.
reflection/nulib/interactive.nu + help.nu — migrate group wired
group-command-info—migratecase added (list, pending, show).run-group-command—migratedispatch added.help-group—migratehelp section added; fallback "Available groups" updated.
reflection/bin/ontoref.nu — shims + aliases
main migrate,main migrate list/pending/showadded.- Short aliases:
mg,mg l,mg p.
reflection/schemas/justfile-convention.ncl — export fix
- Removed
ModuleandModuleSystemfrom the exported record (open contract fields with no default value causednickel exportto fail). Both remain asletbindings for internal NCL use.
on+re update
| Artifact | Change |
|---|---|
adrs/adr-010-...ncl |
Created — protocol migration system, progressive NCL checks |
.ontology/core.ncl |
protocol-migration-system node added; adopt-ontoref-tooling artifacts updated; adr-lifecycle updated with ADR-010; 4 new edges |
.ontology/state.ncl |
protocol-maturity catalyst updated (10 consumers, all features complete); blocker narrowed to ontoref.dev not yet published |
Manifest Self-Interrogation Layer — capabilities, requirements, critical deps (ADR-009)
Three new typed arrays in manifest_type answering operational self-knowledge queries distinct from
ontology Practice nodes. describe requirements new subcommand; describe guides extended.
ontology/schemas/manifest.ncl — three new types
capability_type—id,name,summary,rationale,how,artifacts[],adrs[],nodes[].nodes[]cross-references ontology node IDs;adrs[]cross-references ADR IDs.env_target_type—'Production | 'Development | 'Bothclassification axis for requirements.requirement_kind_type—'Tool | 'Service | 'EnvVar | 'Infrastructure.requirement_type—id,name,env,kind,version,required,impact,provision.critical_dep_type—id,name,ref,used_for,failure_impact(required),mitigation.manifest_typegainsdescription | String | default = ""(bug fix —collect-identitywas reading a field that didn't exist),capabilities[],requirements[],critical_deps[](alldefault = []).- New exports:
EnvTarget,RequirementKind,Capability,Requirement,CriticalDep.
ontology/defaults/manifest.ncl — three new builders
make_capability,make_requirement,make_critical_depadded alongside existing builders.- New type re-exports:
EnvTarget,RequirementKind,Capability,Requirement,CriticalDep.
reflection/modules/describe.nu — new subcommand + extended outputs
describe requirements— renders requirements grouped by env (Production / Development / Both) and critical deps table (name, ref, used_for, failure_impact, mitigation).--environmentflag filters.describe capabilitiesextended — loadsmanifest.capabilities?and renders aPROJECT CAPABILITIES (manifest)section with name, summary, artifacts per entry.describe guidesoutput gainscapabilities,requirements,critical_depskeys — agents on cold start viaontoref_guidesMCP tool receive full self-interrogation context without extra tool calls.- Bug fix:
collect-identitywas readingmanifest.kind?(field absent from schema, always returned"") — changed tomanifest.repo_kind?. Same fix formanifest.description?(now exists).
.ontology/manifest.ncl — ontoref self-described
descriptionfield populated.- 3 capabilities:
protocol-spec,daemon-api,reflection-modes— each with rationale, how, artifacts, adrs, nodes cross-references. - 5 requirements:
nushell(both),nickel(both),rust-nightly(dev),surrealdb(prod optional),stratumiops(dev optional) — each with impact and provision. - 3 critical deps:
nickel-lang,inventory,axum— each with failure_impact and mitigation.
on+re update
| Artifact | Change |
|---|---|
adrs/adr-009-...ncl |
Created — manifest self-interrogation layer, three semantic axes |
.ontology/core.ncl |
manifest-self-description node added (29 nodes, 59 edges); adr-lifecycle updated with ADR-009 |
.ontology/state.ncl |
protocol-maturity blocker + self-description-coverage catalyst updated |
Config Surface — typed config, NCL contracts, override-layer mutation
Per-project config introspection, coherence verification, and audited mutation. NCL contracts are the single validation gate; config mutation never modifies source NCL files.
crates/ontoref-daemon/src/config.rs — typed DaemonNclConfig (parse-at-boundary)
DaemonNclConfig— top-level deserialize target fornickel export .ontoref/config.ncl | daemon --config-stdin; fields:nickel_import_paths,ui: UiConfig,log: LogConfig,mode_run: ModeRunConfig,nats_events: NatsEventsConfig,actor_init: Vec<ActorInit>,quick_actions: Vec<QuickAction>,daemon: DaemonRuntimeConfig.#[cfg(feature = "db")] db: DbConfig. All#[serde(default)].- Each section struct derives
#[derive(Deserialize, ConfigFields)]+#[config_section(id, ncl_file)]— emitsinventory::submit!(ConfigFieldsEntry{...})at link time. DaemonRuntimeConfig— optional port, timeouts, sweep intervals,notification_ack_required: Vec<String>.
crates/ontoref-daemon/src/main.rs — 3-tuple bootstrap block
- Bootstrap block changed to
(nickel_import_path, loaded_ncl_config, stdin_raw)—loaded_ncl_config: Option<DaemonNclConfig>replaces rawOption<serde_json::Value>.stdin_raw: Option<serde_json::Value>retained only for service-modeprojectsextraction. apply_stdin_confignow deserializes JSON toDaemonNclConfigbefore applying CLI overrides;apply_ui_configsignature changed from&serde_json::Valueto&UiConfig.load_config_overridesreturns(Option<String>, Option<DaemonNclConfig>)— all.get("daemon").and_then(...)chains replaced with typed field access (ncl.daemon.port, etc.).- NATS call site updated to
loaded_ncl_config.as_ref().map(|c| &c.nats_events). resolve_asset_dirgated with#[cfg(feature = "ui")];#[allow(unused_variables)]on bootstrap tuple for--no-default-features.
crates/ontoref-derive/src/lib.rs — #[derive(ConfigFields)] macro
- New
proc_macro_deriveConfigFieldswith helper attributeconfig_section(id, ncl_file). Extracts serde-renamed field names; emitsinventory::submit!(ConfigFieldsEntry{section_id, ncl_file, struct_name, fields}). - Extracted
serde_rename_of(field)helper to fixclippy::excessive_nesting(depth was 6). Field names collected via.iter().map(|f| serde_rename_of(f).unwrap_or_else(|| f.ident...)).filter(|s| !s.is_empty()).
crates/ontoref-daemon/src/config_coherence.rs — clippy fixes
and_then(|_| full_export.as_ref())→.and(full_export.as_ref())(unnecessary lazy evaluation).- Extracted
merge_meta_into_sectionhelper to reduce nesting depth for_meta_*record merging.
crates/ontoref-daemon/src/api.rs — index_section_fields helper
- Extracted
index_section_fieldsto fixclippy::excessive_nestingat the cross-project field indexing loop. Skips_meta_*and_overrides_metakeys; indexes(section_id, field) → Vec<(slug, value)>.
.ontoref/contracts.ncl — new file
NCL contracts for ontoref's own config sections using std.contract.from_validator (not the deprecated fun label value => pattern):
LogLevel— enum validator:error | warn | info | debug | traceLogRotation— enum validator:daily | hourly | neverPositiveInt—value > 0 && is_numberPort—value >= 1 && value <= 65535LogConfig— applies per-field contracts + defaults (level = "info",rotation = "daily",max_files = 7)DaemonConfig— all fields optional (override-only); port, timeouts, intervals,notification_ack_required
.ontoref/config.ncl — contracts applied
let C = import "contracts.ncl"added at top.log | C.LogConfig = { ... }— contract enforced before JSON reaches Rust.
.ontology/manifest.ncl — config surface enriched
contracts_path = ".ontoref"added toconfig_surface.logsection:contract = "contracts.ncl → LogConfig"added.- New
daemonsection:contract = "contracts.ncl → DaemonConfig", consumerdaemon-configpointing tocrates/ontoref-daemon/src/config.rs → DaemonRuntimeConfigwith 7 declared fields.
Protocol
- ADR-007 extended:
#[derive(ConfigFields)]is a second application of theinventory::submit!/inventory::collect!linker registration pattern first established by#[onto_api]. Both are now referenced from theconfig-surfacenode. - ADR-008 accepted: NCL-first config validation and override-layer mutation. NCL contracts are the single validation gate;
Rust structs are contract-trusted with
#[serde(default)]. Config mutation writes{section}.overrides.nclwith_overrides_metaaudit record; original NCL source files are never modified. nickel export validates the merged result before commit; contract violations revert the override file. (adr-008)
Self-Description — on+re Update
.ontology/core.ncl — new Practice node, updated nodes, 6 new edges:
| Change | Detail |
|---|---|
New node config-surface |
Yang — typed DaemonNclConfig, ConfigFields inventory registry, override-layer mutation API, NCL contracts, multi-consumer manifest schema; adrs = ["adr-007", "adr-008"] |
Updated adr-lifecycle |
ADR-007 + ADR-008 added to artifact_paths and adrs list (now 8 ADRs) |
New edges: config-surface → ontoref-daemon (ManifestsIn/High),
config-surface → ontoref-ontology-crate (DependsOn/High — ConfigFieldsEntry lives in zero-dep crate),
config-surface → api-catalog-surface (Complements/High — shared inventory pattern),
config-surface → dag-formalized (ManifestsIn/High),
config-surface → self-describing (Complements/High — ontoref validates its own config with its own contracts),
config-surface → adopt-ontoref-tooling (Complements/Medium).
.ontology/state.ncl — protocol-maturity blocker updated to record config surface completion.
self-description-coverage catalyst updated with session 2026-03-26 additions.
Previous: 4 axioms, 2 tensions, 27 practices. Current: 4 axioms, 2 tensions, 28 practices.
API Catalog Surface — #[onto_api] proc-macro
Annotated HTTP surface discoverable at compile time via inventory.
crates/ontoref-derive/src/lib.rs—#[proc_macro_attribute] onto_api(method, path, description, auth, actors, params, tags)emitsinventory::submit!(ApiRouteEntry{...})for each handler; auth validated at compile time (none | viewer | admin); param entries parsed asname:type:constraint:descriptionsemicolon-delimitedcrates/ontoref-daemon/src/api_catalog.rs—ApiRouteEntry+ApiParamstructs (&'static strfields for process lifetime);inventory::collect!(ApiRouteEntry);catalog()returns sortedVec<&'static ApiRouteEntry>GET /api/catalog— annotated with#[onto_api]; returns all registered routes as JSON sorted by path+method; no auth requiredGET /projects/{slug}/ontology/versions— per-file reload counters asBTreeMap<filename, u64>; counter bumped on every watcher-triggered NCL cache invalidationdescribe api [--actor] [--tag] [--auth] [--fmt json|text]— queries/api/catalog, groups by first tag, renders auth badges, param detail per route; available asonref daaliasdescribe diff [--file <ncl>] [--fmt json|text]— semantic diff of.ontology/files vs HEAD viagit show HEAD:<rel> | mktemp | nickel export; diffs nodes by id, edges byfrom→to[kind]key; available asonref dfaliasontoref_api_catalogMCP tool — callsapi_catalog::catalog()directly; filters by actor/tag/auth; returns{ routes, total }ontoref_file_versionsMCP tool — readsProjectContext.file_versionsDashMap; returns per-filename counters- Web UI:
/{slug}/apipage — table with client-side filtering (path, auth, method) + expandable detail panel; linked from nav and dashboard - Dashboard: "Ontology File Versions" section showing per-file counters; "API Catalog" card
insert_mcp_ctxinhandlers.rsupdated: 15 → 28 tools (previously stale for qa, bookmark, action, ontology extensions, validate, impact, guides)HelpToolJSON updated: 8 entries added (validate_adrs, validate, impact, guides, bookmark_list, bookmark_add, api_catalog, file_versions)MCP ServerHandler::get_info()instructions updated to mentionontoref_guides,ontoref_api_catalog,ontoref_file_versions,ontoref_validate
Protocol Update Mode
reflection/modes/update_ontoref.ncl— new mode bringing existing ontoref-adopted projects to protocol v2; 9-step DAG: 5 parallel detect steps (manifest, connections, ADR check_hint scan, ADRs missing check, daemon /api/catalog probe), 2 parallel update steps (add-manifest, add-connections — both idempotent viatest -f || sed), 2 validate steps (nickel export with explicit import paths), 1 aggregate report steptemplates/ontology/manifest.ncl— consumer-project stub; importsontology/defaults/manifest.nclvia import-path-relative resolutiontemplates/ontology/connections.ncl— consumer-project stub; importsconnectionsschema; empty upstream/downstream/peers with format docsreflection/modes/adopt_ontoref.ncl— updated: addscopy_ontology_manifestandcopy_ontology_connectionssteps (parallel,'Continue, idempotent);validate_ontologydepends on both with'Alwaysreflection/templates/update-ontology-prompt.md— 8-phase reusable prompt for full ontology enrichment: infrastructure update, audit, core.ncl nodes/edges, state.ncl dimensions, manifest.ncl assets, connections.ncl cross-project, ADR migration, final validation
CLI — describe group extensions and aliases
main describe diffandmain describe apiwrappers inreflection/bin/ontoref.numain d diff,main d api— short aliases withindgroupmain df,main da— toplevel aliases (consistent withd,ad,bklpattern)- QUICK REFERENCE:
describe diff,describe api,run update_ontorefentries added help describedescription updated to includediff, api surface
Self-Description — on+re Update
.ontology/core.ncl — 1 new Practice node, 3 updated nodes, 3 new edges:
| Change | Detail |
|---|---|
New node api-catalog-surface |
Yang — #[onto_api] proc-macro + inventory catalog; GET /api/catalog; describe api; ApiCatalogTool; /ui/{slug}/api page |
Updated describe-query-layer |
Description extended: describe diff (semantic vs HEAD) and describe api (annotated surface) |
Updated adopt-ontoref-tooling |
Description extended: update_ontoref mode, manifest/connections templates, enrichment prompt; artifact_paths updated |
Updated ontoref-daemon |
11 pages, 29 MCP tools, per-file versioning, API catalog endpoint; artifact_paths: api_catalog.rs, api_catalog.html, crates/ontoref-derive/ |
New edge api-catalog-surface → ontoref-daemon |
ManifestsIn/High |
New edge api-catalog-surface → describe-query-layer |
Complements/High |
New edge api-catalog-surface → protocol-not-runtime |
Complements/Medium — catalog is link-time, no runtime |
.ontology/state.ncl — self-description-coverage catalyst updated (session 2026-03-23).
protocol-maturity blocker updated to reflect protocol v2 completeness.
Previous: 4 axioms, 2 tensions, 20 practices. Current: 4 axioms, 2 tensions, 21 practices.
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.