2026-03-13 00:21:04 +00:00
let m = import "../ontology/defaults/manifest.ncl" in
m.make_manifest {
2026-03-26 21:01:39 +00:00
project = "ontoref",
repo_kind = 'DevWorkspace,
description = "Protocol specification and tooling layer for structured self-knowledge in software projects. Provides schemas, Nushell automation, and Rust crates so projects can describe what they are, record architectural decisions, track operational state, and execute formalized procedures as typed, queryable artifacts.",
2026-03-13 00:21:04 +00:00
---
feat: API catalog surface, protocol v2 tooling, MCP expansion, on+re update
## Summary
Session 2026-03-23. Closes the loop between handler code and discoverability
across all three surfaces (browser, CLI, MCP agent) via compile-time inventory
registration. Adds protocol v2 update tooling, extends MCP from 21 to 29 tools,
and brings the self-description up to date.
## API Catalog Surface (#[onto_api] proc-macro)
- crates/ontoref-derive: new proc-macro crate; `#[onto_api(method, path,
description, auth, actors, params, tags)]` emits `inventory::submit!(ApiRouteEntry{...})`
at link time
- crates/ontoref-daemon/src/api_catalog.rs: `catalog()` — pure fn over
`inventory::iter::<ApiRouteEntry>()`, zero runtime allocation
- GET /api/catalog: returns full annotated HTTP surface as JSON
- templates/pages/api_catalog.html: new page with client-side filtering by
method, auth, path/description; detail panel per route (params table,
feature flag); linked from dashboard card and nav
- UI nav: "API" link (</> icon) added to mobile dropdown and desktop bar
- inventory = "0.3" added to workspace.dependencies (MIT, zero transitive deps)
## Protocol Update Mode
- reflection/modes/update_ontoref.ncl: 9-step DAG (5 detect parallel, 2 update
idempotent, 2 validate, 1 report) — brings any project from protocol v1 to v2
by adding manifest.ncl and connections.ncl if absent, scanning ADRs for
deprecated check_hint, validating with nickel export
- reflection/templates/update-ontology-prompt.md: 8-phase reusable prompt for
agent-driven ontology enrichment (infrastructure → audit → core.ncl →
state.ncl → manifest.ncl → connections.ncl → ADR migration → validation)
## CLI — describe group extensions
- reflection/bin/ontoref.nu: `describe diff [--fmt] [--file]` and
`describe api [--actor] [--tag] [--auth] [--fmt]` registered as canonical
subcommands with log-action; aliases `df` and `da` added; QUICK REFERENCE
and ALIASES sections updated
## MCP — two new tools (21 → 29 total)
- ontoref_api_catalog: filters catalog() output by actor/tag/auth; returns
{ routes, total } — no HTTP roundtrip, calls inventory directly
- ontoref_file_versions: reads ProjectContext.file_versions DashMap per slug;
returns BTreeMap<filename, u64> reload counters
- insert_mcp_ctx: audited and updated from 15 to 28 entries in 6 groups
- HelpTool JSON: 8 new entries (validate_adrs, validate, impact, guides,
bookmark_list, bookmark_add, api_catalog, file_versions)
- ServerHandler::get_info instructions updated to mention new tools
## Web UI — dashboard additions
- Dashboard: "API Catalog" card (9th); "Ontology File Versions" section showing
per-file reload counters from file_versions DashMap
- dashboard_mp: builds BTreeMap<String, u64> from ctx.file_versions and injects
into Tera context
## on+re update
- .ontology/core.ncl: describe-query-layer and adopt-ontoref-tooling descriptions
updated; ontoref-daemon updated ("11 pages", "29 tools", API catalog,
per-file versioning, #[onto_api]); new node api-catalog-surface (Yang/Practice)
with 3 edges; artifact_paths extended across 3 nodes
- .ontology/state.ncl: protocol-maturity blocker updated (protocol v2 complete);
self-description-coverage catalyst updated with session 2026-03-23 additions
- ADR-007: "API Surface Discoverability via #[onto_api] Proc-Macro" — Accepted
## Documentation
- README.md: crates table updated (11 pages, 29 MCP tools, ontoref-derive row);
MCP representative table expanded; API Catalog, Semantic Diff, Per-File
Versioning paragraphs added; update_ontoref onboarding section added
- CHANGELOG.md: [Unreleased] section with 4 change groups
- assets/web/src/index.html: tool counts 19→29 (EN+ES), page counts 12→11
(EN+ES), daemon description paragraph updated with API catalog + #[onto_api]
2026-03-23 00:58:27 +01:00
content_assets = [
m.make_asset {
id = "logo-horizontal",
kind = 'Logo,
source_path = "assets/branding/ontoref-h.svg",
variants = ["assets/branding/ontoref-h-static.svg", "assets/branding/ontoref-dark-h.svg", "assets/branding/ontoref-mono-black-h.svg", "assets/branding/ontoref-mono-white-h.svg"],
description = "Primary horizontal logo — animated SVG with static and dark/mono variants.",
},
m.make_asset {
id = "logo-vertical",
kind = 'Logo,
source_path = "assets/branding/ontoref-v.svg",
variants = ["assets/branding/ontoref-v-static.svg", "assets/branding/ontoref-dark-v.svg", "assets/branding/ontoref-mono-black-v.svg", "assets/branding/ontoref-mono-white-v.svg"],
description = "Vertical logo — animated SVG with static and dark/mono variants.",
},
m.make_asset {
id = "logo-icon",
kind = 'Icon,
source_path = "assets/branding/ontoref-icon.svg",
variants = ["assets/branding/ontoref-icon-static.svg"],
description = "Square icon mark — animated and static variants.",
},
m.make_asset {
id = "logo-text",
kind = 'Logo,
source_path = "assets/branding/ontoref-text.svg",
description = "Logotype text-only mark.",
},
m.make_asset {
id = "logo-pakua",
kind = 'Logo,
source_path = "assets/branding/pakua/ontoref_pakua_img.svg",
variants = ["assets/branding/pakua/ontoref-pakua-dark-v.svg"],
description = "Pakua symbol variant of the logo.",
},
m.make_asset {
id = "diagram-architecture",
kind = 'Diagram,
source_path = "assets/architecture.svg",
description = "Current architecture diagram showing the three-layer protocol model.",
},
m.make_asset {
id = "screenshot-graph-dark",
kind = 'Screenshot,
source_path = "assets/ontoref_graph_view-dark.png",
variants = ["assets/ontoref_graph_view-light.png"],
description = "Graph view UI screenshot — dark and light variants.",
},
m.make_asset {
id = "presentation-deck",
kind = 'Document,
source_path = "assets/presentation/slides.md",
description = "Slidev presentation deck for ontoref protocol introduction.",
},
],
2026-03-13 00:21:04 +00:00
consumption_modes = [
m.make_consumption_mode {
consumer = 'Developer,
needs = ['OntologyExport],
audit_level = 'Standard,
description = "Clones repo, runs ./ontoref, imports Nushell modules. Uses reflection tooling to manage project self-description.",
},
m.make_consumption_mode {
consumer = 'Agent,
needs = ['OntologyExport, 'JsonSchema],
audit_level = 'Quick,
description = "Reads .ontology/core.ncl via nickel export. Queries nodes, edges, and ADRs to understand project constraints before acting.",
},
],
feat: config surface, NCL contracts, override-layer mutation, on+re update
Config surface — per-project config introspection, coherence verification, and
audited mutation without destroying NCL structure (ADR-008):
- crates/ontoref-daemon/src/config.rs — typed DaemonNclConfig (parse-at-boundary
pattern); all section structs derive ConfigFields + config_section(id, ncl_file)
emitting inventory::submit!(ConfigFieldsEntry{...}) at link time
- crates/ontoref-derive/src/lib.rs — #[derive(ConfigFields)] proc-macro; serde
rename support; serde_rename_of() helper extracted to fix excessive_nesting
- crates/ontoref-daemon/src/main.rs — 3-tuple bootstrap block (nickel_import_path,
loaded_ncl_config: Option<DaemonNclConfig>, stdin_raw); apply_ui_config takes
&UiConfig; NATS call site typed; resolve_asset_dir cfg(feature = "ui")
- crates/ontoref-daemon/src/api.rs — config GET/PUT endpoints, quickref, coherence,
cross-project comparison; index_section_fields() extracted (excessive_nesting)
- crates/ontoref-daemon/src/config_coherence.rs — multi-consumer coherence;
merge_meta_into_section() extracted; and() replaces unnecessary and_then
NCL contracts for ontoref's own config:
- .ontoref/contracts.ncl — LogConfig (LogLevel, LogRotation, PositiveInt) and
DaemonConfig (Port, optional overrides); std.contract.from_validator throughout
- .ontoref/config.ncl — log | C.LogConfig applied
- .ontology/manifest.ncl — contracts_path, log/daemon contract refs, daemon section
with DaemonRuntimeConfig consumer and 7 declared fields
Protocol:
- adrs/adr-008-ncl-first-config-validation-and-override-layer.ncl — NCL contracts
as single validation gate; Rust structs are contract-trusted; override-layer
mutation writes {section}.overrides.ncl + _overrides_meta, never touches source
on+re update:
- .ontology/core.ncl — config-surface node (28 practices); adr-lifecycle extended
to adr-007 + adr-008; 6 new edges (ManifestsIn daemon, DependsOn ontology-crate,
Complements api-catalog-surface/dag-formalized/self-describing/adopt-ontoref)
- .ontology/state.ncl — protocol-maturity blocker and self-description-coverage
catalyst updated for session 2026-03-26
- README.md / CHANGELOG.md updated
2026-03-26 20:20:22 +00:00
config_surface = m.make_config_surface {
config_root = ".ontoref",
entry_point = "config.ncl",
kind = 'SingleFile,
contracts_path = ".ontoref",
sections = [
m.make_config_section {
id = "nickel_import_paths",
file = "config.ncl",
description = "Ordered list of directories added to NICKEL_IMPORT_PATH when invoking nickel.",
rationale = "Ontoref resolves ontology schemas, ADRs, and reflection schemas through this path list. Order matters: earlier entries shadow later ones.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "env",
kind = 'NuScript,
ref = "reflection/modules/env.nu",
fields = ["nickel_import_paths"],
},
m.make_config_consumer {
id = "daemon-main",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/main.rs",
fields = ["nickel_import_paths"],
},
],
},
m.make_config_section {
id = "ui",
file = "config.ncl",
description = "Daemon HTTP/UI settings: template directory, static assets, TLS certs, logo override.",
rationale = "Allows dev-mode templates to be served from the source tree instead of the installed path, and TLS to be toggled without recompiling.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "daemon-main",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/main.rs",
fields = ["templates_dir", "public_dir", "tls_cert", "tls_key", "logo"],
},
],
},
m.make_config_section {
id = "log",
file = "config.ncl",
contract = "contracts.ncl → LogConfig",
description = "Daemon structured logging: level, rotation policy, archive and retention.",
rationale = "Daily rotation with 7-file retention keeps log footprint bounded; separate archive path allows cold storage without disrupting active logs.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "daemon-main",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/main.rs",
fields = ["level", "path", "rotation", "compress", "archive", "max_files"],
},
],
},
m.make_config_section {
id = "mode_run",
file = "config.ncl",
description = "ACL rules for which actors may execute which reflection modes.",
rationale = "Agent and CI actors need unrestricted mode access; human actors are gated per mode to prevent accidental destructive operations.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "daemon-main",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/main.rs",
fields = ["rules"],
},
],
},
m.make_config_section {
id = "nats_events",
file = "config.ncl",
description = "NATS event bus integration: enabled flag, server URL, emit/subscribe topic lists, handlers directory.",
rationale = "Disabled by default to keep ontoref zero-dependency for projects without a NATS deployment. Feature-gated in the daemon crate.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "daemon-main",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/main.rs",
fields = ["enabled", "url", "emit", "subscribe", "handlers_dir"],
},
],
},
m.make_config_section {
id = "actor_init",
file = "config.ncl",
description = "Per-actor bootstrap: which reflection mode to auto-run on first invocation.",
rationale = "Agents always auto-run 'describe capabilities' so they orient themselves before acting; developers and CI start clean.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "env",
kind = 'NuScript,
ref = "reflection/modules/env.nu",
fields = ["actor", "mode", "auto_run"],
},
],
},
m.make_config_section {
id = "quick_actions",
file = "config.ncl",
description = "Shortcut actions surfaced in the daemon UI dashboard: id, label, icon, category, mode, allowed actors.",
rationale = "Frequently used modes (generate-mdbook, sync-ontology, coder-workflow) promoted to one-click access without navigating the modes list.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "daemon-ui",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/ui/handlers.rs",
fields = ["id", "label", "icon", "category", "mode", "actors"],
},
],
},
m.make_config_section {
id = "daemon",
file = "config.ncl",
contract = "contracts.ncl → DaemonConfig",
description = "Runtime overrides for daemon CLI defaults: port, timeouts, sweep intervals, notification limits.",
rationale = "All fields are optional — absent fields use the daemon's built-in CLI defaults. Set only when the defaults need project-specific tuning without rebuilding the binary.",
mutable = true,
consumers = [
m.make_config_consumer {
id = "daemon-config",
kind = 'RustStruct,
ref = "crates/ontoref-daemon/src/config.rs → DaemonRuntimeConfig",
fields = ["port", "idle_timeout", "invalidation_interval", "actor_sweep_interval", "actor_stale_timeout", "max_notifications", "notification_ack_required"],
},
],
},
],
},
2026-03-26 21:01:39 +00:00
capabilities = [
m.make_capability {
id = "protocol-spec",
name = "Protocol Specification",
summary = "Typed NCL schemas for nodes, edges, ADRs, state, gates, and manifests.",
rationale = "Projects need a contract layer to describe what they are — not just code comments. NCL provides typed, queryable, git-versionable schemas with contract enforcement at export time. Alternatives (TOML/JSON/YAML) lack contracts; Rust-only structs are not adoption-friendly.",
how = "ontology/schemas/ defines all type contracts. adrs/adr-schema.ncl defines the ADR lifecycle contract. ontology/defaults/ exposes builders (make_node, make_edge, make_adr) so consumer projects never write raw NCL records. nickel export validates against declared contracts before any JSON reaches Rust or Nushell.",
artifacts = ["ontology/schemas/", "ontology/defaults/", "adrs/adr-schema.ncl", "adrs/adr-defaults.ncl"],
nodes = ["dag-formalized", "protocol-not-runtime", "adr-lifecycle"],
},
m.make_capability {
id = "daemon-api",
name = "Daemon HTTP + MCP Surface",
summary = "HTTP UI (11 pages), 29 MCP tools, annotated API catalog, Q&A store, search bookmarks, config surface, per-file versioning.",
rationale = "Agents and developers need a queryable interface to ontology state without spawning nickel on every request. The NCL export cache reduces full-sync from ~2m42s to <30s. 29 MCP tools give agents structured access to every capability without screen-scraping the CLI. ADR-002 records the architectural decision to extract the daemon; ADR-007 covers the #[onto_api] catalog pattern; ADR-008 covers the config override layer.",
how = "crates/ontoref-daemon uses axum for HTTP. #[onto_api(...)] proc-macro + inventory::submit! registers every route at link time; GET /api/catalog aggregates via inventory::collect!. NclCache (DashMap<PathBuf, CachedExport>) keyed on path + mtime. File watcher (notify) triggers cache invalidation and drift detection after 15s debounce. MCP over stdio and streamable-HTTP.",
artifacts = [
"crates/ontoref-daemon/",
"GET /api/catalog",
"GET /projects/{slug}/ontology",
"GET /projects/{slug}/config/coherence",
"MCP: ontoref_guides, ontoref_api_catalog, ontoref_validate, ontoref_impact",
],
adrs = ["adr-002", "adr-004", "adr-007", "adr-008"],
nodes = ["ontoref-daemon", "api-catalog-surface", "config-surface"],
},
m.make_capability {
id = "reflection-modes",
name = "Reflection Mode Executor",
summary = "NCL DAG workflow engine executing ADR lifecycle, project adoption, content modes, and operational procedures.",
rationale = "Structured procedures expressed as typed DAGs rather than ad-hoc scripts. Every step has a declared dep graph — the executor validates it before running. Agent-safe: modes are NCL contracts, not imperative scripts, so agents can read and reason about them before execution.",
how = "crates/ontoref-reflection loads a mode NCL file, validates the DAG contract (no cycles, declared deps exist), then executes steps via Nushell subprocesses. reflection/modules/ contains 16 Nushell modules that implement the actual step logic. reflection/modes/ contains the typed NCL DAG definitions.",
artifacts = ["reflection/modes/", "reflection/modules/", "crates/ontoref-reflection/"],
nodes = ["reflection-modes", "adopt-ontoref-tooling"],
},
],
requirements = [
m.make_requirement {
id = "nushell",
name = "Nushell",
env = 'Both,
kind = 'Tool,
version = "0.110.0",
required = true,
impact = "All reflection modes and the ./ontoref dispatcher are Nushell scripts. Without Nu nothing executes — no mode runs, no describe subcommands, no sync.",
provision = "https://www.nushell.sh/ — cargo install nu or OS package manager.",
},
m.make_requirement {
id = "nickel",
name = "Nickel",
env = 'Both,
kind = 'Tool,
version = "",
required = true,
impact = "All schema evaluation, ADR parsing, config export blocked. Daemon NCL cache inoperable. Config surface mutation cannot validate overrides.",
provision = "https://nickel-lang.org/ — cargo install nickel-lang-cli or nix flake.",
},
m.make_requirement {
id = "rust-nightly",
name = "Rust nightly toolchain",
env = 'Development,
kind = 'Tool,
version = "",
required = true,
impact = "cargo +nightly fmt fails — pre-commit hook blocks all commits.",
provision = "rustup toolchain install nightly",
},
m.make_requirement {
id = "surrealdb",
name = "SurrealDB",
env = 'Production,
kind = 'Service,
version = "",
required = false,
impact = "Daemon db feature disabled; ontology not projected into DB. Daemon still works via --no-default-features for local-only use.",
provision = "https://surrealdb.com/ — binary or container. Feature-gated: cargo build -p ontoref-daemon --no-default-features omits it.",
},
m.make_requirement {
id = "stratumiops",
name = "stratumiops repo checkout",
env = 'Development,
kind = 'Infrastructure,
version = "",
required = false,
impact = "ontoref-daemon (db/nats features) and ontoref-reflection (nats feature) cannot build. Build with --no-default-features to work without it.",
provision = "git clone at ../../../stratumiops relative to this repo root.",
},
],
critical_deps = [
m.make_critical_dep {
id = "nickel-lang",
name = "nickel-lang",
ref = "crates.io: nickel-lang-core / nickel-lang-cli",
used_for = "Schema evaluation, contract enforcement, config export, ADR parsing. Every nickel export call in the daemon cache and in Nushell modules.",
failure_impact = "Total loss of typed schema layer. All NCL export operations fail. Daemon cache inoperable. Config override mutation cannot validate before committing. describe guides and ontoref_guides MCP tool return empty data.",
mitigation = "Pin to a specific Nickel release in PATH. Nickel is a subprocess dep (never linked into Rust binary) — breakage manifests at runtime as nickel export exit code != 0, which all callers handle gracefully (daemon-export-safe returns null, callers use | default []).",
},
m.make_critical_dep {
id = "inventory",
name = "inventory",
ref = "crates.io: inventory 0.3",
used_for = "#[onto_api] HTTP catalog registration and #[derive(ConfigFields)] config coherence registry. Both use inventory::submit! at link time; GET /api/catalog and GET /config/coherence use inventory::iter! at runtime.",
failure_impact = "GET /api/catalog returns empty. Config coherence endpoint loses Rust struct field data. ontoref_api_catalog MCP tool blind. API version change would require updating both onto_api and ConfigFields derive macros simultaneously.",
mitigation = "inventory uses linker sections (zero runtime overhead). Version is pinned in Cargo.toml. ontoref-derive and ontoref-ontology both declare it — version must stay in sync.",
},
m.make_critical_dep {
id = "axum",
name = "axum",
ref = "crates.io: axum",
used_for = "All 11 UI pages and REST API endpoints in ontoref-daemon. Router, handlers, extractors, middleware.",
failure_impact = "Daemon does not compile. Full HTTP surface down: UI, REST API, MCP over HTTP, session management, config surface endpoints.",
mitigation = "ontoref-ontology and the ./ontoref CLI are axum-free. Reflection modes, ADR tooling, and describe subcommands continue working without the daemon.",
},
],
2026-03-13 00:21:04 +00:00
layers = [
m.make_layer {
id = "protocol",
paths = ["ontology/", "adrs/", "reflection/schemas/"],
committed = true,
description = "Protocol specification: Nickel schemas, ADR tooling, and reflection schemas. The contract layer that projects implement.",
},
m.make_layer {
id = "tooling",
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
paths = ["reflection/", "install/", "nats/", "templates/", "ontoref"],
2026-03-13 00:21:04 +00:00
committed = true,
feat: unified auth model, project onboarding, install pipeline, config management
The full scope across this batch: POST /sessions key→token exchange, SessionStore dual-index with revoke_by_id, CLI Bearer injection (ONTOREF_TOKEN), ontoref setup
--gen-keys, install scripts, daemon config form roundtrip, ADR-004/005, on+re self-description update (fully-self-described), and landing page refresh.
2026-03-13 20:56:31 +00:00
description = "Operational tooling: Nushell modules, modes, forms, dispatcher, bash entry point, install scripts, default config resources, and NATS stream topology.",
2026-03-13 00:21:04 +00:00
},
m.make_layer {
id = "crates",
paths = ["crates/", "Cargo.toml"],
committed = true,
description = "Rust implementation: ontoref-ontology (load/query .ontology/ as typed structs) and ontoref-reflection (execute reflection modes against project state).",
},
m.make_layer {
id = "self-description",
paths = [".ontology/"],
committed = true,
description = "Ontoref consuming ontoref: the project's own ontology, state, gate, and manifest.",
},
m.make_layer {
id = "process",
paths = [".coder/"],
committed = false,
description = "Session artifacts: plans, investigations, summaries. Process memory for actors.",
},
],
}