let d = import "../defaults.ncl" in d.make_mode String { id = "sync-ontology", trigger = "Synchronize .ontology/ declarations with actual project artifacts — detect drift, propose patches, apply with confirmation", preconditions = [ "ONTOREF_PROJECT_ROOT is set and points to a project with .ontology/core.ncl", "nickel binary is available on PATH", "Nushell >= 0.110.0 is available", ], guards = [ { id = "ontology-exists", cmd = "test -f .ontology/core.ncl", reason = "No .ontology/core.ncl found — this project has no ontology to sync. Run adopt_ontoref first.", severity = 'Block, }, { id = "nickel-available", cmd = "command -v nickel >/dev/null 2>&1", reason = "nickel binary not on PATH — cannot export NCL schemas. Install via: cargo install nickel-lang-cli", severity = 'Block, }, { id = "manifest-capabilities", cmd = "ONTOREF_ROOT=\"$(pwd)\" ONTOREF_PROJECT_ROOT=\"$(pwd)\" nu --no-config-file -c 'use ./reflection/modules/sync.nu *; sync manifest-check'", reason = "Manifest capabilities incomplete — sync may report drift caused by undeclared capabilities rather than real code divergence. Fix manifest first.", severity = 'Warn, }, ], converge = { condition = "ONTOREF_ROOT=\"$(pwd)\" ONTOREF_PROJECT_ROOT=\"$(pwd)\" nu --no-config-file -c 'use ./reflection/modules/sync.nu *; let d = (sync diff --quick); let issues = ($d | where { |r| $r.status != \"OK\" }); if ($issues | is-empty) { exit 0 } else { exit 1 }'", max_iterations = 2, strategy = 'RetryFailed, }, steps = [ { id = "scan", action = "Analyze project structure: crates, scenarios, agents, CI, forms, modes. If nightly toolchain is available, extract pub API surface via cargo doc JSON.", cmd = "./ontoref sync scan", actor = 'Both, on_error = { strategy = 'Stop }, }, { id = "diff", action = "Compare scan results against .ontology/core.ncl nodes and edges. Categorize each item as OK, MISSING (artifact without node), STALE (node without artifact), DRIFT (node outdated), or BROKEN (edge referencing missing node).", cmd = "./ontoref sync diff", actor = 'Both, depends_on = [{ step = "scan" }], on_error = { strategy = 'Stop }, }, { id = "doc-drift", action = "Compare crate-level //! doc comments against ontology node descriptions. Reports DRIFT for nodes whose description is empty or diverges significantly (Jaccard < 25%) from what the crate itself documents.", cmd = "ontoref sync diff --docs", actor = 'Both, depends_on = [{ step = "diff" }], on_error = { strategy = 'Continue }, note = "Skipped silently if no crates have //! doc comments. Requires src/lib.rs or src/main.rs with //! lines.", }, { id = "propose", action = "Generate Nickel code for new nodes (MISSING), mark stale nodes for removal, generate updated nodes for DRIFT, mark broken edges for deletion.", cmd = "ontoref sync propose", actor = 'Both, depends_on = [{ step = "doc-drift" }], on_error = { strategy = 'Stop }, }, { id = "review", action = "Human reviews the proposal. Agent can skip this step.", actor = 'Human, depends_on = [{ step = "propose" }], on_error = { strategy = 'Stop }, }, { id = "apply", action = "Apply approved changes to .ontology/core.ncl. Each category (add/remove/update) confirmed separately.", cmd = "./ontoref sync apply", actor = 'Human, depends_on = [{ step = "review" }], on_error = { strategy = 'Stop }, }, { id = "verify", action = "Run nickel typecheck on modified files. Execute sync diff again to confirm zero drift.", cmd = "nickel typecheck .ontology/core.ncl && ./ontoref sync diff", actor = 'Both, depends_on = [{ step = "apply" }], on_error = { strategy = 'Stop }, }, ], postconditions = [ "nickel export .ontology/core.ncl succeeds without errors", "sync diff reports zero MISSING, STALE, or BROKEN items", "All existing ADR Hard constraints still pass", ], }