provisioning-code/.ncl-cache/2c938ce3d1604daeffcc372d32949bcb160a0e79a451ece510f71bd27b02f570.json

67 lines
5.3 KiB
JSON

{
"alternatives_considered": [
{
"option": "Wrap each call site individually with do { } | complete (existing pattern)",
"why_rejected": "Works only for external commands, not for Nu plugin commands. Plugin commands raise LabeledError — not catchable via complete. Keeping ^nickel export at call sites means all cache benefits are lost."
},
{
"option": "Single ncl-export.nu wrapper delegating to ^nickel export with inline cache check",
"why_rejected": "Duplicates the cache logic already inside nu_plugin_nickel. Two cache implementations with different key strategies would diverge. The plugin is the correct cache owner — the wrapper should delegate to it."
},
{
"option": "Migrate all 124 call sites at once",
"why_rejected": "Risk surface too large. Priority-ordered migration (C1 hot-path first) allows validating cache correctness on the most-exercised paths before touching validation, bootstrap, and diagnostic paths that are harder to test."
}
],
"consequences": {
"negative": [
"nu_plugin_nickel must be registered in the Nu session for performance benefits; unregistered sessions fall back to the `^nickel export` path in nickel-eval-soft (via the catch branch)",
"Block C2/C3 (remaining 120 call sites) are not yet migrated — those paths still use ^nickel export directly"
],
"positive": [
"Hot-path call sites (4 files, C1) are now cache-backed via nu_plugin_nickel",
"Single try/catch location for soft-failure pattern — easy to audit",
"| from json eliminated from migrated call sites"
]
},
"constraints": [
{
"check_hint": "grep '^nickel export' provisioning/core/nulib/main_provisioning/{dispatcher,components,workflow,extensions}.nu — must return empty",
"claim": "Hot-path files (C1) must not contain direct ^nickel export calls after migration",
"id": "c1-no-direct-nickel-export",
"rationale": "Direct ^nickel export in C1 files bypasses the plugin cache, negating the performance benefit of ADR-022. All C1 exports must go through ncl-eval or ncl-eval-soft.",
"scope": "dispatcher.nu, components.nu, workflow.nu, extensions.nu",
"severity": "Hard"
}
],
"context": "After ADR-022 established the ncl-sync daemon and shared cache, Nu call sites needed to be migrated from `^nickel export --format json ... | from json` to the plugin. Two call patterns exist: hard-failure (export failure should propagate as an error — uses `error make`) and soft-failure (export failure should return a fallback value — uses `if $result.exit_code != 0`). Distributing try/catch across 124 call sites would violate the guideline against widespread use of try/catch for Nu plugin commands.",
"date": "2026-04-16",
"decision": "Two wrapper functions in `lib_provisioning/utils/nickel_processor.nu` serve as the single abstraction layer: `ncl-eval [path import_paths]` for hard-failure call sites (error propagates from the plugin directly — no try/catch needed), and `ncl-eval-soft [path import_paths fallback]` for soft-failure call sites (a single try/catch returns `fallback` on any plugin error). Block C1 migrates the four hot-path call sites: `dispatcher.nu` (commands-registry), `components.nu` (comp-ncl-export helper + servers.ncl), `workflow.nu` (wf-ncl-export helper + settings.ncl + state.ncl), `extensions.nu` (metadata.ncl per taskserv). Block C2/C3 cover the remaining operation-path and validation call sites.",
"id": "adr-023",
"ontology_check": {
"decision_string": "ncl-eval + ncl-eval-soft wrappers in nickel_processor.nu replace ^nickel export at hot-path call sites; single try/catch in ncl-eval-soft",
"invariants_at_risk": [
"config-driven-always"
],
"verdict": "Safe"
},
"rationale": [
{
"claim": "ncl-eval-soft isolates the single try/catch to one location",
"detail": "In Nu 0.111.0, try/catch is valid for Nu internal commands (including plugins). However, dispersing try/catch across dozens of call sites increases cognitive load and creates inconsistency. Centralizing it in ncl-eval-soft means the pattern is reviewed once and applied uniformly. Callers declare intent via the `fallback` parameter (`{}`, `[]`, or `null`) rather than embedding error-handling logic inline."
},
{
"claim": "ncl-eval (hard-failure) requires no try/catch — plugin LabeledError propagates naturally",
"detail": "When `nickel-eval` fails, it raises a `LabeledError` that Nu surfaces as a structured error. This is identical in behavior to `error make { msg: ... }` in the existing code. The call site is simply `ncl-eval $path [$ws $prov]` — one line instead of four. No error handling is needed because the error propagation is the correct behavior."
},
{
"claim": "nickel-eval returns Nu values natively, eliminating | from json",
"detail": "The plugin converts `serde_json::Value` to `nu_protocol::Value` via `json_value_to_nu_value`. Call sites receive a Nu record or list directly and can use cell path access (`$data.components`, `$data.dimensions`) without an intermediate string parse step. This removes a class of parse errors where `from json` would fail on empty stdout from a cached result."
}
],
"related_adrs": [
"adr-022-ncl-sync-daemon"
],
"status": "Accepted",
"title": "ncl-eval wrapper: nu_plugin_nickel as the single ^nickel export abstraction in Nu"
}