ontoref/adrs/adr-003-qa-and-knowledge-persistence-as-ncl.ncl

111 lines
7.8 KiB
Plaintext
Raw Permalink Normal View History

2026-03-13 00:21:04 +00:00
let d = import "defaults.ncl" in
d.make_adr {
id = "adr-003",
title = "Q&A and Accumulated Knowledge Persist to NCL, Not Browser Storage",
status = 'Accepted,
date = "2026-03-12",
context = "The initial Q&A bookmarks feature stored entries in browser localStorage keyed by project. This is convenient but violates the dag-formalized axiom: knowledge that lives only in a browser session is invisible to agents, not git-versioned, not queryable via MCP, and is lost on browser data reset. The same problem applies to quick actions and any other accumulated operational knowledge. The system accumulates knowledge during development sessions (AI interactions, architectural reviews, debugging) that should be first-class artifacts — not ephemeral browser state.",
decision = "All Q&A entries persist to `reflection/qa.ncl` — a typed NCL record file governed by `reflection/schemas/qa.ncl`. Mutations happen via `crates/ontoref-daemon/src/ui/qa_ncl.rs` (line-level surgery, same pattern as backlog_ncl.rs). Four MCP tools expose the store to AI agents: `ontoref_qa_list` (read, with filter), `ontoref_qa_add` (append), `ontoref_qa_delete` (remove block), `ontoref_qa_update` (field mutation). HTTP endpoints `/qa-json` (GET), `/qa/add`, `/qa/delete`, `/qa/update` (POST) serve the UI. The UI renders server-side entries via Tera template injection (SERVER_ENTRIES JSON blob), eliminating the need for a separate fetch on load. The same principle is applied to quick actions: they are declared in `.ontoref/config.ncl` (quick_actions array) and new modes are created as `reflection/modes/<id>.ncl` files via `ontoref_action_add`.",
rationale = [
{
claim = "NCL persistence makes knowledge git-versioned and queryable",
detail = "A Q&A entry in reflection/qa.ncl is a typed Nickel record. It survives browser resets, is visible in git history, can be diffed, and is exported via the NCL cache just like any other protocol artifact. localStorage provides none of these properties.",
},
{
claim = "MCP access enables AI agents to read and write accumulated knowledge",
detail = "Without server-side persistence, an AI agent cannot query what questions have been asked and answered about this project. With ontoref_qa_list, the agent can orient itself immediately from accumulated Q&A without re-asking questions already answered in previous sessions. With ontoref_qa_add, the agent can record newly discovered knowledge while working.",
},
{
claim = "Line-level NCL surgery avoids full AST parsing",
detail = "The qa_ncl.rs module uses the same pattern as backlog_ncl.rs: find blocks by id field, insert/replace/remove by line index. No nickel-lang-core dependency, no AST round-trip, no format drift. The NCL format is predictable enough that targeted string operations are safe and sufficient.",
},
{
claim = "Server-side hydration eliminates the initial fetch round-trip",
detail = "The qa.html template receives `entries` as a Tera context variable and embeds SERVER_ENTRIES as a JSON literal in the page script. The JS initialises its in-memory list directly from this value. No separate GET /qa-json fetch on load, no loading state, no flash of empty content.",
},
{
claim = "Passive drift observation closes the knowledge-accumulation loop",
detail = "As code changes, drift_watcher.rs detects divergence between Yang artifacts and Yin ontology and emits a notification. Combined with Q&A persistence, the system can accumulate observations about drift patterns — enabling future Q&A entries like 'why does crate X often drift?' to be answered from stored context.",
},
],
consequences = {
positive = [
"Q&A knowledge survives session boundaries — AI agents and developers share a common context store",
"MCP tools give AI clients direct read/write access to accumulated project knowledge",
"All entries are typed (QaEntry schema), git-diffable, and auditable",
"Quick actions in config.ncl are the canonical source — no UI state drift",
"Passive drift observer closes the feedback loop without requiring manual sync",
],
negative = [
"qa.ncl and backlog.ncl use line-level surgery — format changes to the NCL templates require updating the mutation modules",
"No concurrent write protection — simultaneous writes from multiple actors could corrupt qa.ncl (same limitation as backlog.ncl; mitigated by advisory file locks in the ./ontoref CLI)",
"Deletion removes the block permanently — no soft-delete or archive mechanism yet",
],
},
alternatives_considered = [
{
option = "Keep localStorage as the storage backend, add optional sync to NCL on explicit user action",
why_rejected = "Two sources of truth creates sync complexity and divergence. AI agents would still not see localStorage entries. The sync step would be frequently skipped. NCL as primary is simpler.",
},
{
option = "Store Q&A in SurrealDB via stratum-db (existing optional dependency)",
why_rejected = "Requires the db feature and a running SurrealDB instance. NCL files are always present, git-versioned, and work without any database. The protocol-not-runtime axiom argues for file-first. SurrealDB can be added as a secondary index later if full-text search is needed.",
},
{
option = "Full AST parse of qa.ncl via nickel-lang-core for mutations",
why_rejected = "nickel-lang-core has an unstable Rust API. The file structure is predictable enough for line-level surgery. backlog_ncl.rs has been operating safely with this pattern. Adding a hard dependency on nickel-lang-core for a file that is always written by the daemon is unnecessary complexity.",
},
],
constraints = [
{
--- 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
id = "qa-write-via-mutation-module",
claim = "All mutations to reflection/qa.ncl must go through crates/ontoref-daemon/src/ui/qa_ncl.rs — no direct file writes from other call sites",
scope = "crates/ontoref-daemon/src/",
severity = 'Hard,
check = {
tag = 'Grep,
pattern = "qa\\.ncl",
paths = ["crates/ontoref-daemon/src"],
must_be_empty = false,
},
rationale = "Centralising mutations in one module ensures consistent id generation, NCL format, and cache invalidation.",
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
id = "qa-schema-typed",
claim = "reflection/qa.ncl must conform to the QaStore contract from reflection/schemas/qa.ncl — nickel typecheck must pass",
scope = "reflection/qa.ncl",
severity = 'Hard,
check = { tag = 'NuCmd, cmd = "nickel typecheck reflection/qa.ncl", expect_exit = 0 },
rationale = "Untyped Q&A would degrade to an unstructured log. The schema enforces id, question, answer, actor, created_at fields on every entry.",
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
id = "mcp-qa-tools-no-apply-drift",
claim = "MCP tools ontoref_qa_list and ontoref_qa_add must never trigger sync apply steps or modify .ontology/ files",
scope = "crates/ontoref-daemon/src/mcp/mod.rs",
severity = 'Hard,
check = {
tag = 'Grep,
pattern = "apply|sync_apply|write_ontology",
paths = ["crates/ontoref-daemon/src/mcp/mod.rs"],
must_be_empty = true,
},
rationale = "Q&A mutation tools operate only on reflection/qa.ncl. Ontology changes require deliberate human or agent review via the sync-ontology mode.",
2026-03-13 00:21:04 +00:00
},
],
related_adrs = ["adr-001-protocol-as-standalone-project", "adr-002-daemon-for-caching-and-notification-barrier"],
ontology_check = {
decision_string = "Q&A and operational knowledge persist as typed NCL artifacts, git-versioned, accessible via MCP and HTTP, rendered server-side in UI",
invariants_at_risk = ["dag-formalized", "protocol-not-runtime"],
verdict = 'Safe,
},
}