ontoref-code/assets/web/src/architecture-diagram-details.html
Jesús Pérez 0436a3b436
Some checks failed
Rust CI / Security Audit (push) Has been cancelled
Rust CI / Check + Test + Lint (nightly) (push) Has been cancelled
Rust CI / Check + Test + Lint (stable) (push) Has been cancelled
chore: add web and branding
2026-03-13 00:19:51 +00:00

838 lines
41 KiB
HTML

<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Ontoref — Architecture</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet" />
<style>
:root {
--bg: #0f172a;
--bg2: #1e293b;
--bg3: #0a0f1e;
--text: #e2e8f0;
--muted: #64748b;
--subtle: #334155;
--blue: #60a5fa;
--orange: #E8A838;
--green: #4ade80;
--purple: #c084fc;
--teal: #22d3ee;
--yellow: #facc15;
--red: #f87171;
--border: rgba(96,165,250,0.2);
}
html.light {
--bg: #f8fafc;
--bg2: #f1f5f9;
--bg3: #e2e8f0;
--text: #0f172a;
--muted: #64748b;
--subtle: #cbd5e1;
--border: rgba(96,165,250,0.35);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
background: var(--bg);
color: var(--text);
font-family: "Inter", sans-serif;
min-height: 100vh;
transition: background 0.3s, color 0.3s;
padding: 2rem;
padding-top: 5rem;
}
/* ── Nav ── */
.nav {
position: fixed;
top: 1.5rem; right: 1.5rem;
z-index: 100;
display: flex; gap: 0.4rem; align-items: center;
background: var(--bg2);
border: 1px solid var(--border);
border-radius: 20px;
padding: 0.3rem;
backdrop-filter: blur(10px);
}
.nav a, .nav button {
background: transparent; border: none;
color: var(--muted);
padding: 0.4rem 0.9rem;
border-radius: 16px;
cursor: pointer;
font-weight: 700;
font-size: 0.8rem;
text-transform: uppercase;
font-family: "JetBrains Mono", monospace;
text-decoration: none;
transition: all 0.2s;
}
.nav a:hover, .nav button:hover { color: var(--blue); }
.nav .back {
background: rgba(96,165,250,0.12);
color: var(--blue);
border: 1px solid rgba(96,165,250,0.3);
}
.nav .back:hover { background: rgba(96,165,250,0.2); }
/* ── Layout ── */
.page { max-width: 1200px; margin: 0 auto; }
.page-title {
text-align: center;
margin-bottom: 3rem;
}
.page-title h1 {
font-size: 2.2rem;
font-weight: 800;
background: linear-gradient(135deg, var(--blue) 0%, var(--orange) 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
margin-bottom: 0.5rem;
}
.page-title p { color: var(--muted); font-size: 0.95rem; }
/* ── Layer stack ── */
.arch {
display: flex;
flex-direction: column;
gap: 1px;
border-radius: 16px;
overflow: hidden;
border: 1px solid var(--border);
margin-bottom: 3rem;
}
.layer {
display: grid;
grid-template-columns: 180px 1fr;
min-height: 80px;
position: relative;
}
.layer-tag {
display: flex;
align-items: center;
justify-content: center;
padding: 1rem;
font-family: "JetBrains Mono", monospace;
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
text-align: center;
line-height: 1.4;
border-right: 1px solid var(--border);
}
.layer-body {
padding: 1rem 1.5rem;
display: flex;
flex-direction: column;
justify-content: center;
gap: 0.4rem;
}
.layer-body .items {
display: flex;
flex-wrap: wrap;
gap: 0.4rem;
}
.chip {
font-family: "JetBrains Mono", monospace;
font-size: 0.72rem;
padding: 0.2rem 0.6rem;
border-radius: 6px;
background: rgba(255,255,255,0.04);
border: 1px solid var(--subtle);
color: var(--muted);
}
.layer-body .desc {
font-size: 0.78rem;
color: var(--muted);
font-style: italic;
line-height: 1.5;
}
/* Layer colours */
.l-declarative .layer-tag { background: rgba(96,165,250,0.08); color: var(--blue); border-color: rgba(96,165,250,0.2); }
.l-declarative .layer-body { background: rgba(96,165,250,0.03); }
.l-operational .layer-tag { background: rgba(232,168,56,0.08); color: var(--orange); border-color: rgba(232,168,56,0.2); }
.l-operational .layer-body { background: rgba(232,168,56,0.02); }
.l-entry .layer-tag { background: rgba(74,222,128,0.08); color: var(--green); border-color: rgba(74,222,128,0.2); }
.l-entry .layer-body { background: rgba(74,222,128,0.02); }
.l-graph .layer-tag { background: rgba(192,132,252,0.08); color: var(--purple); border-color: rgba(192,132,252,0.2); }
.l-graph .layer-body { background: rgba(192,132,252,0.02); }
.l-runtime .layer-tag { background: rgba(34,211,238,0.08); color: var(--teal); border-color: rgba(34,211,238,0.2); }
.l-runtime .layer-body { background: rgba(34,211,238,0.02); }
.l-adopt .layer-tag { background: rgba(250,204,21,0.08); color: var(--yellow); border-color: rgba(250,204,21,0.2); }
.l-adopt .layer-body { background: rgba(250,204,21,0.02); }
/* ── Daemon Components 5-col grid ── */
.daemon-detail {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
margin-bottom: 3rem;
}
.daemon-detail .span2 {
grid-column: span 2;
}
.daemon-box {
border-radius: 12px;
padding: 1.5rem;
border: 1px solid var(--border);
background: var(--bg2);
}
.daemon-box h3 {
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
font-family: "JetBrains Mono", monospace;
margin-bottom: 1rem;
}
.daemon-box.box-http h3 { color: var(--teal); }
.daemon-box.box-mcp h3 { color: var(--blue); }
.daemon-box.box-sessions h3 { color: var(--green); }
.daemon-box.box-notify h3 { color: var(--orange); }
.daemon-box.box-ncl h3 { color: var(--yellow); }
.daemon-box h3.sub-h {
font-size: 0.7rem;
margin-top: 1.25rem;
margin-bottom: 0.5rem;
padding-top: 1rem;
border-top: 1px solid rgba(255,255,255,0.06);
}
.route-list { list-style: none; }
.route-list li {
display: flex;
align-items: baseline;
gap: 0.5rem;
padding: 0.25rem 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
font-size: 0.75rem;
}
.route-list li:last-child { border-bottom: none; }
.method {
font-family: "JetBrains Mono", monospace;
font-size: 0.65rem;
font-weight: 700;
padding: 0.1rem 0.35rem;
border-radius: 4px;
min-width: 2.8rem;
text-align: center;
flex-shrink: 0;
}
.method.get { background: rgba(74,222,128,0.15); color: var(--green); }
.method.post { background: rgba(96,165,250,0.15); color: var(--blue); }
.route-path {
font-family: "JetBrains Mono", monospace;
color: var(--text);
font-size: 0.72rem;
flex: 1;
}
.route-desc { color: var(--muted); font-size: 0.68rem; }
.tool-list { list-style: none; }
.tool-list li {
padding: 0.25rem 0;
border-bottom: 1px solid rgba(255,255,255,0.04);
font-size: 0.73rem;
}
.tool-list li:last-child { border-bottom: none; }
.tool-name {
font-family: "JetBrains Mono", monospace;
color: var(--blue);
font-size: 0.7rem;
}
.tool-desc { color: var(--muted); font-size: 0.68rem; }
/* ── Flow diagram ── */
.flow-section {
margin-bottom: 3rem;
}
.flow-section h2 {
font-size: 1.1rem;
font-weight: 700;
color: var(--muted);
letter-spacing: 0.06em;
text-transform: uppercase;
font-family: "JetBrains Mono", monospace;
margin-bottom: 1.5rem;
text-align: center;
}
.flow-row {
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
gap: 0;
margin-bottom: 1rem;
}
.flow-row-label {
font-family: "JetBrains Mono", monospace;
font-size: 0.68rem;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.06em;
margin-bottom: 0.6rem;
text-align: center;
}
.flow-node {
background: var(--bg2);
border: 1px solid var(--border);
border-radius: 10px;
padding: 0.8rem 1.2rem;
text-align: center;
min-width: 100px;
}
.flow-node .fn-label {
font-family: "JetBrains Mono", monospace;
font-size: 0.72rem;
font-weight: 600;
}
.flow-node .fn-sub {
font-size: 0.65rem;
color: var(--muted);
margin-top: 0.2rem;
}
.flow-arrow {
color: var(--subtle);
font-size: 1.4rem;
padding: 0 0.3rem;
flex-shrink: 0;
}
.flow-arrow.warn { color: var(--orange); }
.fn-actor .fn-label { color: var(--green); }
.fn-cli .fn-label { color: var(--yellow); }
.fn-daemon .fn-label { color: var(--teal); }
.fn-mcp .fn-label { color: var(--blue); }
.fn-ncl .fn-label { color: var(--orange); }
.fn-git .fn-label { color: var(--purple); }
.fn-hook .fn-label { color: var(--red); }
.fn-pass .fn-label { color: var(--green); }
/* ── Persistence tiers ── */
.persistence-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.5rem;
margin-bottom: 3rem;
}
.persist-box {
border-radius: 12px;
padding: 1.5rem;
border: 1px solid;
background: var(--bg2);
}
.persist-box.tier-memory { border-color: rgba(96,165,250,0.25); }
.persist-box.tier-surreal { border-color: rgba(34,211,238,0.25); }
.persist-box h3 {
font-size: 0.78rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
font-family: "JetBrains Mono", monospace;
margin-bottom: 0.4rem;
}
.persist-box.tier-memory h3 { color: var(--blue); }
.persist-box.tier-surreal h3 { color: var(--teal); }
.persist-box .tier-sub {
font-size: 0.72rem;
color: var(--muted);
font-style: italic;
margin-bottom: 0.85rem;
}
.persist-box ul {
list-style: none;
padding: 0;
font-size: 0.75rem;
color: var(--muted);
line-height: 1.9;
}
.persist-box ul li::before { content: "· "; }
.persist-box ul strong { color: var(--text); }
.persist-box ul code {
font-family: "JetBrains Mono", monospace;
font-size: 0.7rem;
color: var(--blue);
}
/* ── Constraint callouts ── */
.constraints {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin-bottom: 3rem;
}
.constraint-card {
background: var(--bg2);
border-radius: 10px;
padding: 1rem 1.25rem;
border-left: 3px solid;
font-size: 0.8rem;
}
.constraint-card.hard { border-color: var(--red); }
.constraint-card.soft { border-color: var(--yellow); }
.constraint-card .c-id {
font-family: "JetBrains Mono", monospace;
font-size: 0.68rem;
color: var(--muted);
margin-bottom: 0.3rem;
}
.constraint-card .c-claim { color: var(--text); line-height: 1.5; }
.constraint-card.hard .c-id::before { content: "HARD · "; color: var(--red); }
.constraint-card.soft .c-id::before { content: "SOFT · "; color: var(--yellow); }
/* ── section headers ── */
.section-h {
font-size: 1.1rem;
font-weight: 700;
color: var(--muted);
letter-spacing: 0.06em;
text-transform: uppercase;
font-family: "JetBrains Mono", monospace;
margin-bottom: 1.5rem;
padding-bottom: 0.5rem;
border-bottom: 1px solid var(--border);
}
@media (max-width: 1000px) {
.daemon-detail { grid-template-columns: 1fr 1fr; }
.daemon-detail .span2 { grid-column: span 2; }
.layer { grid-template-columns: 120px 1fr; }
}
@media (max-width: 700px) {
.daemon-detail { grid-template-columns: 1fr; }
.daemon-detail .span2 { grid-column: span 1; }
.persistence-grid { grid-template-columns: 1fr; }
body { padding: 1rem; padding-top: 5rem; }
.layer { grid-template-columns: 1fr; }
.layer-tag { border-right: none; border-bottom: 1px solid var(--border); min-height: auto; }
.flow-row { gap: 0.5rem; flex-direction: column; }
.flow-arrow { transform: rotate(90deg); }
}
</style>
</head>
<body>
<div class="nav">
<button onclick="toggleTheme()" id="theme-btn" title="Toggle theme">☀️</button>
<a href="index.html" class="back">← Ontoref</a>
</div>
<div class="page">
<div class="page-title">
<h1>Architecture</h1>
<p>Protocol layers, runtime components, data flows, persistence tiers, and active constraints — v0.1.0</p>
</div>
<!-- ── Protocol Layer Stack ── -->
<div class="section-h">Protocol Layers</div>
<div class="arch">
<div class="layer l-declarative">
<div class="layer-tag">Declarative<br/>Layer<br/>Nickel</div>
<div class="layer-body">
<div class="items">
<span class="chip">.ontology/core.ncl</span>
<span class="chip">.ontology/state.ncl</span>
<span class="chip">adrs/adr-NNN.ncl</span>
<span class="chip">reflection/schemas/</span>
<span class="chip">reflection/modes/</span>
<span class="chip">reflection/forms/</span>
<span class="chip">reflection/qa.ncl</span>
<span class="chip">.ontoref/config.ncl</span>
</div>
<div class="desc">Strong types, contracts, enums. Fails at definition time. No runtime evaluation — nickel export produces JSON consumed by all layers above.</div>
</div>
</div>
<div class="layer l-graph">
<div class="layer-tag">Knowledge<br/>Graph<br/>.ontology/</div>
<div class="layer-body">
<div class="items">
<span class="chip">4 axioms</span>
<span class="chip">2 tensions</span>
<span class="chip">12 practices</span>
<span class="chip">nodes + edges</span>
<span class="chip">invariants</span>
<span class="chip">gates</span>
<span class="chip">dimensions</span>
</div>
<div class="desc">The project knows what it knows. Actor-agnostic. Machine-queryable. ontoref uses its own protocol — the .ontology/ here describes ontoref itself.</div>
</div>
</div>
<div class="layer l-operational">
<div class="layer-tag">Operational<br/>Layer<br/>Nushell</div>
<div class="layer-body">
<div class="items">
<span class="chip">adr.nu</span>
<span class="chip">backlog.nu</span>
<span class="chip">coder.nu</span>
<span class="chip">describe.nu</span>
<span class="chip">sync.nu</span>
<span class="chip">config.nu</span>
<span class="chip">register.nu</span>
<span class="chip">forms.nu</span>
<span class="chip">+8 more</span>
</div>
<div class="desc">Typed Nushell pipelines over structured data — no text streams. 16 modules, each responsible for one operational domain. DAG modes validated before execution.</div>
</div>
</div>
<div class="layer l-entry">
<div class="layer-tag">Entry<br/>Point<br/>Bash → Nu</div>
<div class="layer-body">
<div class="items">
<span class="chip">./ontoref</span>
<span class="chip">actor detection</span>
<span class="chip">advisory locking</span>
<span class="chip">NICKEL_IMPORT_PATH</span>
<span class="chip">./ontoref -V</span>
</div>
<div class="desc">Single entry point per project. Detects actor (developer / agent / CI) from flag, env var, or TTY. Acquires mkdir-based POSIX advisory lock per resource. Dispatches to Nu.</div>
</div>
</div>
<div class="layer l-runtime">
<div class="layer-tag">Runtime<br/>Layer<br/>Rust + axum</div>
<div class="layer-body">
<div class="items">
<span class="chip">ontoref-daemon</span>
<span class="chip">ontoref-ontology</span>
<span class="chip">ontoref-reflection</span>
<span class="chip">axum HTTP API</span>
<span class="chip">Tera templates</span>
<span class="chip">MCP server</span>
<span class="chip">DashMap cache</span>
<span class="chip">notify watcher</span>
<span class="chip">drift observer</span>
<span class="chip">search engine</span>
<span class="chip">notification store</span>
<span class="chip">actor registry</span>
<span class="chip">session store</span>
<span class="chip">SurrealDB (optional)</span>
</div>
<div class="desc">Optional persistent daemon. Caches nickel export results (path+mtime keyed). Serves HTTP UI (10 pages) + MCP server (19 tools). Actor registry with DashMap sessions. Notification barrier (pre_commit, drift, ontology_drift). Full-text search engine across nodes/ADRs/modes. Never a protocol requirement.</div>
</div>
</div>
<div class="layer l-adopt">
<div class="layer-tag">Adoption<br/>Layer<br/>Per-project</div>
<div class="layer-body">
<div class="items">
<span class="chip">templates/ontology/</span>
<span class="chip">templates/ontoref-config.ncl</span>
<span class="chip">templates/scripts-ontoref</span>
<span class="chip">adopt_ontoref mode</span>
<span class="chip">ONTOREF_PROJECT_ROOT</span>
</div>
<div class="desc">Each project maintains its own .ontology/ data. One ontoref checkout serves multiple projects via ONTOREF_PROJECT_ROOT. Zero lock-in — files are plain NCL.</div>
</div>
</div>
</div>
<!-- ── Request Flows ── -->
<div class="flow-section">
<div class="section-h">Request Flows</div>
<!-- Flow 1: CLI path -->
<div class="flow-row-label">CLI path — developer or agent via shell</div>
<div class="flow-row">
<div class="flow-node fn-actor">
<div class="fn-label">Actor</div>
<div class="fn-sub">developer · agent · CI</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-cli">
<div class="fn-label">./ontoref</div>
<div class="fn-sub">bash wrapper</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-cli">
<div class="fn-label">ontoref.nu</div>
<div class="fn-sub">Nu dispatcher</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-daemon">
<div class="fn-label">daemon</div>
<div class="fn-sub">cache / notify</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-ncl">
<div class="fn-label">nickel export</div>
<div class="fn-sub">.ontology/ · adrs/</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-git">
<div class="fn-label">reflection/</div>
<div class="fn-sub">qa.ncl · backlog.ncl</div>
</div>
</div>
<!-- Flow 2: MCP/agent path -->
<div class="flow-row-label" style="margin-top:1.5rem;">MCP / AI agent path — Claude Code or any MCP client</div>
<div class="flow-row" style="opacity: 0.85;">
<div class="flow-node fn-mcp" style="min-width: 80px;">
<div class="fn-label">AI Agent</div>
<div class="fn-sub">Claude · any MCP client</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-daemon">
<div class="fn-label">MCP server</div>
<div class="fn-sub">stdio · HTTP /mcp</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-ncl">
<div class="fn-label">19 tools</div>
<div class="fn-sub">read · write · query</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-git">
<div class="fn-label">NCL files</div>
<div class="fn-sub">git-versioned</div>
</div>
</div>
<!-- Flow 3: pre-commit hook -->
<div class="flow-row-label" style="margin-top:1.5rem;">Pre-commit barrier — notification-gated commit path</div>
<div class="flow-row" style="opacity: 0.85;">
<div class="flow-node fn-hook">
<div class="fn-label">git commit</div>
<div class="fn-sub">pre-commit hook fires</div>
</div>
<div class="flow-arrow warn"></div>
<div class="flow-node fn-daemon">
<div class="fn-label">GET /notifications</div>
<div class="fn-sub">/pending?token=X&amp;project=Y</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-ncl">
<div class="fn-label">pending?</div>
<div class="fn-sub">pre_commit · drift · ontology_drift</div>
</div>
<div class="flow-arrow warn"></div>
<div class="flow-node fn-hook">
<div class="fn-label">block</div>
<div class="fn-sub">until acked in UI / POST /notifications/ack</div>
</div>
<div class="flow-arrow"></div>
<div class="flow-node fn-pass">
<div class="fn-label">pass</div>
<div class="fn-sub">fail-open if daemon unreachable</div>
</div>
</div>
</div>
<!-- ── Daemon Components ── -->
<div class="section-h">Daemon Components</div>
<div class="daemon-detail">
<!-- Box 1: HTTP API routes -->
<div class="daemon-box box-http">
<h3>HTTP API · /ui/{slug}</h3>
<ul class="route-list">
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/</span><span class="route-desc">Dashboard — overview, actors, cache stats</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/graph</span><span class="route-desc">D3 ontology graph (nodes by pole, edges)</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/search</span><span class="route-desc">Search across nodes/ADRs/modes</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/sessions</span><span class="route-desc">Live actor registry</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/notifications</span><span class="route-desc">Notification feed + ack UI</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/backlog</span><span class="route-desc">Backlog with priority/status</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/qa</span><span class="route-desc">Q&amp;A knowledge store</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/actions</span><span class="route-desc">Quick actions catalog</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/modes</span><span class="route-desc">Reflection modes browser</span></li>
<li><span class="method get">GET</span><span class="route-path">/ui/{slug}/compose</span><span class="route-desc">Agent task composer (live sharing)</span></li>
<li><span class="method get">GET</span><span class="route-path">/actors</span><span class="route-desc">Actor sessions list</span></li>
<li><span class="method get">GET</span><span class="route-path">/search?q=&amp;project=</span><span class="route-desc">Search JSON endpoint</span></li>
<li><span class="method get">GET</span><span class="route-path">/notifications/pending</span><span class="route-desc">Pre-commit hook polling</span></li>
<li><span class="method post">POST</span><span class="route-path">/notifications/ack</span><span class="route-desc">Ack one or all notifications</span></li>
<li><span class="method post">POST</span><span class="route-path">/{slug}/notifications/emit</span><span class="route-desc">Emit custom notification</span></li>
<li><span class="method post">POST</span><span class="route-path">/{slug}/notifications/{id}/action</span><span class="route-desc">Trigger notification action</span></li>
<li><span class="method post">POST</span><span class="route-path">/compose/send</span><span class="route-desc">Dispatch mode to ./ontoref server-side</span></li>
<li><span class="method post">POST</span><span class="route-path">/actions/run</span><span class="route-desc">Execute quick action by id</span></li>
<li><span class="method post">POST</span><span class="route-path">/qa/add</span><span class="route-desc">Append Q&amp;A entry to NCL</span></li>
<li><span class="method post">POST</span><span class="route-path">/qa/delete</span><span class="route-desc">Remove entry by id</span></li>
<li><span class="method post">POST</span><span class="route-path">/qa/update</span><span class="route-desc">Mutate question + answer</span></li>
<li><span class="method get">GET</span><span class="route-path">/qa-json</span><span class="route-desc">Export all Q&amp;A entries</span></li>
<li><span class="method post">POST</span><span class="route-path">/backlog/add</span><span class="route-desc">Add backlog item</span></li>
<li><span class="method post">POST</span><span class="route-path">/backlog/status</span><span class="route-desc">Update backlog item status</span></li>
<li><span class="method get">GET</span><span class="route-path">/backlog-json</span><span class="route-desc">Export backlog as JSON</span></li>
<li><span class="method get">GET</span><span class="route-path">/health</span><span class="route-desc">Cache stats + uptime + SurrealDB status</span></li>
</ul>
</div>
<!-- Box 2: MCP Server -->
<div class="daemon-box box-mcp">
<h3>MCP Server · 19 tools</h3>
<ul class="tool-list">
<li><span class="tool-name">ontoref_help</span> <span class="tool-desc">— list all tools and usage</span></li>
<li><span class="tool-name">ontoref_list_projects</span> <span class="tool-desc">— enumerate loaded projects</span></li>
<li><span class="tool-name">ontoref_set_project</span> <span class="tool-desc">— set session default project</span></li>
<li><span class="tool-name">ontoref_project_status</span> <span class="tool-desc">— full project dashboard</span></li>
<li><span class="tool-name">ontoref_describe</span> <span class="tool-desc">— architecture overview</span></li>
<li><span class="tool-name">ontoref_search</span> <span class="tool-desc">— free-text across nodes/ADRs/modes</span></li>
<li><span class="tool-name">ontoref_get</span> <span class="tool-desc">— fetch node by id</span></li>
<li><span class="tool-name">ontoref_get_node</span> <span class="tool-desc">— full ontology node with edges</span></li>
<li><span class="tool-name">ontoref_list_adrs</span> <span class="tool-desc">— list ADRs by status filter</span></li>
<li><span class="tool-name">ontoref_get_adr</span> <span class="tool-desc">— full ADR with constraints</span></li>
<li><span class="tool-name">ontoref_list_modes</span> <span class="tool-desc">— list reflection modes</span></li>
<li><span class="tool-name">ontoref_get_mode</span> <span class="tool-desc">— mode DAG contract</span></li>
<li><span class="tool-name">ontoref_get_backlog</span> <span class="tool-desc">— backlog filtered by status</span></li>
<li><span class="tool-name">ontoref_backlog</span> <span class="tool-desc">— add / update_status</span></li>
<li><span class="tool-name">ontoref_constraints</span> <span class="tool-desc">— all architectural constraints</span></li>
<li><span class="tool-name">ontoref_qa_list</span> <span class="tool-desc">— list Q&amp;A with optional filter</span></li>
<li><span class="tool-name">ontoref_qa_add</span> <span class="tool-desc">— append to reflection/qa.ncl</span></li>
<li><span class="tool-name">ontoref_action_list</span> <span class="tool-desc">— quick actions from config.ncl</span></li>
<li><span class="tool-name">ontoref_action_add</span> <span class="tool-desc">— create mode + register action</span></li>
</ul>
</div>
<!-- Box 3: Sessions & Actors -->
<div class="daemon-box box-sessions">
<h3>Sessions &amp; Actor Registry</h3>
<ul class="route-list">
<li><span class="route-path">ActorRegistry</span><span class="route-desc">backed by DashMap&lt;String, ActorSession&gt;</span></li>
<li><span class="route-path">token</span><span class="route-desc">random string, unique per session</span></li>
<li><span class="route-path">actor_type</span><span class="route-desc">developer · agent · CI</span></li>
<li><span class="route-path">registered_at</span><span class="route-desc">ISO timestamp, set on registration</span></li>
<li><span class="route-path">last_seen</span><span class="route-desc">updated on each API call</span></li>
<li><span class="route-path">current_mode</span><span class="route-desc">last active reflection mode</span></li>
<li><span class="route-path">serializable</span><span class="route-desc">snapshot for disk persistence</span></li>
<li><span class="route-path">pre-commit CI</span><span class="route-desc">registers as ci actor on hook fire</span></li>
</ul>
<h3 class="sub-h">Compose / Live Sharing</h3>
<ul class="route-list">
<li><span class="route-path">/ui/{slug}/compose</span><span class="route-desc">renders mode form schemas as interactive HTML</span></li>
<li><span class="route-path">POST /compose/send</span><span class="route-desc">dispatches ./ontoref {mode} {params} on server</span></li>
<li><span class="route-path">shared context</span><span class="route-desc">multiple actors see same composed task live</span></li>
<li><span class="route-path">/ui/{slug}/manage</span><span class="route-desc">project registry CRUD (multi-project mode)</span></li>
</ul>
</div>
<!-- Box 4: Notification Store & Search -->
<div class="daemon-box box-notify">
<h3>Notification Store &amp; Search</h3>
<ul class="route-list">
<li><span class="route-path">NotificationStore</span><span class="route-desc">in-memory queue per (project, token)</span></li>
<li><span class="route-path">pre_commit</span><span class="route-desc">blocks git commits until acked by actor</span></li>
<li><span class="route-path">drift</span><span class="route-desc">schema drift between codebase and ontology</span></li>
<li><span class="route-path">ontology_drift</span><span class="route-desc">from passive observer — missing/stale/drift/broken counts</span></li>
<li><span class="route-path">fail-open</span><span class="route-desc">if daemon unreachable, pre-commit hook passes</span></li>
<li><span class="route-path">ack workflow</span><span class="route-desc">POST /notifications/ack (one or all); action buttons in UI</span></li>
</ul>
<h3 class="sub-h">Search Engine</h3>
<ul class="route-list">
<li><span class="route-path">indexes</span><span class="route-desc">ontology nodes (id, name, description)</span></li>
<li><span class="route-path">indexes</span><span class="route-desc">ADRs (title, context, decision)</span></li>
<li><span class="route-path">indexes</span><span class="route-desc">reflection modes (name, description, steps)</span></li>
<li><span class="route-path">SearchResult</span><span class="route-desc">kind · id · title · snippet · score</span></li>
<li><span class="route-path">used by</span><span class="route-desc">UI search page + MCP ontoref_search tool</span></li>
</ul>
</div>
<!-- Box 5: NCL mutations + SurrealDB -->
<div class="daemon-box box-ncl span2">
<h3>NCL Files — Mutations</h3>
<ul class="route-list">
<li><span class="route-path">reflection/qa.ncl</span><span class="route-desc">QaStore — typed entries, git-versioned; written only via crates/ontoref-daemon/src/ui/qa_ncl.rs (ADR-003 hard constraint)</span></li>
<li><span class="route-path">reflection/backlog.ncl</span><span class="route-desc">BacklogStore — items with status (Open/InProgress/Done/Cancelled) and priority (Critical/High/Medium/Low)</span></li>
<li><span class="route-path">.ontoref/config.ncl</span><span class="route-desc">quick_actions catalog, log level, NATS toggle</span></li>
<li><span class="route-path">reflection/modes/*.ncl</span><span class="route-desc">new mode files created by ontoref_action_add</span></li>
</ul>
<h3 class="sub-h">Drift Observer</h3>
<ul class="route-list">
<li><span class="route-path">watches</span><span class="route-desc">crates/ · .ontology/ · adrs/ · reflection/modes/</span></li>
<li><span class="route-path">debounce</span><span class="route-desc">15 seconds after last filesystem event</span></li>
<li><span class="route-path">runs</span><span class="route-desc">sync scan → sync diff (read-only, never applies changes)</span></li>
<li><span class="route-path">emits</span><span class="route-desc">ontology_drift { missing, stale, drift, broken } to NotificationStore</span></li>
</ul>
<h3 class="sub-h">SurrealDB (optional --db feature)</h3>
<ul class="route-list">
<li><span class="route-path">connection</span><span class="route-desc">WebSocket to --db-url ws://...; 5s timeout at startup; fail-open</span></li>
<li><span class="route-path">seeds</span><span class="route-desc">ontology tables from local NCL files on startup and on file changes (via notify watcher)</span></li>
<li><span class="route-path">persists</span><span class="route-desc">actor sessions, seeded ontology tables, search index, notification history — survives daemon restarts</span></li>
<li><span class="route-path">without --db</span><span class="route-desc">DashMap-backed in-memory cache only; process-lifetime; actor sessions and search index are ephemeral</span></li>
</ul>
</div>
</div>
<!-- ── Persistence Tiers ── -->
<div class="section-h">Persistence Tiers</div>
<div class="persistence-grid">
<div class="persist-box tier-memory">
<h3>In-Memory — Always Active</h3>
<p class="tier-sub">DashMap-backed, process-lifetime — no flags required</p>
<ul>
<li><strong>DashMap cache</strong> — NCL export results, path+mtime keyed</li>
<li><strong>NotificationStore</strong> — per (project, token) notification queue</li>
<li><strong>ActorRegistry</strong> — live session DashMap</li>
<li><strong>SessionStore</strong> — token → ActorSession map</li>
<li><strong>Search index</strong> — in-process full-text over loaded NCL data</li>
<li>All state is <strong>lost on daemon restart</strong></li>
<li>Serializable snapshots can be written to disk as JSON/NCL</li>
</ul>
</div>
<div class="persist-box tier-surreal">
<h3>SurrealDB — Optional <code>--db</code> Flag</h3>
<p class="tier-sub">WebSocket connection, fail-open — survives daemon restarts</p>
<ul>
<li><strong>Actor sessions</strong> — token, type, registered_at, last_seen, mode</li>
<li><strong>Seeded ontology tables</strong> — nodes, ADRs, modes from NCL files</li>
<li><strong>Search index</strong> — persisted search index for nodes/ADRs/modes</li>
<li><strong>Notification history</strong> — acked and dismissed notifications</li>
<li>Enabled with <code>--db-url ws://...</code> + <code>--db-namespace</code></li>
<li>If SurrealDB is unreachable at startup, <strong>daemon still starts</strong> using in-memory tier</li>
</ul>
</div>
</div>
<!-- ── Active Constraints ── -->
<div class="section-h">Active Constraints (ADR-003 · ADR-002 · ADR-001)</div>
<div class="constraints">
<div class="constraint-card hard">
<div class="c-id">qa-write-via-mutation-module</div>
<div class="c-claim">All mutations to reflection/qa.ncl must go through <code>crates/ontoref-daemon/src/ui/qa_ncl.rs</code> — no direct file writes from other call sites.</div>
</div>
<div class="constraint-card hard">
<div class="c-id">qa-schema-typed</div>
<div class="c-claim"><code>reflection/qa.ncl</code> must conform to the QaStore contract from <code>reflection/schemas/qa.ncl</code> — nickel typecheck must pass.</div>
</div>
<div class="constraint-card hard">
<div class="c-id">mcp-qa-tools-no-apply-drift</div>
<div class="c-claim">MCP tools <code>ontoref_qa_list</code> and <code>ontoref_qa_add</code> must never trigger sync apply steps or modify <code>.ontology/</code> files.</div>
</div>
<div class="constraint-card hard">
<div class="c-id">protocol-not-runtime (axiom)</div>
<div class="c-claim">ontoref-daemon is optional runtime support — not a protocol requirement. The protocol functions without it. Every module falls back to direct subprocess.</div>
</div>
<div class="constraint-card hard">
<div class="c-id">notification-barrier-fail-open</div>
<div class="c-claim">The pre-commit hook must not block git commits when the daemon is unreachable. If <code>GET /notifications/pending</code> fails (connection error, timeout), the hook must pass immediately.</div>
</div>
<div class="constraint-card hard">
<div class="c-id">ontology-crate-zero-stratumiops-deps (ADR-001)</div>
<div class="c-claim"><code>ontoref-ontology</code> must never depend on <code>stratum-graph</code>, <code>stratum-state</code>, or <code>stratum-orchestrator</code>. The ontology crate is the protocol's minimal adoption surface and must build standalone.</div>
</div>
</div>
</div><!-- /page -->
<script>
const KEY = "ontoref-arch-theme";
function getTheme() { return localStorage.getItem(KEY) || "dark"; }
function setTheme(t) {
localStorage.setItem(KEY, t);
const btn = document.getElementById("theme-btn");
if (t === "light") {
document.documentElement.classList.add("light");
btn.textContent = "🌙";
} else {
document.documentElement.classList.remove("light");
btn.textContent = "☀️";
}
}
function toggleTheme() { setTheme(getTheme() === "dark" ? "light" : "dark"); }
document.addEventListener("DOMContentLoaded", () => setTheme(getTheme()));
</script>
</body>
</html>