151 lines
5.8 KiB
HTML
Raw Normal View History

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
{% extends "base.html" %}
{% import "macros/ui.html" as m %}
{% block title %}ADRs — {{ slug }} — Ontoref{% endblock title %}
{% block nav_adrs %}active{% endblock nav_adrs %}
{% block nav_group_dev %}active{% endblock nav_group_dev %}
{% block head %}
{% endblock head %}
{% block content %}
<div class="mb-6 flex items-center justify-between">
<div>
<h1 class="text-2xl font-bold">Architecture Decision Records</h1>
<p class="text-base-content/50 text-sm mt-1">Typed NCL records — constraints, rationale, and alternatives for lasting architectural decisions</p>
</div>
<span class="badge badge-lg badge-neutral">{{ adr_count }} ADRs</span>
</div>
<!-- Filter bar -->
<div class="flex flex-wrap gap-2 mb-4">
<input id="filter-input" type="text" placeholder="Filter by ID, title, or context…"
class="input input-sm input-bordered flex-1 min-w-48 font-mono"
oninput="filterAdrs()">
<select id="filter-status" class="select select-sm select-bordered" onchange="filterAdrs()">
<option value="">All statuses</option>
<option value="Accepted">Accepted</option>
<option value="Proposed">Proposed</option>
<option value="Deprecated">Deprecated</option>
<option value="Superseded">Superseded</option>
</select>
</div>
<!-- ADR table -->
<div class="overflow-x-auto" id="adrs-container">
<table class="table table-sm w-full bg-base-200 rounded-lg" id="adrs-table">
<thead>
<tr class="text-base-content/50 text-xs uppercase tracking-wider">
<th class="w-24">ID</th>
<th>Title</th>
<th class="w-24">Status</th>
<th class="w-20">Date</th>
<th class="w-20">Constraints</th>
</tr>
</thead>
<tbody id="adrs-body"></tbody>
</table>
</div>
<!-- ADR detail modal -->
<dialog id="adr-modal" class="modal">
<div class="modal-box w-full max-w-3xl">
<form method="dialog">
<button class="btn btn-sm btn-circle btn-ghost absolute right-3 top-3"></button>
</form>
<div class="flex items-baseline gap-3 mb-1 pr-8">
<span id="detail-id" class="font-mono font-bold text-primary"></span>
<span id="detail-status-badge"></span>
<span id="detail-date" class="text-xs text-base-content/40 font-mono"></span>
</div>
<h2 id="detail-title" class="text-lg font-bold mb-3"></h2>
<div class="space-y-4 text-sm">
<div>
<h3 class="text-xs font-semibold uppercase tracking-wider text-base-content/50 mb-1">Context</h3>
<p id="detail-context" class="text-base-content/80 leading-relaxed"></p>
</div>
<div>
<h3 class="text-xs font-semibold uppercase tracking-wider text-base-content/50 mb-1">Decision</h3>
<p id="detail-decision" class="text-base-content/80 leading-relaxed"></p>
</div>
</div>
<div class="flex gap-4 text-xs text-base-content/40 border-t border-base-content/10 pt-3 mt-4">
<span>Hard constraints: <span id="detail-hard" class="font-mono text-error"></span></span>
<span>Soft constraints: <span id="detail-soft" class="font-mono text-warning"></span></span>
<span class="ml-auto font-mono" id="detail-file"></span>
</div>
</div>
<form method="dialog" class="modal-backdrop">
<button>close</button>
</form>
</dialog>
<script>
const ADRS = {{ adrs_json | safe }};
function statusBadge(s) {
2026-03-29 00:19:56 +00:00
const cls = {
Accepted: 'badge badge-success badge-xs font-mono',
Proposed: 'badge badge-warning badge-xs font-mono',
Deprecated: 'badge badge-ghost badge-xs font-mono',
Superseded: 'badge badge-error badge-xs font-mono',
Error: 'badge badge-error badge-xs font-mono',
}[s] ?? 'badge badge-ghost badge-xs font-mono';
return `<span class="${cls}">${s}</span>`;
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
}
let visibleAdrs = ADRS;
function renderAdrs(adrs) {
visibleAdrs = adrs;
const tbody = document.getElementById('adrs-body');
tbody.innerHTML = adrs.map((a, i) => `
<tr class="hover cursor-pointer" onclick="showDetail(${i})">
<td class="font-mono text-xs text-primary">${a.id}</td>
<td class="text-sm">${a.title || '<span class="text-base-content/30"></span>'}</td>
<td>${statusBadge(a.status)}</td>
<td class="font-mono text-xs text-base-content/50">${a.date || ''}</td>
<td class="text-xs">
${a.hard_constraints > 0 ? `<span class="text-error font-mono mr-1">${a.hard_constraints}H</span>` : ''}
${a.soft_constraints > 0 ? `<span class="text-warning font-mono">${a.soft_constraints}S</span>` : ''}
${a.hard_constraints === 0 && a.soft_constraints === 0 ? '<span class="text-base-content/30"></span>' : ''}
</td>
</tr>
`).join('');
}
function showDetail(index) {
const a = visibleAdrs[index];
if (!a) return;
document.getElementById('detail-id').textContent = a.id;
document.getElementById('detail-status-badge').innerHTML = statusBadge(a.status);
document.getElementById('detail-date').textContent = a.date;
document.getElementById('detail-title').textContent = a.title;
document.getElementById('detail-context').textContent = a.context;
document.getElementById('detail-decision').textContent = a.decision;
document.getElementById('detail-hard').textContent = a.hard_constraints;
document.getElementById('detail-soft').textContent = a.soft_constraints;
document.getElementById('detail-file').textContent = a.file + '.ncl';
document.getElementById('adr-modal').showModal();
}
function filterAdrs() {
const text = document.getElementById('filter-input').value.toLowerCase();
const status = document.getElementById('filter-status').value;
const filtered = ADRS.filter(a => {
const textMatch = !text ||
a.id.toLowerCase().includes(text) ||
a.title.toLowerCase().includes(text) ||
a.context.toLowerCase().includes(text);
const statusMatch = !status || a.status === status;
return textMatch && statusMatch;
});
renderAdrs(filtered);
}
renderAdrs(ADRS);
</script>
{% endblock content %}