{
  "adr_bodies_format": 1,
  "engine_commit": "5d61b7e13f1f6a10c17414f61146648f76bcf2bd",
  "release_tag": "v0.107.0",
  "law": "roomed = every status except `proposed` — a proposal published as an indexed page invites citation as though it were settled",
  "bodies": [
    {
      "id": "ADR-001",
      "file": "adr-001-diamond-orphan-branch.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 514,
      "sha256": "2065043ff330e1d2dec8969d6deff23c7112861ece6551116306015be513b7b6",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-001: Diamond — orphan branch rewrite instead of iterative refactor",
          "id": "adr-001-diamond--orphan-branch-rewrite-instead-of-iterative-refactor"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Legacy Nika ("
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": ", v0.79.3) had reached 322k LOC across 31 crates with 1,276 "
            },
            {
              "k": "code",
              "text": ".unwrap()"
            },
            {
              "k": "text",
              "text": " calls and 47 files above the 1,500-LOC cap. The single biggest correctness risk became "
            },
            {
              "k": "strong",
              "text": "context loss"
            },
            {
              "k": "text",
              "text": ": no AI assistant could fit the whole system in a single context window. The math in "
            },
            {
              "k": "code",
              "text": "ROADMAP.md"
            },
            {
              "k": "text",
              "text": " is explicit — a 138k-LOC monolith ("
            },
            {
              "k": "code",
              "text": "nika-core"
            },
            {
              "k": "text",
              "text": ") ≈ 750k tokens ≈ 75% of a 1M-token context just to read one crate. Reviewing any non-trivial change devolved into pattern-matching against partial slices, which is how hallucinated refactors land."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Shadow-zone audit (16-agent Rust Council, 2026-04-13) confirmed 7 subsystems where documentation diverged from implementation. Incremental refactor would require untangling these in-place while maintaining compat."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Create an "
            },
            {
              "k": "strong",
              "text": "orphan branch `nika-diamond`"
            },
            {
              "k": "text",
              "text": " from scratch. No code inherited from "
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": ". Legacy "
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": " becomes a read-only reference via "
            },
            {
              "k": "code",
              "text": "git show brouillon:path"
            },
            {
              "k": "text",
              "text": ". Every line of the diamond is "
            },
            {
              "k": "strong",
              "text": "rewritten"
            },
            {
              "k": "text",
              "text": ", not ported. 32 legacy crate directories are excluded from "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " members via the "
            },
            {
              "k": "code",
              "text": "exclude"
            },
            {
              "k": "text",
              "text": " key — they sit in the orphan working tree but do not participate in the workspace."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Legacy "
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": " stays frozen at v0.79.3. The diamond starts at "
            },
            {
              "k": "strong",
              "text": "v0.80.0"
            },
            {
              "k": "text",
              "text": " to mark the boundary. Users on v0.79.x cannot benefit until v0.90 feature parity."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Each diamond crate fits entirely in an AI context window (≤15k LOC ≈ 70k tokens ≈ 7% of 1M-token context, 10× headroom)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Zero accumulated legacy debt admitted — each crate enters via the 12-gate protocol (see ADR-003)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Clean APIs enforced up-front; no breaking-change migrations mid-Diamond."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Approximately 11–12 months to feature parity (v0.90 milestone). Substantially longer than incremental refactor."
              }
            ],
            [
              {
                "k": "text",
                "text": "No interim releasable state for Diamond users; the v0.79→v0.80 cliff is real."
              }
            ],
            [
              {
                "k": "text",
                "text": "5 admitted crates at v0.80.0-alpha.x means tiny installable surface today."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Legacy bug fixes (CVE-class only) require separate back-port commits on "
              },
              {
                "k": "code",
                "text": "main"
              },
              {
                "k": "text",
                "text": ". None expected post-abandonment."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " lines 1–17 — 5 admitted "
              },
              {
                "k": "code",
                "text": "members"
              },
              {
                "k": "text",
                "text": ", 32 excluded legacy crates"
              }
            ],
            [
              {
                "k": "code",
                "text": "ROADMAP.md"
              },
              {
                "k": "text",
                "text": " lines 17–35 — \"Why Diamond\" context-window argument"
              }
            ],
            [
              {
                "k": "code",
                "text": "CHANGELOG.md"
              },
              {
                "k": "text",
                "text": " lines 307–320 — v0.80.0-alpha.0 commit "
              },
              {
                "k": "code",
                "text": "42909b1c7"
              }
            ],
            [
              {
                "k": "code",
                "text": "DIAMOND.md"
              },
              {
                "k": "text",
                "text": " lines 40–53 — orphan-branch rationale"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "POST_AUDIT_REVISIONS.md"
              },
              {
                "k": "text",
                "text": " — 10 locked decisions, 2026-04-13"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Iterative in-place refactor",
          "id": "alt-a--iterative-in-place-refactor"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Split each oversized file, add gates progressively, delete unwraps crate-by-crate on "
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": ". Rejected because the ~8k-LOC "
            },
            {
              "k": "code",
              "text": "nika-core/src/binding/"
            },
            {
              "k": "text",
              "text": " alone would need 4–5 ADR-scale decisions each and the audit proved human+AI context capacity would not hold across 322k LOC over 11+ months."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Workspace overlay (additive)",
          "id": "alt-b--workspace-overlay-additive"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Keep "
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": ", add new crates alongside, migrate consumers lazily. Rejected because compat tax (dual APIs, feature flags, fallback logic) would re-create the 2,945-LOC "
            },
            {
              "k": "code",
              "text": "resolve.rs"
            },
            {
              "k": "text",
              "text": " class of file inside the new crates within months."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-003 — 12-gate admission (the quality floor every diamond crate must pass)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-004 — context-window-sized crates (the sizing rule the rewrite enables)"
              }
            ],
            [
              {
                "k": "code",
                "text": "archive/nika-v0.79/adr/"
              },
              {
                "k": "text",
                "text": " (supernovae-hq monorepo) — 8 pre-Diamond ADRs, superseded by this rewrite"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Revisit if a v0.95 feature requires legacy behavior that was never documented. Otherwise this ADR is terminal — orphan rewrites are not reverted."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-002",
      "file": "adr-002-forever-v0x.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 979,
      "sha256": "70ccc92a28475dfc95af015773077105d04d0e3edbac6915fcd263e42b48d572",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-002: Forever v0.x — no v1.0 target",
          "id": "adr-002-forever-v0x--no-v10-target"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "⚠️ AMENDED 2026-06-20 (D-2026-06-20-N1) — this doctrine is RETIRED."
            },
            {
              "k": "text",
              "text": "\nThe \"forever-v0.x · no v1.0 target\" model described below is "
            },
            {
              "k": "strong",
              "text": "superseded"
            },
            {
              "k": "text",
              "text": ".\nNika now follows "
            },
            {
              "k": "strong",
              "text": "real semver toward a 1.0.0 public launch"
            },
            {
              "k": "text",
              "text": " (the engine\nis at 0.90.0 RC-grade; 1.0.0 ships when the 7 shadow zones are green; the\nremaining crates land additively across the 1.x minors). The "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": "\nLANGUAGE envelope is frozen forever and is unaffected (a different axis).\nThe original text is preserved below per "
            },
            {
              "k": "code",
              "text": "cross-source-validation.md"
            },
            {
              "k": "text",
              "text": " §2.7."
            }
          ]
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "⚠️ AMENDED 2026-06-20 (D-2026-06-20-N1) · \"forever-v0.x · no v1.0 ever\" is RETIRED → real semver toward a 1.0 launch."
            },
            {
              "k": "text",
              "text": "\nThe decision below is SUPERSEDED at the version-policy layer. Canon courant ·"
            },
            {
              "k": "text",
              "text": "The "
            },
            {
              "k": "code",
              "text": "v0.8X.Y"
            },
            {
              "k": "text",
              "text": " layer-phase tag scheme and the \"42 crates @ v0.90 milestone /\nno v1.0\" framing are SUPERSEDED · 42 crates is now a post-1.0 target reached\nacross 1.x minors. The original body is preserved below per\ncross-source-validation §2.7 (zero silent rewrite · this ADR's filename + title\nstay frozen as historical anchors)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika is developed primarily by a solo author augmented by AI. Committing to a v1.0 target introduces two failure modes: (a) rushing breaking changes into a 1.0 window to \"lock the API,\" producing a worse API than iteration would; (b) refusing to change load-bearing APIs post-1.0 because SemVer punishes breaking changes on major releases. Neither outcome serves correctness."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Context: legacy Nika had 17 releases in v0.x space (v0.1 → v0.79.3) across ~6 months of solo+AI work. Phase D (Diamond) is projected at ~10–11 months to v0.90 milestone. Applying strict SemVer 1.0 semantics over that horizon forces compat decisions that conflict with Diamond's \"zero dead code\" philosophy."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Nika remains at v0.x indefinitely."
            },
            {
              "k": "text",
              "text": " No v1.0 target. Ever."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Phase milestones (v0.90, v0.95, v0.100) are "
              },
              {
                "k": "strong",
                "text": "markers"
              },
              {
                "k": "text",
                "text": ", not contracts. They signal accumulated crate admissions (40–42 @ v0.90, ~63 @ v0.95, ~75 @ v0.100, cap 100)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Breaking changes ship on MINOR bumps (v0.90 → v0.91) without ceremony. "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "pub fn new()"
              },
              {
                "k": "text",
                "text": " (ADR-007) preserve forward-compat "
              },
              {
                "k": "em",
                "text": "by construction"
              },
              {
                "k": "text",
                "text": " within minor ranges for downstream struct-literal use."
              }
            ],
            [
              {
                "k": "text",
                "text": "The CHANGELOG distinguishes "
              },
              {
                "k": "code",
                "text": "Breaking"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Added"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Changed"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Fixed"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Deprecated"
              },
              {
                "k": "text",
                "text": " sections. Breaking changes are explicit but not gated by a SemVer major."
              }
            ],
            [
              {
                "k": "text",
                "text": "Public documentation ("
              },
              {
                "k": "code",
                "text": "README.md"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MANIFESTO.md"
              },
              {
                "k": "text",
                "text": ") states this explicitly so users adopt with correct expectations."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Freedom to refactor load-bearing APIs when evidence demands (post-audit findings, shadow-zone resolutions)."
              }
            ],
            [
              {
                "k": "text",
                "text": "No artificial \"pre-1.0\" vs \"post-1.0\" discipline gap. Every release held to the same quality floor."
              }
            ],
            [
              {
                "k": "text",
                "text": "Quality-first signaling: a v0.95 release with 95% mutation coverage is tangibly better than a v1.0 with compat shims."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Some ecosystem tools (package registries, dependabot rules) treat pre-1.0 crates as \"unstable\" and apply stricter update policies. Acceptable — Nika distributes as a CLI + library, not a churned dependency."
              }
            ],
            [
              {
                "k": "text",
                "text": "Requires explicit user education: "
              },
              {
                "k": "code",
                "text": "v0.x"
              },
              {
                "k": "text",
                "text": " here ≠ \"pre-alpha quality.\" README must state this."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "AGPL license already signals long-term project commitment independent of version number."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "ROADMAP.md"
              },
              {
                "k": "text",
                "text": " — \"forever v0.x\" in header, phased crate counts"
              }
            ],
            [
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " line 19 — "
              },
              {
                "k": "code",
                "text": "version = \"0.80.0\""
              },
              {
                "k": "text",
                "text": " at workspace root, inherited"
              }
            ],
            [
              {
                "k": "code",
                "text": "CHANGELOG.md"
              },
              {
                "k": "text",
                "text": " header — explicit \"Nika follows forever-v0.x\" statement"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "feedback_no_deadlines.md"
              },
              {
                "k": "text",
                "text": " — \"never pressure with timelines\""
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "POST_AUDIT_REVISIONS.md"
              },
              {
                "k": "text",
                "text": " — \"Version model: Forever v0.x\""
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Target v1.0 at milestone v0.90",
          "id": "alt-a--target-v10-at-milestone-v090"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Ship v1.0.0 once the 40–42 admitted crates hit the quality bar. Rejected: v0.90 is an arbitrary crate count, not an API stabilization moment. Memory features (v0.95) and agent-v2 (v0.100) are forward-known to require kernel API evolution; committing v1.0 before those land is dishonest."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Calendar versioning (YYYY.MM)",
          "id": "alt-b--calendar-versioning-yyyymm"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "2026.04"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "2026.10"
            },
            {
              "k": "text",
              "text": ". Rejected: loses the \"quality ratchets up with number\" signal. Users infer from v0.80 → v0.90 that substantial progress happened."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-001 — orphan rewrite (why we're starting the v0.8x line)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants (how we preserve struct-literal compat within minor ranges)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "If the ecosystem changes such that \"forever v0.x\" actively harms adoption (e.g., major packaging tool blocks < 1.0 by policy), revisit. Not expected within 24 months."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-003",
      "file": "adr-003-12-gate-admission.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        },
        {
          "title": "Amendment — 2026-06-10 · social → structural enforcement",
          "anchor": "amendment--2026-06-10--social--structural-enforcement"
        }
      ],
      "words": 876,
      "sha256": "6e32089e6044a389790f9e70acd199fc991ad46660e49c90738b826f0d63811d",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-003: 12-gate crate admission protocol",
          "id": "adr-003-12-gate-crate-admission-protocol"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The Diamond rewrite (ADR-001) is valuable only if the new crates don't accumulate the same debt as legacy (1,276 unwraps, 47 oversized files, silent rot). Code review alone failed in the legacy codebase — reviews happened, yet a P0 audit discovered 29 broken MCP aliases that had lived for months. The enforcement must be "
            },
            {
              "k": "strong",
              "text": "mechanical"
            },
            {
              "k": "text",
              "text": ", not social."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Five crates have been admitted to date ("
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-catalog"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-catalog-verify"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-kernel-mock"
            },
            {
              "k": "text",
              "text": "). Each carries the commit body listing all 12 gate results. The process works under load."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Every crate passes "
            },
            {
              "k": "strong",
              "text": "12 sequential gates"
            },
            {
              "k": "text",
              "text": " before its path is added to "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " members:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "# · Gate · Enforces"
              }
            ],
            [
              {
                "k": "text",
                "text": "1 · SPEC doc · crate purpose + API surface documented in `docs/crate-specs/<name>.md`"
              }
            ],
            [
              {
                "k": "text",
                "text": "2 · TDD red/green · failing tests written before implementation"
              }
            ],
            [
              {
                "k": "text",
                "text": "3 · IMPL compiles · workspace builds with the new crate added"
              }
            ],
            [
              {
                "k": "text",
                "text": "4 · Zero Clippy warnings · `cargo clippy -- -D warnings` clean"
              }
            ],
            [
              {
                "k": "text",
                "text": "5 · Mutation ≥90% · `cargo-mutants` kills ≥90% of mutants (or documented exemption)"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 · Property tests · `proptest` on security/parser/boundary code"
              }
            ],
            [
              {
                "k": "text",
                "text": "7 · Benchmarks · `criterion` on hot paths if applicable"
              }
            ],
            [
              {
                "k": "text",
                "text": "8 · Zero `cargo doc` warnings · `RUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps` clean"
              }
            ],
            [
              {
                "k": "text",
                "text": "9 · Canary E2E · 1 real workflow exercises the crate end-to-end (or exempted for infra crates)"
              }
            ],
            [
              {
                "k": "text",
                "text": "10 · Golden parity test · output matches legacy where applicable"
              }
            ],
            [
              {
                "k": "text",
                "text": "11 · 3-agent review swarm · parallel review by fresh agents, all findings resolved"
              }
            ],
            [
              {
                "k": "text",
                "text": "12 · Atomic commit · single commit with full admission body"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Exemptions (e.g., Gate 9 N/A for "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": ") require written justification in the crate spec. No gate skipping without explicit record."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Admission commit body format is mandatory (see "
            },
            {
              "k": "code",
              "text": ".claude/rules/commit-granularity.md"
            },
            {
              "k": "text",
              "text": ")."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Every admitted crate starts at a correctness floor legacy never achieved: 0 unwraps, 0 clippy warnings, ≥90% mutation killed, documented public API."
              }
            ],
            [
              {
                "k": "text",
                "text": "Regression detection front-loaded, not retrofitted."
              }
            ],
            [
              {
                "k": "text",
                "text": "Proven on 5 crates: "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " 100% mutation, "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " 100% mutation, "
              },
              {
                "k": "code",
                "text": "nika-kernel-mock"
              },
              {
                "k": "text",
                "text": " 95.7%."
              }
            ],
            [
              {
                "k": "text",
                "text": "Bisect-friendly history: one crate = one atomic commit."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Admission is expensive per crate (a full session per admission in some cases)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gate 5 ("
              },
              {
                "k": "code",
                "text": "cargo-mutants"
              },
              {
                "k": "text",
                "text": ") is slow on larger crates — up to ~20 minutes on "
              },
              {
                "k": "code",
                "text": "nika-catalog"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gate 9 (E2E) is N/A for infrastructure crates, requiring explicit documented exemptions."
              }
            ],
            [
              {
                "k": "text",
                "text": "Velocity-over-Safety tension: an under-pressure solo dev might rationalize skipping a gate. The process must be socially enforced via memory + reminders."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Agent review (Gate 11) requires ~3 parallel sub-agents per admission — acceptable cost given error detection rate."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": ".claude/CLAUDE.md"
              },
              {
                "k": "text",
                "text": " — 12-gates summary"
              }
            ],
            [
              {
                "k": "code",
                "text": ".claude/rules/commit-granularity.md"
              },
              {
                "k": "text",
                "text": " — mandatory commit body format"
              }
            ],
            [
              {
                "k": "code",
                "text": "CHANGELOG.md"
              },
              {
                "k": "text",
                "text": " lines 196–216 — "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " admission with all 12 gates confirmed, commit "
              },
              {
                "k": "code",
                "text": "ef8804371"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/"
              },
              {
                "k": "text",
                "text": " — check scripts (≈one per Gate): "
              },
              {
                "k": "code",
                "text": "check-clippy.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-crate-size.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-dead-code.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-expect.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-fn-length.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-loc-limits.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-no-default-features.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-tests.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-unwrap.sh"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "check-adr-coverage.sh"
              },
              {
                "k": "text",
                "text": " (see ADR-009), "
              },
              {
                "k": "code",
                "text": "check-mutation-floor.sh"
              },
              {
                "k": "text",
                "text": " (the REAL Gate 5 executor, 2026-06-10)"
              }
            ],
            [
              {
                "k": "code",
                "text": ".github/workflows/diamond-ci.yml"
              },
              {
                "k": "text",
                "text": " — CI matrix running the gates"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "crate-admit"
              },
              {
                "k": "text",
                "text": " skill + "
              },
              {
                "k": "code",
                "text": "gate-check"
              },
              {
                "k": "text",
                "text": " skill — automation entrypoints"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Code review only",
          "id": "alt-a--code-review-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected empirically — legacy had reviews, still accumulated 1,276 unwraps."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — 8-gate protocol",
          "id": "alt-b--8-gate-protocol"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Earlier draft. Rejected because mutation coverage, property tests, and the agent review swarm each caught real issues in admission sessions. Downgrading to 8 would re-open those failure modes."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Gates as PR-level CI only (not admission commit)",
          "id": "alt-c--gates-as-pr-level-ci-only-not-admission-commit"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected because CI failure on a PR that has already touched 6 crates is expensive to unwind. Gates run "
            },
            {
              "k": "em",
              "text": "before"
            },
            {
              "k": "text",
              "text": " admission commit lands on nika-diamond."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-001 — orphan rewrite (the reason this process exists)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-004 — context-window sizing (why we have ~40 admissions to run)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-009 — ADR process (Gate 12 invokes ADR check)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crate-admit"
              },
              {
                "k": "text",
                "text": " skill — guided admission workflow"
              }
            ],
            [
              {
                "k": "code",
                "text": "gate-check"
              },
              {
                "k": "text",
                "text": " skill — run gates against an in-flight crate"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "If a future gate proves redundant (e.g., Gate 10 golden parity becomes meaningless post-v0.95), revisit and supersede. Gate count is not sacred — enforcement floor is."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Amendment — 2026-06-10 · social → structural enforcement",
          "id": "amendment--2026-06-10--social--structural-enforcement"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The Context section's thesis (\"enforcement must be "
            },
            {
              "k": "strong",
              "text": "mechanical"
            },
            {
              "k": "text",
              "text": ", not social\")\nand the Negative note (\"an under-pressure solo dev might rationalize skipping a\ngate · socially enforced via memory + reminders\") had two residual honor-system\ngaps. Both are now structurally closed (no decision change · additive per\n"
            },
            {
              "k": "code",
              "text": "cross-source-validation.md"
            },
            {
              "k": "text",
              "text": " §2.7):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Gate 5 (mutation ≥90%)"
              },
              {
                "k": "text",
                "text": " was checked only by "
              },
              {
                "k": "em",
                "text": "presence"
              },
              {
                "k": "text",
                "text": " (the crate-spec\nmentioning \"Mutation\"). Now "
              },
              {
                "k": "code",
                "text": "scripts/ci/check-mutation-floor.sh <crate> [floor]"
              },
              {
                "k": "text",
                "text": " actually runs "
              },
              {
                "k": "code",
                "text": "cargo-mutants"
              },
              {
                "k": "text",
                "text": ", parses the kill ratio (caught /\nviable, excluding unviable), and fails below the floor. Admission/CI-tier\n(minutes-slow — not pre-commit)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Gate 2 / ADR-081 guards"
              },
              {
                "k": "text",
                "text": " for L1 computer-use crates ("
              },
              {
                "k": "code",
                "text": "nika-screen"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "nika-a11y"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-input"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-browser"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-vision-local"
              },
              {
                "k": "text",
                "text": ") were\n\"MANDATORY-at-admission\" in prose only. Now hygiene vector 35\n"
              },
              {
                "k": "code",
                "text": "scripts/hygiene/check-adr-081-guards.sh"
              },
              {
                "k": "text",
                "text": " reads the ADR-081 ownership matrix\nand FAILS if a workspace guard-owner lacks its guard impl+test (binding in\n"
              },
              {
                "k": "code",
                "text": "scripts/ci/adr-081-guard-manifest.tsv"
              },
              {
                "k": "text",
                "text": "). Declarative/evolutive: a future\nguard-owner (e.g. "
              },
              {
                "k": "code",
                "text": "nika-input"
              },
              {
                "k": "text",
                "text": " M2.4 Guards 1+2) goes RED at admission until\nthe guard ships."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Companion supply-chain hardening (same arc): hygiene vectors 34\n("
            },
            {
              "k": "code",
              "text": "check-cargo-deny.sh"
            },
            {
              "k": "text",
              "text": " — bans/licenses/sources, superset of vector 15 advisories)\nand 36 ("
            },
            {
              "k": "code",
              "text": "check-unused-deps.sh"
            },
            {
              "k": "text",
              "text": " — cargo-machete)."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-004",
      "file": "adr-004-context-window-sized-crates.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 677,
      "sha256": "51721d8b7ee7df1afbf601aa206b068fa747be52ce8829bc4856e5695713e904",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-004: Context-window-sized crate architecture",
          "id": "adr-004-context-window-sized-crate-architecture"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The fundamental premise of the Diamond rewrite (ADR-001) is that each crate must fit in an AI assistant's context window to prevent hallucinated refactors. The math: 15k LOC ≈ 70k tokens ≈ 7% of a 1M-token context window, leaving 93% for tests, kernel traits, and working space. Legacy "
            },
            {
              "k": "code",
              "text": "nika-core"
            },
            {
              "k": "text",
              "text": " was a 138k-LOC monolith — effectively invisible to any AI reviewer."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Post-audit (2026-04-13) counted crates by natural boundary using the \"≥2 distinct consumers\" rule. The initial draft (32–34 crates at v0.90) was expanded on 2026-04-14 after the pck + native-adapter brainstorm to "
            },
            {
              "k": "strong",
              "text": "40–42 crates at v0.90"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Hard caps enforced by CI (see ADR-009):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Per-crate LOC cap:"
              },
              {
                "k": "text",
                "text": " 15,000 lines"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Per-file LOC cap:"
              },
              {
                "k": "text",
                "text": " 1,500 lines"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Per-function line cap:"
              },
              {
                "k": "text",
                "text": " 100 lines"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Total crate count cap:"
              },
              {
                "k": "text",
                "text": " 100 (hard limit; admissions past this require gate-review)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Crates with fewer than 2 distinct consumers are merged (examples: "
            },
            {
              "k": "code",
              "text": "nika-lsp-core"
            },
            {
              "k": "text",
              "text": " merged into "
            },
            {
              "k": "code",
              "text": "nika-lsp"
            },
            {
              "k": "text",
              "text": "; "
            },
            {
              "k": "code",
              "text": "nika-policy"
            },
            {
              "k": "text",
              "text": " is a module of "
            },
            {
              "k": "code",
              "text": "nika-runtime"
            },
            {
              "k": "text",
              "text": ", not a crate). "
            },
            {
              "k": "code",
              "text": "nika-macros"
            },
            {
              "k": "text",
              "text": " was removed entirely (Q1 decision 2026-04-16: manual impl + "
            },
            {
              "k": "code",
              "text": "macro_rules!"
            },
            {
              "k": "text",
              "text": ", no proc macros in L0)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Heavy-dep isolation justifies aggressive splits: "
            },
            {
              "k": "code",
              "text": "nika-provider"
            },
            {
              "k": "text",
              "text": " becomes 3 crates ("
            },
            {
              "k": "code",
              "text": "nika-provider-rig"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-provider-native"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-provider-mock"
            },
            {
              "k": "text",
              "text": ") because "
            },
            {
              "k": "code",
              "text": "mistral.rs"
            },
            {
              "k": "text",
              "text": " CUDA/Metal features are incompatible with "
            },
            {
              "k": "code",
              "text": "rig-core"
            },
            {
              "k": "text",
              "text": "'s cloud dep graph. "
            },
            {
              "k": "code",
              "text": "nika-media"
            },
            {
              "k": "text",
              "text": " becomes 5 crates (pdfium, c2pa, resvg, image, CAS) to isolate C-FFI and large binary deps."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Phased target table:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Phase · Target count · Running total"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.80.x (current) · 5 · 5"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.90 · 32–34 core + 8 new (pck + native adapters) · **40–42**"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.95 · +14–15 (Cortex + media + natives) · ~63–65"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.100 · +4–6 (observability + LSP + WASM) · ~72–75"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.110+ · incremental · cap **100**"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "An AI reviewer working on "
              },
              {
                "k": "code",
                "text": "nika-catalog"
              },
              {
                "k": "text",
                "text": " (4,690 LOC) holds the entire crate + tests + kernel traits simultaneously. No blind spots."
              }
            ],
            [
              {
                "k": "text",
                "text": "12-gate protocol (ADR-003) is feasible at this scale; it would be impractical on 138k-LOC crates."
              }
            ],
            [
              {
                "k": "text",
                "text": "Dep isolation from provider/media splits reduces binary size and compile time for downstream consumers (CLI users don't pull PDFium unless they opt in)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "40-42 crates creates substantial "
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " ceremony and cross-crate import discipline overhead."
              }
            ],
            [
              {
                "k": "text",
                "text": "The merge/split decisions require ongoing gate-review (≥2-consumers rule applied but judgment-loaded on new crates)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Admission overhead at 40+ crates is a major driver of the 11–12 month v0.90 timeline."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Workspace-level dependency deduplication (one pin per external crate in "
              },
              {
                "k": "code",
                "text": "[workspace.dependencies]"
              },
              {
                "k": "text",
                "text": ") mitigates the compile-time concern."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " lines 1–17 — 5 admitted members, 32 excluded"
              }
            ],
            [
              {
                "k": "code",
                "text": "ROADMAP.md"
              },
              {
                "k": "text",
                "text": " lines 17–35 — \"Why Diamond\" context-window math"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/check-crate-size.sh"
              },
              {
                "k": "text",
                "text": " — enforces 15k LOC cap per crate"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/check-loc-limits.sh"
              },
              {
                "k": "text",
                "text": " — enforces 1,500 LOC cap per file"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/check-fn-length.sh"
              },
              {
                "k": "text",
                "text": " — enforces 100-line cap per function"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "POST_AUDIT_REVISIONS.md"
              },
              {
                "k": "text",
                "text": " lines 18–64 — phased crate count table, merge/split decisions"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "RUST_ENFORCEMENT.md"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "check_layers.rs"
              },
              {
                "k": "text",
                "text": " pattern for layer-level ordering enforcement (see ADR-006)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — 20–25 crates, looser caps (40k LOC per crate)",
          "id": "alt-a--2025-crates-looser-caps-40k-loc-per-crate"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Closer to Nushell (43 crates) or DataFusion (47). Rejected because 40k LOC = 180k tokens = 18% of context, cutting reasoning headroom in half. Shadow zones found historically in files >1,500 LOC (legacy "
            },
            {
              "k": "code",
              "text": "resolve.rs"
            },
            {
              "k": "text",
              "text": " at 2,945 LOC); enforcing the file cap is empirically valuable."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — 10–12 mega-crates, no caps",
          "id": "alt-b--1012-mega-crates-no-caps"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected immediately — recreates the monolith problem."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Unbounded crate count, organic splits",
          "id": "alt-c--unbounded-crate-count-organic-splits"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected because 100+ crates would violate the \"≥2 consumers\" rule and produce coordination overhead."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-001 — orphan rewrite (the thesis that produced this sizing)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-003 — 12-gate admission (the process the sizing enables)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-006 — layered kernel + ISP traits (why even 15k-LOC crates have clear internal structure)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-011 — "
              },
              {
                "k": "code",
                "text": "cargo xtask"
              },
              {
                "k": "text",
                "text": " (excluded from the 100-crate cap by convention; see ADR-011 Decision §2)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Revisit the 100-crate cap after v0.100. If the ecosystem grows beyond what 100 crates can express, consider spinning satellite workspaces rather than expanding the cap — 100 is a humility check, not a hard engineering limit."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-005",
      "file": "adr-005-error-hierarchy.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 644,
      "sha256": "fb1e494ec115c0cfd34a2b7c1308faa9a261ae23e3130a3be9f9e56ee1f42e43",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-005: Trait-based error hierarchy (`NikaErrorCode` + `Box<dyn>` + code registry)",
          "id": "adr-005-trait-based-error-hierarchy-nikaerrorcode--boxdyn--code-registry"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Legacy Nika had a god "
            },
            {
              "k": "code",
              "text": "NikaError"
            },
            {
              "k": "text",
              "text": " enum with hundreds of variants spread across crates. Shadow-zone audit (2026-04-13) identified this as zone #6: "
            },
            {
              "k": "strong",
              "text": "95% of variants lacked Display golden tests"
            },
            {
              "k": "text",
              "text": ", and error codes had silently collided. Downstream crates had to depend on the central enum, creating cyclic dep risks."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Three alternatives were weighed during "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " admission:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "A — `anyhow::Error` everywhere:"
              },
              {
                "k": "text",
                "text": " opaque, no structured codes, loses the wire-format IDs ("
              },
              {
                "k": "code",
                "text": "NIKA-140"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "NIKA-384"
              },
              {
                "k": "text",
                "text": ") that Shield and CLI rely on."
              }
            ],
            [
              {
                "k": "strong",
                "text": "B — single `thiserror` enum in a root crate:"
              },
              {
                "k": "text",
                "text": " every crate must depend on the enum; adding a variant touches the root crate and rebuilds the world; circular-dep magnet."
              }
            ],
            [
              {
                "k": "strong",
                "text": "C+ — trait-based hierarchy:"
              },
              {
                "k": "text",
                "text": " per-crate error enums implement a shared trait; unified at API boundaries via "
              },
              {
                "k": "code",
                "text": "Box<dyn>"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Option C+\" — trait-based error hierarchy."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": " trait lives in "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " (L0). Every crate's error enum implements it."
              }
            ],
            [
              {
                "k": "code",
                "text": "NikaError = Box<dyn NikaErrorCode>"
              },
              {
                "k": "text",
                "text": " — dynamic dispatch at API boundaries."
              }
            ],
            [
              {
                "k": "code",
                "text": "NikaCode"
              },
              {
                "k": "text",
                "text": " carries a "
              },
              {
                "k": "strong",
                "text": "dual representation"
              },
              {
                "k": "text",
                "text": ": wire format "
              },
              {
                "k": "code",
                "text": "\"NIKA-140\""
              },
              {
                "k": "text",
                "text": " for Display/logs AND a structured "
              },
              {
                "k": "code",
                "text": "{num: u16, category: Category, severity: Severity, slug: &'static str}"
              },
              {
                "k": "text",
                "text": " for JSON serialization and programmatic routing."
              }
            ],
            [
              {
                "k": "code",
                "text": "CoreError"
              },
              {
                "k": "text",
                "text": " handles cross-cutting errors that don't belong to any specific crate (config, bootstrap, etc.)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Error code ranges reserved per subsystem"
              },
              {
                "k": "text",
                "text": " in "
              },
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": ":"
              },
              {
                "k": "text",
                "text": "\n\n"
              },
              {
                "k": "text",
                "text": "| Range | Subsystem |\n|---|---|\n| NIKA-000..099 | Core |\n| NIKA-100..199 | MCP |\n| NIKA-200..299 | Shield (security) |\n| NIKA-300..399 | Catalog |\n| NIKA-400..499 | Cortex (memory) |\n| NIKA-500..599 | Providers |\n| NIKA-600..699 | Runtime / DAG |\n| NIKA-700..799 | IO (fs, http, shell) |\n| NIKA-800..899 | Observability |\n| NIKA-900..999 | Reserved |\n"
              }
            ],
            [
              {
                "k": "text",
                "text": "Default trait methods: "
              },
              {
                "k": "code",
                "text": "is_transient() -> bool"
              },
              {
                "k": "text",
                "text": " (for retry logic), "
              },
              {
                "k": "code",
                "text": "fingerprint() -> u64"
              },
              {
                "k": "text",
                "text": " (for dedup/ratelimit)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Downcasting via "
              },
              {
                "k": "code",
                "text": "AsAny"
              },
              {
                "k": "text",
                "text": " supertrait avoids per-impl boilerplate."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Each crate owns its error enum without circular deps."
              }
            ],
            [
              {
                "k": "code",
                "text": "is_transient()"
              },
              {
                "k": "text",
                "text": " default enables retry logic across all errors without coupling the retry layer to specific error types."
              }
            ],
            [
              {
                "k": "text",
                "text": "Error code ranges prevent collision as subsystems are admitted over 11+ months."
              }
            ],
            [
              {
                "k": "text",
                "text": "Display parity enforced via golden tests per crate, resolving shadow zone #6."
              }
            ],
            [
              {
                "k": "text",
                "text": "JSON serialization round-trips: "
              },
              {
                "k": "code",
                "text": "{num, category, severity, slug}"
              },
              {
                "k": "text",
                "text": " → wire format → back."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Box<dyn>"
              },
              {
                "k": "text",
                "text": " allocation on the error path (vs zero-alloc enum with "
              },
              {
                "k": "code",
                "text": "thiserror"
              },
              {
                "k": "text",
                "text": "). Measured: ~50ns additional latency per error — acceptable; errors are exceptional by definition."
              }
            ],
            [
              {
                "k": "text",
                "text": "Every new crate author must implement "
              },
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": " — more ceremony than "
              },
              {
                "k": "code",
                "text": "anyhow::bail!"
              },
              {
                "k": "text",
                "text": ". Mitigated by "
              },
              {
                "k": "code",
                "text": "derive"
              },
              {
                "k": "text",
                "text": "-style helpers in "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Box<dyn>"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "AsAny"
              },
              {
                "k": "text",
                "text": " supertrait means error types must be "
              },
              {
                "k": "code",
                "text": "'static"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Send"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Sync"
              },
              {
                "k": "text",
                "text": " — matches existing conventions."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/traits.rs"
              },
              {
                "k": "text",
                "text": " lines 43–71 — "
              },
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": " trait definition with "
              },
              {
                "k": "code",
                "text": "is_transient"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "fingerprint"
              },
              {
                "k": "text",
                "text": " defaults"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/lib.rs"
              },
              {
                "k": "text",
                "text": " — full crate structure, 1,013 LOC, 44 tests, Gate 5 mutation 100%"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " lines 105–127 — reserved error code ranges table"
              }
            ],
            [
              {
                "k": "code",
                "text": "CHANGELOG.md"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " admission commit "
              },
              {
                "k": "code",
                "text": "42909b1c7"
              }
            ],
            [
              {
                "k": "text",
                "text": "Related crate: "
              },
              {
                "k": "strong",
                "text": "`nika-error`"
              },
              {
                "k": "text",
                "text": " (first admitted, L0)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — `anyhow::Error` at API boundaries",
          "id": "alt-a--anyhowerror-at-api-boundaries"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "See context. Rejected: opaque; loses wire-format IDs; breaks Shield integration."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Single `thiserror` enum in root",
          "id": "alt-b--single-thiserror-enum-in-root"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "See context. Rejected: coupling + build-time cascade on any variant addition."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D — `snafu` with context selectors",
          "id": "alt-d--snafu-with-context-selectors"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Evaluated in Rust Council. Rejected: snafu's context pattern is ergonomic for small error trees but less flexible for dual wire/structured representation. Trait-based pattern scales better past 100 error variants."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants ("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " on error enums)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-010 — miette as L4 presentation (the rendering contract that wraps this structural contract)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-015 — "
              },
              {
                "k": "code",
                "text": "expect-test"
              },
              {
                "k": "text",
                "text": " inline snapshots (regression coverage for rendered error output)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error"
              },
              {
                "k": "text",
                "text": " — implementation"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — error code range registry"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "If a subsystem exhausts its 100-number range (unlikely), extend into NIKA-900..999 reserved range with explicit registry update. Never overlap."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-006",
      "file": "adr-006-layered-kernel-isp-traits.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        },
        {
          "title": "Amendment 2026-04-16 — Kernel stays monolithic forever",
          "anchor": "amendment-2026-04-16--kernel-stays-monolithic-forever"
        }
      ],
      "words": 943,
      "sha256": "7df3b5bccada68ecdb5243370ee4a13958010c37b82896de026285c958018c19",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-006: Layered architecture + kernel ISP atomic traits + `trait_variant`",
          "id": "adr-006-layered-architecture--kernel-isp-atomic-traits--trait_variant"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel layer defines contracts for all side effects (filesystem, HTTP, shell, memory, embeddings, tool execution, WASM host, observability). Legacy approach was coarse-grained: one large "
            },
            {
              "k": "code",
              "text": "Provider"
            },
            {
              "k": "text",
              "text": " trait, one "
            },
            {
              "k": "code",
              "text": "Fs"
            },
            {
              "k": "text",
              "text": " trait. Consumers that only needed \"read a file\" had to depend on the entire surface. "
            },
            {
              "k": "code",
              "text": "async_trait"
            },
            {
              "k": "text",
              "text": " was universally used, which boxes every future on the async dispatch path — measurable overhead on the inference hot path."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rust 1.91 native AFIT (async fn in trait) + "
            },
            {
              "k": "code",
              "text": "trait_variant"
            },
            {
              "k": "text",
              "text": " crate (v0.1) unlocked a better pattern: write the async trait once, let "
            },
            {
              "k": "code",
              "text": "trait_variant::make"
            },
            {
              "k": "text",
              "text": " generate a companion "
            },
            {
              "k": "code",
              "text": "Dyn"
            },
            {
              "k": "text",
              "text": "-suffixed trait for object-safety without boxing on the static path."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Layered architecture"
            },
            {
              "k": "text",
              "text": " (strict dep direction):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Layer · Purpose · Admitted"
              }
            ],
            [
              {
                "k": "text",
                "text": "L0 · Primitives — error codes, catalog data, pure types · `nika-error`, `nika-catalog`"
              }
            ],
            [
              {
                "k": "text",
                "text": "L0.5 · Kernel — abstract side-effect traits + request/response types · `nika-kernel`, `nika-kernel-mock`"
              }
            ],
            [
              {
                "k": "text",
                "text": "L1 · Infrastructure — implementations of kernel traits (fs, http, providers, etc.) · pending Phase 2+"
              }
            ],
            [
              {
                "k": "text",
                "text": "L2 · Core runtime + DAG execution · pending Phase 2+"
              }
            ],
            [
              {
                "k": "text",
                "text": "L3 · Subsystems — binding, catalog overlay, policy · pending Phase 3+"
              }
            ],
            [
              {
                "k": "text",
                "text": "L4 · Applications — CLI, TUI, daemon, serve, LSP, verify tooling · `nika-catalog-verify` (binary)"
              }
            ],
            [
              {
                "k": "text",
                "text": "L5 · Full product integration · pending"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Layer-level ordering is enforced by "
            },
            {
              "k": "code",
              "text": "scripts/ci/check_layers.rs"
            },
            {
              "k": "text",
              "text": " pattern (see ADR-009): "
            },
            {
              "k": "code",
              "text": "layer(dep) ≥ layer(crate)"
            },
            {
              "k": "text",
              "text": " is verified by parsing "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " deps against a table."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Kernel trait design (L0.5)"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "~20 atomic ISP traits: "
              },
              {
                "k": "code",
                "text": "FsRead"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "FsWrite"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "FsMeta"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "FsList"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "HttpGet"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "HttpPost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ShellRun"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ShellCancel"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryRemember"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryRecall"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryForget"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ProviderInfer"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ProviderStream"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ProviderMeta"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ProviderEmbed"
              },
              {
                "k": "text",
                "text": " (optional), "
              },
              {
                "k": "code",
                "text": "ProviderVision"
              },
              {
                "k": "text",
                "text": " (optional), "
              },
              {
                "k": "code",
                "text": "ToolExecutor"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ObservabilitySink"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Clock"
              },
              {
                "k": "text",
                "text": " (sync by design)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Grouped into ~6 super-traits via blanket impls: "
              },
              {
                "k": "code",
                "text": "Fs"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "HttpClient"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ShellExecutor"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "All async traits use "
              },
              {
                "k": "code",
                "text": "#[trait_variant::make(Send)]"
              },
              {
                "k": "text",
                "text": " — native Rust 1.91 AFIT on static path, "
              },
              {
                "k": "code",
                "text": "Dyn"
              },
              {
                "k": "text",
                "text": "-suffixed companion trait for object safety."
              }
            ],
            [
              {
                "k": "code",
                "text": "BTreeMap"
              },
              {
                "k": "text",
                "text": " preferred over "
              },
              {
                "k": "code",
                "text": "HashMap"
              },
              {
                "k": "text",
                "text": " in kernel types for deterministic iteration (tests are reproducible)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Cancel tokens passed as function parameters, not embedded in request structs (keeps structs runtime-agnostic)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "A context loader that only reads files imports "
              },
              {
                "k": "code",
                "text": "FsRead"
              },
              {
                "k": "text",
                "text": " alone — compiler enforces minimum surface."
              }
            ],
            [
              {
                "k": "text",
                "text": "Zero boxing on the hot inference path (static dispatch via native AFIT)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Test hermeticity is structural: every I/O path is behind a trait, so "
              },
              {
                "k": "code",
                "text": "nika-kernel-mock"
              },
              {
                "k": "text",
                "text": " substitutes with zero network/disk. "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " 99 tests + "
              },
              {
                "k": "code",
                "text": "nika-kernel-mock"
              },
              {
                "k": "text",
                "text": " 88 tests exercise this fully."
              }
            ],
            [
              {
                "k": "text",
                "text": "Adding a new side effect = new atomic trait, no impact on existing ones."
              }
            ],
            [
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": " mandatory-vs-optional split ("
              },
              {
                "k": "code",
                "text": "ProviderInfer + ProviderStream + ProviderMeta"
              },
              {
                "k": "text",
                "text": " mandatory; "
              },
              {
                "k": "code",
                "text": "ProviderEmbed + ProviderVision"
              },
              {
                "k": "text",
                "text": " optional) lets lightweight providers (e.g., mock) implement only what they support."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "trait_variant"
              },
              {
                "k": "text",
                "text": " generates a pair of traits per async trait, doubling the documented API surface — ~40 traits visible to "
              },
              {
                "k": "code",
                "text": "cargo doc"
              },
              {
                "k": "text",
                "text": " from ~20 source definitions."
              }
            ],
            [
              {
                "k": "text",
                "text": "Caller ergonomics: implementing Provider requires 3 trait impls minimum ("
              },
              {
                "k": "code",
                "text": "Infer + Stream + Meta"
              },
              {
                "k": "text",
                "text": "). Mitigated by default methods where possible."
              }
            ],
            [
              {
                "k": "code",
                "text": "BTreeMap"
              },
              {
                "k": "text",
                "text": " perf in ultra-hot loops is slightly worse than "
              },
              {
                "k": "code",
                "text": "FxHashMap"
              },
              {
                "k": "text",
                "text": "; kernel isn't a hot loop, so acceptable."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Clock"
              },
              {
                "k": "text",
                "text": " sync-only is explicit YAGNI. Revisit if proven wrong."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/src/lib.rs"
              },
              {
                "k": "text",
                "text": " lines 19–34 — trait hierarchy docstring + re-export list"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs"
              },
              {
                "k": "text",
                "text": " lines 272–305 — "
              },
              {
                "k": "code",
                "text": "MemoryRemember"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryRecall"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryForget"
              },
              {
                "k": "text",
                "text": " + blanket "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "EmbeddingProvider"
              },
              {
                "k": "text",
                "text": " with "
              },
              {
                "k": "code",
                "text": "#[trait_variant::make(Send)]"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "InferResponse"
              },
              {
                "k": "text",
                "text": " with reserved "
              },
              {
                "k": "code",
                "text": "Option<MemoryDirective>"
              },
              {
                "k": "text",
                "text": " field (see ADR-007)"
              }
            ],
            [
              {
                "k": "code",
                "text": "CHANGELOG.md"
              },
              {
                "k": "text",
                "text": " lines 169–215 — "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " admission, key decisions listed, commit "
              },
              {
                "k": "code",
                "text": "ef8804371"
              }
            ],
            [
              {
                "k": "text",
                "text": "Related crates: "
              },
              {
                "k": "strong",
                "text": "`nika-kernel`"
              },
              {
                "k": "text",
                "text": " (L0.5 traits), "
              },
              {
                "k": "strong",
                "text": "`nika-kernel-mock`"
              },
              {
                "k": "text",
                "text": " (L0.5 test mock)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Coarse-grained traits (`Fs`, `Provider` mega-traits only)",
          "id": "alt-a--coarse-grained-traits-fs-provider-mega-traits-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected: re-creates the legacy problem where consumers over-depend."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — `async_trait` everywhere (boxed futures)",
          "id": "alt-b--async_trait-everywhere-boxed-futures"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected: measured ~100ns per call overhead; multiplied across a DAG with 100+ nodes becomes visible. Native AFIT is free."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — No super-traits, only atomic traits",
          "id": "alt-c--no-super-traits-only-atomic-traits"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected: 20 atomic trait bounds is unergonomic for consumers that genuinely need most of Fs/Provider. Blanket "
            },
            {
              "k": "code",
              "text": "Fs: FsRead + FsWrite + FsMeta + FsList"
            },
            {
              "k": "text",
              "text": " gives the best of both."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-003 — 12-gate admission (kernel admission went through all 12)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-004 — context-window sizing (kernel + kernel-mock together are 5,080 LOC)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants (kernel pre-plants "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ToolExecutor"
              },
              {
                "k": "text",
                "text": " traits for v0.95 Cortex and v0.100 agent-v2 without touching kernel later)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-012 — typestate runtime (consumes the kernel via "
              },
              {
                "k": "code",
                "text": "bind_kernel(&dyn Kernel)"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-013 — Loom concurrency verification (validates trait "
              },
              {
                "k": "em",
                "text": "impls"
              },
              {
                "k": "text",
                "text": " of the contracts this ADR defines)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-014 — sealed kernel traits (locks the trait surface this ADR introduced behind a "
              },
              {
                "k": "code",
                "text": "Sealed"
              },
              {
                "k": "text",
                "text": " super-trait)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rust 1.91 "
            },
            {
              "k": "code",
              "text": "trait_variant"
            },
            {
              "k": "text",
              "text": " is still v0.1.x. Keep version-pinned. Re-evaluate when the crate reaches v1.0 or if stdlib absorbs the pattern."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Amendment 2026-04-16 — Kernel stays monolithic forever",
          "id": "amendment-2026-04-16--kernel-stays-monolithic-forever"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The original ADR-006 reserved a future split into 4 sibling crates\n("
            },
            {
              "k": "code",
              "text": "nika-kernel-{core,ai,runtime,plugin}"
            },
            {
              "k": "text",
              "text": ") when LOC > 10k OR pub trait count"
            }
          ]
        },
        {
          "k": "quote",
          "inline": []
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " stays as ONE crate forever, with 5 internal group modules\n("
            },
            {
              "k": "code",
              "text": "io/"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ai/"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "runtime/"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "plugin/"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "infra/"
            },
            {
              "k": "text",
              "text": ") that already provide the\nlogical separation. Real Rust workspaces (rust-analyzer, helix, oxc, biome,\nruff, uv, bevy, tokio) keep their kernel-equivalents monolithic; only\npolkadot-sdk splits and that's blockchain-feature-gated. Splitting would force\n"
            },
            {
              "k": "code",
              "text": "use nika_kernel_ai::Provider; use nika_kernel_core::Clock;"
            },
            {
              "k": "text",
              "text": " everywhere — worse\nDX. The Q7 prelude hub (added 2026-04-16) depends on kernel being whole."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Trigger to revisit (both must hold):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "kernel exceeds 15k LOC, AND"
              }
            ],
            [
              {
                "k": "text",
                "text": "2+ active sub-domains being modified independently in same session for 3+\nconsecutive sessions"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Reserved crate names (nika-kernel-core/ai/runtime/plugin) freed."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "See "
            },
            {
              "k": "code",
              "text": "~/.claude/.../memory/feedback_kernel_monolithic_forever.md"
            },
            {
              "k": "text",
              "text": " for the full\nrationale + agent evidence."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Status of original split-trigger: "
            },
            {
              "k": "strong",
              "text": "superseded by this amendment."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-007",
      "file": "adr-007-forward-compat-invariants.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 797,
      "sha256": "52aef16a21bba2be72991ea42111ab1e7998185d23afc55ad0bb9dc77c701572",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-007: Forward-compat invariants (`#[non_exhaustive]` + `::new()` + pre-planted kernel hooks)",
          "id": "adr-007-forward-compat-invariants-non_exhaustive--new--pre-planted-kernel-hooks"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika ships on real semver toward a 1.0 launch (ADR-002 · amended D-2026-06-20-N1) with features shipping incrementally over years. Adding a field to a public struct after 1.0 would be a breaking change for any downstream crate using struct literal syntax — and even pre-1.0, churning load-bearing DTOs every minor is costly. "
            },
            {
              "k": "code",
              "text": "InferRequest"
            },
            {
              "k": "text",
              "text": " flows through every provider, verb, and memory call — if Connectome memory fields aren't reserved now, every consumer must be touched when the Connectome lands."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The 16-agent Rust Council (2026-04-13) quantified the ROI: "
            },
            {
              "k": "strong",
              "text": "1.5 days of pre-planting now saves 10 days of migration later (ROI 6.7×)"
            },
            {
              "k": "text",
              "text": ". The decision was unanimous."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Four interlocking mechanisms"
            },
            {
              "k": "text",
              "text": " make Nika's public API forward-compatible by construction:"
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "1. `#[non_exhaustive]` + `pub fn new()` (Invariant #19)",
          "id": "1-non_exhaustive--pub-fn-new-invariant-19"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Every public struct, enum, and error variant carries "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": ". External code cannot use struct literal syntax ("
            },
            {
              "k": "code",
              "text": "Foo { x: 1 }"
            },
            {
              "k": "text",
              "text": ") — only the constructor ("
            },
            {
              "k": "code",
              "text": "Foo::new(1)"
            },
            {
              "k": "text",
              "text": "). Adding a field post-v0.90 is then non-breaking: existing callers keep calling "
            },
            {
              "k": "code",
              "text": "new()"
            },
            {
              "k": "text",
              "text": " with the old signature; new fields get default values; the struct literally grows without rippling."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Invariant #19 mandates "
            },
            {
              "k": "code",
              "text": "pub fn new()"
            },
            {
              "k": "text",
              "text": " on "
            },
            {
              "k": "strong",
              "text": "every"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " public struct. 15 constructors confirmed across the workspace as of Session 2a. Verified by "
            },
            {
              "k": "code",
              "text": "scripts/ci/check-tests.sh"
            },
            {
              "k": "text",
              "text": " + explicit snapshot tests in "
            },
            {
              "k": "code",
              "text": "nika-catalog"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "2. Reserved `Option<T>` fields on `InferRequest` / `InferResponse`",
          "id": "2-reserved-optiont-fields-on-inferrequest--inferresponse"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Future-phase fields land "
            },
            {
              "k": "strong",
              "text": "today"
            },
            {
              "k": "text",
              "text": " as "
            },
            {
              "k": "code",
              "text": "None"
            },
            {
              "k": "text",
              "text": "-initialized options:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "memory: Option<MemoryDirective>"
              },
              {
                "k": "text",
                "text": " — Cortex, v0.95"
              }
            ],
            [
              {
                "k": "code",
                "text": "budget: Option<BudgetDirective>"
              },
              {
                "k": "text",
                "text": " — v0.100"
              }
            ],
            [
              {
                "k": "code",
                "text": "replay_seed: Option<u64>"
              },
              {
                "k": "text",
                "text": " — v0.100"
              }
            ],
            [
              {
                "k": "code",
                "text": "memory_frames: Vec<MemoryFrameRef>"
              },
              {
                "k": "text",
                "text": " — Cortex, v0.95"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When v0.95 lands, the provider impls start "
            },
            {
              "k": "em",
              "text": "populating"
            },
            {
              "k": "text",
              "text": " these fields. No struct shape changes. No downstream migrations."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "3. Kernel hooks pre-planted at Phase 0",
          "id": "3-kernel-hooks-pre-planted-at-phase-0"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " (L0.5) defines these traits "
            },
            {
              "k": "strong",
              "text": "now"
            },
            {
              "k": "text",
              "text": ", even though no implementation ships until v0.95/v0.100:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EmbeddingProvider"
              },
              {
                "k": "text",
                "text": " — Cortex (v0.95)"
              }
            ],
            [
              {
                "k": "code",
                "text": "ToolExecutor"
              },
              {
                "k": "text",
                "text": " — agent-v2 parallel tool calling (v0.100)"
              }
            ],
            [
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": " — WASM plugins (v0.100)"
              }
            ],
            [
              {
                "k": "code",
                "text": "ObservabilitySink"
              },
              {
                "k": "text",
                "text": " — tracing + metrics (v0.100)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "At v0.95, a new crate "
            },
            {
              "k": "code",
              "text": "nika-memory-oxigraph"
            },
            {
              "k": "text",
              "text": " implements "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": ". Zero modification to the already-admitted kernel."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "4. `EventKind` reserved variants + `Extension` escape hatch",
          "id": "4-eventkind-reserved-variants--extension-escape-hatch"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "EventKind"
            },
            {
              "k": "text",
              "text": " is "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " and carries placeholder variants for every planned subsystem: "
            },
            {
              "k": "code",
              "text": "MemoryHit"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "AgentPlanned"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Trace"
            },
            {
              "k": "text",
              "text": ". Plus an "
            },
            {
              "k": "code",
              "text": "Extension { ns: &'static str, name: &'static str, payload: serde_json::Value }"
            },
            {
              "k": "text",
              "text": " variant for community extensions that bypass core without conflicting."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "v0.95 Cortex ships by adding "
              },
              {
                "k": "code",
                "text": "nika-memory-oxigraph"
              },
              {
                "k": "text",
                "text": " implementing kernel traits — zero modification to v0.90 code."
              }
            ],
            [
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": " field additions are not breaking changes within v0.x."
              }
            ],
            [
              {
                "k": "code",
                "text": "Extension"
              },
              {
                "k": "text",
                "text": " variant gives ecosystem extensions a forward-safe escape hatch."
              }
            ],
            [
              {
                "k": "text",
                "text": "Downstream users are protected from struct-shape churn during 11+ month Diamond build."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Every public type carries invariant overhead ("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "new()"
              },
              {
                "k": "text",
                "text": " constructor). For internal crates this is ceremony — mitigated by workspace lint "
              },
              {
                "k": "code",
                "text": "unreachable_pub = warn"
              },
              {
                "k": "text",
                "text": " which catches over-visibility."
              }
            ],
            [
              {
                "k": "text",
                "text": "Reserved "
              },
              {
                "k": "code",
                "text": "Option"
              },
              {
                "k": "text",
                "text": " fields in "
              },
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": " serialize as "
              },
              {
                "k": "code",
                "text": "None"
              },
              {
                "k": "text",
                "text": " until v0.95; slight JSON bloat (~5 extra null fields per request). Acceptable."
              }
            ],
            [
              {
                "k": "text",
                "text": "Future-phase fields require discipline to never accidentally use in pre-phase code. Compiler does not enforce \"use only when phase ≥ X.\""
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Extension"
              },
              {
                "k": "text",
                "text": " variant payload is "
              },
              {
                "k": "code",
                "text": "serde_json::Value"
              },
              {
                "k": "text",
                "text": " (unstructured). Intentional — extensions define their own schema; core stays neutral."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs"
              },
              {
                "k": "text",
                "text": " lines 196–239 — "
              },
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": " with "
              },
              {
                "k": "code",
                "text": "memory: Option<MemoryDirective>"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "new()"
              },
              {
                "k": "text",
                "text": " constructor, full field list"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs"
              },
              {
                "k": "text",
                "text": " lines 291–312 — "
              },
              {
                "k": "code",
                "text": "InferResponse"
              },
              {
                "k": "text",
                "text": " with "
              },
              {
                "k": "code",
                "text": "memory_frames: Vec<MemoryFrameRef>"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs"
              },
              {
                "k": "text",
                "text": " lines 272–305 — "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "EmbeddingProvider"
              },
              {
                "k": "text",
                "text": " trait declarations (Cortex hooks)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-runtime/src/tool_executor.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "ToolExecutor"
              },
              {
                "k": "text",
                "text": " trait (agent-v2 hook)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " lines 1–100 — the 8 patterns, 5 locked decisions, 10 API design rules, 10 anti-patterns"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "POST_AUDIT_REVISIONS.md"
              },
              {
                "k": "text",
                "text": " DÉCISION 3 — kernel hooks Phase 0 mandatory"
              }
            ],
            [
              {
                "k": "text",
                "text": "Invariant #19 FULL status in "
              },
              {
                "k": "code",
                "text": "memory: STATE.md"
              },
              {
                "k": "text",
                "text": " (15 constructors, all tested)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Related crates: "
              },
              {
                "k": "strong",
                "text": "`nika-error`"
              },
              {
                "k": "text",
                "text": " (error variants #[non_exhaustive]), "
              },
              {
                "k": "strong",
                "text": "`nika-kernel`"
              },
              {
                "k": "text",
                "text": " (pre-planted hooks), "
              },
              {
                "k": "strong",
                "text": "`nika-catalog`"
              },
              {
                "k": "text",
                "text": " (16 new constructors Session 2a)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Add fields as they're needed, breaking-change bump",
          "id": "alt-a--add-fields-as-theyre-needed-breaking-change-bump"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected per ROI math (6.7× cost at v0.95)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Use builder pattern everywhere (typestate)",
          "id": "alt-b--use-builder-pattern-everywhere-typestate"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Considered. Rejected: overkill for request/response types that have reasonable defaults. Reserve for kernel-level ceremony like "
            },
            {
              "k": "code",
              "text": "WorkflowBuilder"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Versioned struct types (`InferRequestV1` / `InferRequestV2`)",
          "id": "alt-c--versioned-struct-types-inferrequestv1--inferrequestv2"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected: API churn at version boundaries is exactly what the forward-compat discipline (ADR-002 · amended D-2026-06-20-N1 · real semver toward 1.0) is trying to avoid."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-002 — real semver toward 1.0 (amended D-2026-06-20-N1 · the release model that demands forward-compat)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-005 — error hierarchy (error enums also "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP traits (where the hooks land)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-015 — "
              },
              {
                "k": "code",
                "text": "expect-test"
              },
              {
                "k": "text",
                "text": " inline snapshots (regression coverage when "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " types grow new variants and rendered output drifts)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — full reference"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Invariant #19 is a gate in the admission protocol (ADR-003, Gate 4 — zero clippy warnings — includes "
            },
            {
              "k": "code",
              "text": "non_exhaustive_omitted_patterns = \"deny\""
            },
            {
              "k": "text",
              "text": "). Additions slip is mechanically impossible."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-008",
      "file": "adr-008-toml-driven-catalog.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        },
        {
          "title": "Implementation notes — Session 2b / Session 3",
          "anchor": "implementation-notes--session-2b--session-3"
        }
      ],
      "words": 1105,
      "sha256": "7e90352e80b5557da0a141cc8d2292dcf91b5eae34088a5a2ddb7b3c87aaaaa5",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-008: TOML-driven catalog with build-time codegen + perfect-hash lookup",
          "id": "adr-008-toml-driven-catalog-with-build-time-codegen--perfect-hash-lookup"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika needs to resolve a user-supplied "
            },
            {
              "k": "code",
              "text": "provider: claude"
            },
            {
              "k": "text",
              "text": " → canonical provider id → model → capabilities → pricing in a single YAML field. Legacy used hardcoded Rust arrays. That had two problems:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Contribution friction:"
              },
              {
                "k": "text",
                "text": " adding a provider required writing Rust + rebuilding. Community contributors were blocked."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Silent rot:"
              },
              {
                "k": "text",
                "text": " the Phase A audit (2026-03) discovered "
              },
              {
                "k": "strong",
                "text": "29 broken MCP aliases"
              },
              {
                "k": "text",
                "text": " — entries pointing at servers that no longer existed. No one noticed for months because the data lived inside compiled code."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Session 2a (2026-04-14) set the target: move all catalog data to TOML with schema validation at build time, and add a model-capabilities rule table that replaces a 900-line "
            },
            {
              "k": "code",
              "text": "match"
            },
            {
              "k": "text",
              "text": " cascade."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "All catalog data lives in `data/*.toml`"
            },
            {
              "k": "text",
              "text": " with versioned schemas:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika/llm-providers@1.0"
              },
              {
                "k": "text",
                "text": " — 21 providers, each with "
              },
              {
                "k": "code",
                "text": "canonical_id"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "aliases"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "api_dialect"
              },
              {
                "k": "text",
                "text": " (closed enum: anthropic | openai-chat | openai-responses | gemini | cohere | ai21 | bedrock | voyage | mock)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/mcp-servers@1.0"
              },
              {
                "k": "text",
                "text": " — MCP server aliases + trust labels"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/model-capabilities@1.0"
              },
              {
                "k": "text",
                "text": " — 9 first-match-wins rules (Session 2a), projected to 28 (Session 2b)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/builtins@1.0"
              },
              {
                "k": "text",
                "text": " — builtin tool definitions + safety tags"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "`build.rs` does the heavy lifting:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "Parse each TOML file against its schema version"
              }
            ],
            [
              {
                "k": "text",
                "text": "Enforce invariants: every "
              },
              {
                "k": "code",
                "text": "scope.providers[i]"
              },
              {
                "k": "text",
                "text": " in "
              },
              {
                "k": "code",
                "text": "model-capabilities.toml"
              },
              {
                "k": "text",
                "text": " must be a canonical id declared in "
              },
              {
                "k": "code",
                "text": "llm-providers.toml"
              },
              {
                "k": "text",
                "text": " (foreign-key check). "
              },
              {
                "k": "code",
                "text": "api_dialect"
              },
              {
                "k": "text",
                "text": " in rule scopes must be in the closed dialect set."
              }
            ],
            [
              {
                "k": "text",
                "text": "Emit static Rust arrays + "
              },
              {
                "k": "code",
                "text": "phf::Map"
              },
              {
                "k": "text",
                "text": " indexes into "
              },
              {
                "k": "code",
                "text": "$OUT_DIR"
              },
              {
                "k": "text",
                "text": " for zero-runtime-cost lookup."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Lookup strategy is hybrid"
            },
            {
              "k": "text",
              "text": " (see "
            },
            {
              "k": "code",
              "text": "docs/crate-specs/nika-catalog.md"
            },
            {
              "k": "text",
              "text": "):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "phf::Map"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "unicase"
              },
              {
                "k": "text",
                "text": " for case-insensitive entities (providers, MCP aliases) — O(1)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Sorted arrays + "
              },
              {
                "k": "code",
                "text": "binary_search"
              },
              {
                "k": "text",
                "text": " for case-sensitive entities (builtins, transforms) — O(log n)"
              }
            ],
            [
              {
                "k": "text",
                "text": "First-match-wins rule table for model capabilities — O(n) but n = 9–28"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-catalog-verify"
            },
            {
              "k": "text",
              "text": " (L4 binary, also admitted) provides an "
            },
            {
              "k": "strong",
              "text": "online verification layer"
            },
            {
              "k": "text",
              "text": ": fetches provider API endpoints nightly, confirms MCP aliases still respond, flags drift in CI."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Session 2a concrete additions"
            },
            {
              "k": "text",
              "text": " (shipped, grep-verified):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "data/model-capabilities.toml"
              },
              {
                "k": "text",
                "text": " — 9 rules covering OpenAI o-series, GPT-5, Claude family, Anthropic catch-all, DeepSeek reasoner, DeepSeek any, xAI Grok-4"
              }
            ],
            [
              {
                "k": "code",
                "text": "build/capabilities.rs"
              },
              {
                "k": "text",
                "text": " — 380 LOC codegen, extracted from "
              },
              {
                "k": "code",
                "text": "build.rs"
              },
              {
                "k": "text",
                "text": " to respect the 1,500-LOC file cap"
              }
            ],
            [
              {
                "k": "code",
                "text": "src/types/capabilities.rs"
              },
              {
                "k": "text",
                "text": " — 276 LOC ("
              },
              {
                "k": "code",
                "text": "CapPatch"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "Matcher"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "Rule"
              },
              {
                "k": "text",
                "text": "), "
              },
              {
                "k": "code",
                "text": "const fn merge_with"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "fn materialize"
              }
            ],
            [
              {
                "k": "code",
                "text": "api_dialect"
              },
              {
                "k": "text",
                "text": " field added to all 21 providers in "
              },
              {
                "k": "code",
                "text": "llm-providers.toml"
              }
            ],
            [
              {
                "k": "text",
                "text": "16 new constructors (invariant #19 complete — see ADR-007)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Proptest parity harness — 10,000 random (provider, model) pairs compared against frozen legacy"
              }
            ],
            [
              {
                "k": "text",
                "text": "Insta snapshot — 31 golden (provider, model) pairs reviewable"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Community contributors edit TOML, not Rust. Learning curve drops to \"know YAML/TOML.\""
              }
            ],
            [
              {
                "k": "text",
                "text": "Build-time FK checks catch typos before they ship."
              }
            ],
            [
              {
                "k": "text",
                "text": "Zero runtime overhead — all data in static memory."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog-verify"
              },
              {
                "k": "text",
                "text": " catches silent rot (29-alias class) nightly."
              }
            ],
            [
              {
                "k": "text",
                "text": "Schema versions ("
              },
              {
                "k": "code",
                "text": "nika/model-capabilities@1.0"
              },
              {
                "k": "text",
                "text": ") make future migrations explicit."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Build times grow with data (current: catalog build ~8s cold). Acceptable; amortized over cargo caches."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Rule-authoring sharp edge:"
              },
              {
                "k": "text",
                "text": " first-match-wins means a single rule must carry "
              },
              {
                "k": "strong",
                "text": "all"
              },
              {
                "k": "text",
                "text": " capabilities for a matched (provider, model) pair. Documented extensively in "
              },
              {
                "k": "code",
                "text": "model-capabilities.toml"
              },
              {
                "k": "text",
                "text": " header (lines 32–48). Getting this wrong silently drops capabilities."
              }
            ],
            [
              {
                "k": "text",
                "text": "Adding a new capability field requires running the 10k-pair proptest parity harness (~30s)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The TOML-vs-YAML choice: TOML for code-authored config (typing explicit, no indentation fragility, comments allowed). YAML stays for Nika workflow files (user-authored DSL)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-catalog/data/model-capabilities.toml"
              },
              {
                "k": "text",
                "text": " lines 1–80 — schema, FK invariants, resolution algorithm, first-match-wins authoring note"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-catalog/data/llm-providers.toml"
              },
              {
                "k": "text",
                "text": " lines 1–50 — "
              },
              {
                "k": "code",
                "text": "api_dialect"
              },
              {
                "k": "text",
                "text": " on every provider"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-catalog/build.rs"
              },
              {
                "k": "text",
                "text": " lines 1–50 — build script overview"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-catalog-codegen/src/capabilities.rs"
              },
              {
                "k": "text",
                "text": " — capabilities codegen (extracted per 1,500 LOC cap ADR-004 · build/capabilities.rs twin NUKEd at the D-2026-06-10-N4 codegen WIRE)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-catalog/src/types/capabilities.rs"
              },
              {
                "k": "text",
                "text": " — 276 LOC: "
              },
              {
                "k": "code",
                "text": "CapPatch"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Matcher"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Rule"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/crate-specs/nika-catalog.md"
              },
              {
                "k": "text",
                "text": " lines 1–60 — hybrid lookup rationale"
              }
            ],
            [
              {
                "k": "code",
                "text": "CHANGELOG.md"
              },
              {
                "k": "text",
                "text": " — Session 2a entry \"TOML-driven model capabilities\" (Unreleased section)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Related crates: "
              },
              {
                "k": "strong",
                "text": "`nika-catalog`"
              },
              {
                "k": "text",
                "text": " (L0, 4,690 LOC, 154 tests), "
              },
              {
                "k": "strong",
                "text": "`nika-catalog-verify`"
              },
              {
                "k": "text",
                "text": " (L4 binary, online check)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Hardcoded Rust arrays (legacy approach)",
          "id": "alt-a--hardcoded-rust-arrays-legacy-approach"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. See context (contribution friction + 29-alias rot)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Runtime TOML loading (no codegen)",
          "id": "alt-b--runtime-toml-loading-no-codegen"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Adds startup cost, loses compile-time FK checks, introduces a \"which TOML did we load?\" bug class."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Database (sqlite) instead of TOML",
          "id": "alt-c--database-sqlite-instead-of-toml"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Git-based review workflow (PR diffs, blame, history) is more valuable than query flexibility for a 21-provider catalog. sqlite reconsidered if catalog exceeds ~500 entries."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D — JSON instead of TOML",
          "id": "alt-d--json-instead-of-toml"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. No comments, more fragile indentation, harder to review in diff."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-004 — context-window sizing (catalog is 4,690 LOC; "
              },
              {
                "k": "code",
                "text": "build/capabilities.rs"
              },
              {
                "k": "text",
                "text": " extracted to respect 1,500 LOC file cap)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants (schema versions + "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " in generated types)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/crate-specs/nika-catalog.md"
              },
              {
                "k": "text",
                "text": " — full hybrid-lookup spec"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Session 2b (next) expands the rule table to 28 entries + 5 new ModelCapabilities fields (Modality, TokenizerFamily, ParamFlag, supports_system_messages, supports_reasoning). Session 3 adds pricing schema + 8 providers without rules + 4 new providers (qwen, minimax, moonshot, zhipu). Schema version stays "
            },
            {
              "k": "code",
              "text": "@1.0"
            },
            {
              "k": "text",
              "text": " — invariants #19 and #11 of "
            },
            {
              "k": "code",
              "text": "forward-compat-invariants.md"
            },
            {
              "k": "text",
              "text": " allow extension without version bump."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Implementation notes — Session 2b / Session 3",
          "id": "implementation-notes--session-2b--session-3"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The P0-1 \"two sources of truth\" hazard (Rust "
            },
            {
              "k": "code",
              "text": "Default"
            },
            {
              "k": "text",
              "text": " impl + TOML "
            },
            {
              "k": "code",
              "text": "[defaults]"
            },
            {
              "k": "text",
              "text": " diverging silently) is now closed by three coordinated measures:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "CapPatch::materialize(&CAPABILITY_DEFAULTS)"
              },
              {
                "k": "text",
                "text": " draws every unset field from the TOML "
              },
              {
                "k": "code",
                "text": "[defaults]"
              },
              {
                "k": "text",
                "text": " block at resolve time. The Rust "
              },
              {
                "k": "code",
                "text": "ModelCapabilities::default()"
              },
              {
                "k": "text",
                "text": " impl is kept only as an internal-crate shortcut for test scaffolding and struct-literal ergonomics; it is never consulted by the resolver."
              }
            ],
            [
              {
                "k": "text",
                "text": "The "
              },
              {
                "k": "code",
                "text": "materialize_defaults_agree_with_rust_default_impl"
              },
              {
                "k": "text",
                "text": " unit test (in "
              },
              {
                "k": "code",
                "text": "src/types/capabilities.rs"
              },
              {
                "k": "text",
                "text": ") asserts that the TOML-shaped defaults materialise into the same "
              },
              {
                "k": "code",
                "text": "ModelCapabilities"
              },
              {
                "k": "text",
                "text": " as the Rust "
              },
              {
                "k": "code",
                "text": "Default"
              },
              {
                "k": "text",
                "text": " impl — any edit to one without the other fails the test."
              }
            ],
            [
              {
                "k": "text",
                "text": "Session 3 Phase G tightens "
              },
              {
                "k": "code",
                "text": "validate_caps_patch(&file.defaults, \"[defaults]\", require_all=true)"
              },
              {
                "k": "text",
                "text": " so the "
              },
              {
                "k": "code",
                "text": "[defaults]"
              },
              {
                "k": "text",
                "text": " block must explicitly set every field that has an "
              },
              {
                "k": "code",
                "text": "unwrap_or"
              },
              {
                "k": "text",
                "text": " fallback in "
              },
              {
                "k": "code",
                "text": "materialize"
              },
              {
                "k": "text",
                "text": " (8 of 9 slots; "
              },
              {
                "k": "code",
                "text": "tokenizer"
              },
              {
                "k": "text",
                "text": " stays legitimately optional by design). The hard-coded fallbacks in "
              },
              {
                "k": "code",
                "text": "materialize"
              },
              {
                "k": "text",
                "text": " become structurally unreachable for any TOML that passes build-time validation; "
              },
              {
                "k": "code",
                "text": "debug_assert!"
              },
              {
                "k": "text",
                "text": " lines in "
              },
              {
                "k": "code",
                "text": "materialize"
              },
              {
                "k": "text",
                "text": " catch runtime regressions in debug mode at zero release cost."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Session 3 also retires the coarse "
            },
            {
              "k": "code",
              "text": "ModelCapabilities::supports_vision: bool"
            },
            {
              "k": "text",
              "text": " — consumers migrate to "
            },
            {
              "k": "code",
              "text": "input_modalities.contains(&Modality::Image)"
            },
            {
              "k": "text",
              "text": ", which was the Session 2b replacement. The pricing catalog is extended with three "
            },
            {
              "k": "code",
              "text": "Option<f64>"
            },
            {
              "k": "text",
              "text": " axes (cached input, image, reasoning tokens); the full TOML-driven pricing migration (new "
            },
            {
              "k": "code",
              "text": "data/model-pricing.toml"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "build/pricing.rs"
            },
            {
              "k": "text",
              "text": ") is scheduled as Phase E2 of Session 4. The \"two sources of truth\" hazard is now "
            },
            {
              "k": "strong",
              "text": "scoped"
            },
            {
              "k": "text",
              "text": ": defaults is closed (commit 731f11bfe — "
            },
            {
              "k": "code",
              "text": "require_all"
            },
            {
              "k": "text",
              "text": " on "
            },
            {
              "k": "code",
              "text": "[defaults]"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "debug_assert!"
            },
            {
              "k": "text",
              "text": " in "
            },
            {
              "k": "code",
              "text": "materialize"
            },
            {
              "k": "text",
              "text": "); pricing closure lands with Phase E2. The Rust "
            },
            {
              "k": "code",
              "text": "ALL_PRICING"
            },
            {
              "k": "text",
              "text": " slice remains the only source until the TOML migration, so the hazard has no bite today."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-009",
      "file": "adr-009-adr-process.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 815,
      "sha256": "783183810ed04550ba806d8d333711e4343305fe95990cb7dad0651552276d74",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-009: ADR process + hook discipline (meta)",
          "id": "adr-009-adr-process--hook-discipline-meta"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Decisions rot. Architectural reasoning captured in Slack, commit messages, or agent memory degrades faster than the code it explains. Six months from a decision, the question "
            },
            {
              "k": "em",
              "text": "\"why did we pick `trait_variant` over `async_trait`?\""
            },
            {
              "k": "text",
              "text": " has to be re-derived from diffs — or, more often, never answered."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Legacy Nika (v0.1–v0.27) had 8 ADRs, all "
            },
            {
              "k": "code",
              "text": "Accepted"
            },
            {
              "k": "text",
              "text": " status, no superseding chain, invisible to open-source contributors (they lived in the private monorepo at "
            },
            {
              "k": "code",
              "text": "dx/adr/"
            },
            {
              "k": "text",
              "text": "). When Diamond big-banged, those 8 ADRs were archived wholesale — not because they were wrong, but because the "
            },
            {
              "k": "strong",
              "text": "link between decision and code had rotted"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Diamond is a multi-year effort. Without a mechanical discipline for writing and maintaining ADRs, the same rot is inevitable."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Every architecturally-significant decision (ASD) lands with an ADR in the same commit."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Location",
          "id": "location"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADRs live in "
            },
            {
              "k": "code",
              "text": "nika/engine/docs/adr/"
            },
            {
              "k": "text",
              "text": " (this repo, public). Diamond ADRs "
            },
            {
              "k": "strong",
              "text": "must"
            },
            {
              "k": "text",
              "text": " be public — the rationale is educational for open-source contributors and prevents the \"invisible decisions\" problem that killed the legacy ADRs."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Numbering",
          "id": "numbering"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Sequential: "
            },
            {
              "k": "code",
              "text": "adr-001"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "adr-002"
            },
            {
              "k": "text",
              "text": ", … Never reused, never reordered. Superseded ADRs keep their number and add "
            },
            {
              "k": "code",
              "text": "Superseded by ADR-NNN"
            },
            {
              "k": "text",
              "text": " to their status."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Template",
          "id": "template"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "docs/adr/TEMPLATE.md"
            },
            {
              "k": "text",
              "text": " is the mandatory shape:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Frontmatter: "
              },
              {
                "k": "code",
                "text": "Status"
              },
              {
                "k": "text",
                "text": " (Proposed / Accepted / Superseded) + "
              },
              {
                "k": "code",
                "text": "Date"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "Phase"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "Deciders"
              }
            ],
            [
              {
                "k": "text",
                "text": "Sections: Context → Decision → Consequences (Positive / Negative / Neutral) → Evidence → Alternatives → Related → Notes"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "When to write one",
          "id": "when-to-write-one"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "An ASD matches ≥2 of these triggers:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crosses a crate or layer boundary (affects >1 crate, or defines a layer contract)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Introduces a new invariant downstream code must respect"
              }
            ],
            [
              {
                "k": "text",
                "text": "Locks a public API (trait, struct, enum that other crates depend on)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Makes a non-reversible choice (can't be undone without breaking users)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Trades a quality attribute (perf vs correctness, simplicity vs flexibility)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Replaces or supersedes a prior ADR"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Do not write an ADR for:"
            },
            {
              "k": "text",
              "text": " single-file refactors, bug fixes, dependency bumps, renames without behavior change, style decisions."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Enforcement (the \"hook\")",
          "id": "enforcement-the-hook"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "scripts/ci/check-adr-coverage.sh"
            },
            {
              "k": "text",
              "text": " — auxiliary gate script, warn-only initially:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "Parses workspace "
              },
              {
                "k": "code",
                "text": "members"
              },
              {
                "k": "text",
                "text": " from "
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              }
            ],
            [
              {
                "k": "text",
                "text": "For each admitted crate, greps "
              },
              {
                "k": "code",
                "text": "docs/adr/*.md"
              },
              {
                "k": "text",
                "text": " for "
              },
              {
                "k": "code",
                "text": "\\bcrate_name\\b"
              }
            ],
            [
              {
                "k": "text",
                "text": "Reports coverage; exits 0 (warn) unless "
              },
              {
                "k": "code",
                "text": "FAIL_ON_MISS=1"
              },
              {
                "k": "text",
                "text": " is set (CI strict mode)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Integration points:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crate-admit"
              },
              {
                "k": "text",
                "text": " skill invokes this script at Gate 12 (atomic commit) as a soft check"
              }
            ],
            [
              {
                "k": "text",
                "text": "CI "
              },
              {
                "k": "code",
                "text": ".github/workflows/diamond-ci.yml"
              },
              {
                "k": "text",
                "text": " runs "
              },
              {
                "k": "code",
                "text": "FAIL_ON_MISS=1"
              },
              {
                "k": "text",
                "text": " on the "
              },
              {
                "k": "code",
                "text": "main"
              },
              {
                "k": "text",
                "text": " branch"
              }
            ],
            [
              {
                "k": "text",
                "text": "PR template ("
              },
              {
                "k": "code",
                "text": ".github/pull_request_template.md"
              },
              {
                "k": "text",
                "text": ") asks: "
              },
              {
                "k": "em",
                "text": "\"Does this change introduce an ASD? If yes, the ADR number: ADR-NNN. If no, explain why.\""
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Supersession",
          "id": "supersession"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When a decision is replaced:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "Set old ADR's status to "
              },
              {
                "k": "code",
                "text": "Superseded by ADR-NNN"
              }
            ],
            [
              {
                "k": "text",
                "text": "Write the new ADR referencing "
              },
              {
                "k": "code",
                "text": "Supersedes: ADR-M"
              },
              {
                "k": "text",
                "text": " in its Related section"
              }
            ],
            [
              {
                "k": "text",
                "text": "Commit both changes together (one atomic commit)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADRs are "
            },
            {
              "k": "strong",
              "text": "never deleted"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Clarity for future-self and contributors: every hard decision is traceable to a durable document."
              }
            ],
            [
              {
                "k": "text",
                "text": "Onboarding accelerant: new contributor reads "
              },
              {
                "k": "code",
                "text": "docs/adr/"
              },
              {
                "k": "text",
                "text": " to understand the reasoning, not just the shape."
              }
            ],
            [
              {
                "k": "text",
                "text": "Supersession chain lets anyone answer "
              },
              {
                "k": "em",
                "text": "\"what was the old approach, why did it change?\""
              },
              {
                "k": "text",
                "text": " in one git-log."
              }
            ],
            [
              {
                "k": "text",
                "text": "Public ADRs signal project seriousness — mature OSS projects (Rust, Kubernetes, Helix) all ship them."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Writing an ADR is friction. A rushed session may skip it."
              }
            ],
            [
              {
                "k": "text",
                "text": "Coverage-check is pattern-based (grep crate name); misses ADRs that discuss a crate without naming it."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR count grows; at >50 ADRs, navigation becomes harder without tooling. Revisit with "
              },
              {
                "k": "code",
                "text": "docs/adr/INDEX.md"
              },
              {
                "k": "text",
                "text": " auto-generation then."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The meta-ADR (this one) is self-referential: it mandates itself. That is acceptable — bootstrap is always a free variable."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "docs/adr/README.md"
              },
              {
                "k": "text",
                "text": " — full process doc + template usage"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/adr/TEMPLATE.md"
              },
              {
                "k": "text",
                "text": " — mandatory template"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/check-adr-coverage.sh"
              },
              {
                "k": "text",
                "text": " — the coverage hook"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/adr/adr-001-diamond-orphan-branch.md"
              },
              {
                "k": "text",
                "text": " through "
              },
              {
                "k": "code",
                "text": "adr-008-toml-driven-catalog.md"
              },
              {
                "k": "text",
                "text": " — 8 inaugural ADRs demonstrating the process"
              }
            ],
            [
              {
                "k": "text",
                "text": "memory: "
              },
              {
                "k": "code",
                "text": "POST_AUDIT_REVISIONS.md"
              },
              {
                "k": "text",
                "text": " — the process hole that motivated this meta-ADR"
              }
            ],
            [
              {
                "k": "text",
                "text": "Legacy reference: "
              },
              {
                "k": "code",
                "text": "archive/nika-v0.79/adr/LEGACY-NOTE.md"
              },
              {
                "k": "text",
                "text": " (supernovae-hq monorepo) — the rot that this ADR prevents"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Informal ADRs (blog posts, wiki pages)",
          "id": "alt-a--informal-adrs-blog-posts-wiki-pages"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Not version-controlled with the code, lost on migrations, invisible to contributors."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — ADRs in commit messages only",
          "id": "alt-b--adrs-in-commit-messages-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. git-log is lossy UI for decisions that reference each other (supersession is hard to trace)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Private ADRs (in `dx/adr/`, like legacy)",
          "id": "alt-c--private-adrs-in-dxadr-like-legacy"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Nika is AGPL open source; hiding architectural rationale from contributors is both technically counterproductive and philosophically inconsistent."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D — ADR-per-crate (nested in `crate-specs/`)",
          "id": "alt-d--adr-per-crate-nested-in-crate-specs"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Cross-cutting ADRs (layered architecture, forward-compat invariants) span multiple crates. Central "
            },
            {
              "k": "code",
              "text": "docs/adr/"
            },
            {
              "k": "text",
              "text": " preserves the sequential narrative."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-001 through ADR-008 — the inaugural batch"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/check-adr-coverage.sh"
              },
              {
                "k": "text",
                "text": " — enforcement hook"
              }
            ],
            [
              {
                "k": "text",
                "text": "Michael Nygard, "
              },
              {
                "k": "link",
                "text": "Documenting Architecture Decisions",
                "href": "https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions"
              },
              {
                "k": "text",
                "text": " (2011)"
              }
            ],
            [
              {
                "k": "code",
                "text": "archive/nika-v0.79/adr/LEGACY-NOTE.md"
              },
              {
                "k": "text",
                "text": " (supernovae-hq) — prior-art, what happened when the habit wasn't enforced"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "If after ~50 ADRs navigation becomes painful, add "
            },
            {
              "k": "code",
              "text": "docs/adr/INDEX.md"
            },
            {
              "k": "text",
              "text": " generated from ADR frontmatter (status, date, tags). Keep README.md as the human narrative; INDEX.md as the machine view."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-010",
      "file": "adr-010-miette-diagnostic-layer.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 464,
      "sha256": "3897b53ba08a06e42df7746565992d3a33fa21703b4672bf46ba62a610f7fe49",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-010: miette as the L4 diagnostic presentation layer",
          "id": "adr-010-miette-as-the-l4-diagnostic-presentation-layer"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-005 established "
            },
            {
              "k": "code",
              "text": "NikaErrorCode"
            },
            {
              "k": "text",
              "text": " trait + "
            },
            {
              "k": "code",
              "text": "Box<dyn>"
            },
            {
              "k": "text",
              "text": " unification at API boundaries — correct for routing, retry ("
            },
            {
              "k": "code",
              "text": "is_transient()"
            },
            {
              "k": "text",
              "text": "), and fingerprinting. It does NOT, however, solve "
            },
            {
              "k": "em",
              "text": "rendering"
            },
            {
              "k": "text",
              "text": ": when a user runs "
            },
            {
              "k": "code",
              "text": "nika check workflow.nika.yaml"
            },
            {
              "k": "text",
              "text": " and a template parse fails at line 14, they want source-span highlighting, a "
            },
            {
              "k": "code",
              "text": "--explain NIKA-384"
            },
            {
              "k": "text",
              "text": ", and coloured severity — not a single-line "
            },
            {
              "k": "code",
              "text": "Error: NIKA-384: ..."
            },
            {
              "k": "text",
              "text": " string."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The Rust 2026 ecosystem has converged on "
            },
            {
              "k": "strong",
              "text": "miette"
            },
            {
              "k": "text",
              "text": " for this role (ruff, uv, cargo-dist, rye all use it or miette-class renderers). "
            },
            {
              "k": "code",
              "text": "miette 7.6"
            },
            {
              "k": "text",
              "text": " is already pinned in "
            },
            {
              "k": "code",
              "text": "[workspace.dependencies]"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "NikaError"
            },
            {
              "k": "text",
              "text": " already carries "
            },
            {
              "k": "code",
              "text": "impl miette::Diagnostic"
            },
            {
              "k": "text",
              "text": ". This ADR locks the pattern and scope so downstream L4 crates inherit it consistently."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "strong",
                "text": "keeps"
              },
              {
                "k": "text",
                "text": " the "
              },
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": " trait as the structural / routing contract (ADR-005 unchanged)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "strong",
                "text": "additionally"
              },
              {
                "k": "text",
                "text": " implements "
              },
              {
                "k": "code",
                "text": "miette::Diagnostic"
              },
              {
                "k": "text",
                "text": " for "
              },
              {
                "k": "code",
                "text": "NikaError"
              },
              {
                "k": "text",
                "text": " — this is the rendering contract."
              }
            ],
            [
              {
                "k": "text",
                "text": "L4 crates ("
              },
              {
                "k": "code",
                "text": "nika-cli"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-lsp"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-serve"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-catalog-verify"
              },
              {
                "k": "text",
                "text": ") render all user-visible errors via "
              },
              {
                "k": "code",
                "text": "miette::GraphicalReportHandler"
              },
              {
                "k": "text",
                "text": " (or equivalent)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Source spans, severity, help strings, related diagnostics — all flow through miette's "
              },
              {
                "k": "code",
                "text": "Diagnostic"
              },
              {
                "k": "text",
                "text": " API."
              }
            ],
            [
              {
                "k": "code",
                "text": "--explain NIKA-NNN"
              },
              {
                "k": "text",
                "text": " CLI subcommand uses the registered help string per "
              },
              {
                "k": "code",
                "text": "NikaCode"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Library crates (L0, L0.5, L1, L2, L3) "
              },
              {
                "k": "strong",
                "text": "do NOT"
              },
              {
                "k": "text",
                "text": " depend on miette at runtime — their errors implement only "
              },
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": "; miette is purely a presentation concern."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "User-facing errors are best-in-class (source underlines, colour, hyperlinked error codes)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Library crates stay lean — no miette surface in kernel/catalog/error public types beyond the optional "
              },
              {
                "k": "code",
                "text": "Diagnostic"
              },
              {
                "k": "text",
                "text": " impl on "
              },
              {
                "k": "code",
                "text": "NikaError"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "--explain"
              },
              {
                "k": "text",
                "text": " works by registry lookup — zero per-error maintenance cost."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "L0 "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " carries a miette dep (already paid — feature-gated via "
              },
              {
                "k": "code",
                "text": "diagnostic"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Two contracts per error type ("
              },
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "Diagnostic"
              },
              {
                "k": "text",
                "text": "). Mitigated by derive helpers."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "If the community pivots to ariadne or a successor, we swap in "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " only — library crates untouched."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-error/Cargo.toml:14"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "miette = { workspace = true, features = [\"fancy-no-backtrace\"] }"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/nika_error.rs:97"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "impl miette::Diagnostic for NikaError"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/codes.rs:128"
              },
              {
                "k": "text",
                "text": " — per-code help strings"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-005 — the structural error hierarchy this extends"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — `anyhow` / `eyre` at CLI",
          "id": "alt-a--anyhow--eyre-at-cli"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Both produce flat strings — no spans, no "
            },
            {
              "k": "code",
              "text": "--explain"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Custom renderer",
          "id": "alt-b--custom-renderer"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. Reinvents miette. Not valuable."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — `error-stack` (HASH) with attachments",
          "id": "alt-c--error-stack-hash-with-attachments"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Watching. Good for structured observability context but not focused on user-facing rendering. Revisit if miette stagnates."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-005 — trait-based error hierarchy (structural contract this rendering layer wraps)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants ("
              },
              {
                "k": "code",
                "text": "NikaError"
              },
              {
                "k": "text",
                "text": " is "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-015 — "
              },
              {
                "k": "code",
                "text": "expect-test"
              },
              {
                "k": "text",
                "text": " inline snapshots (regression coverage for the rendering this ADR locks)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When "
            },
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": " is admitted, add a "
            },
            {
              "k": "code",
              "text": "test-miette-render.rs"
            },
            {
              "k": "text",
              "text": " golden test that pins the rendered output of 3 representative error codes. Prevents accidental UX regression."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-011",
      "file": "adr-011-cargo-xtask.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 451,
      "sha256": "7502a19dfaf46d86b835a37213c044ceb92e68e66c50be0d13955068070b3f15",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-011: `cargo xtask` as canonical automation surface",
          "id": "adr-011-cargo-xtask-as-canonical-automation-surface"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Diamond currently runs CI ratchets + hygiene checks via ~10 bash scripts under "
            },
            {
              "k": "code",
              "text": "scripts/ci/"
            },
            {
              "k": "text",
              "text": " and supernovae-hq parent runs 10 more under "
            },
            {
              "k": "code",
              "text": "scripts/hygiene/"
            },
            {
              "k": "text",
              "text": ". Bash works for 5 admitted crates. It does not scale to 40 — concerns:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Portability:"
              },
              {
                "k": "text",
                "text": " macOS bash 3.2 quirks already bit us (associative arrays). Windows users of "
              },
              {
                "k": "code",
                "text": "nika init"
              },
              {
                "k": "text",
                "text": " cannot run these scripts at all."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Type safety:"
              },
              {
                "k": "text",
                "text": " parsing "
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " via "
              },
              {
                "k": "code",
                "text": "awk"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "grep"
              },
              {
                "k": "text",
                "text": " is fragile; we've already fixed one regex-injection bug (supernovae-hq allowlist checker)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Composability:"
              },
              {
                "k": "text",
                "text": " rust-analyzer, Zed, tokio, rust-lang/rust itself all migrated to "
              },
              {
                "k": "code",
                "text": "cargo xtask"
              },
              {
                "k": "text",
                "text": " pattern (separate workspace crate named "
              },
              {
                "k": "code",
                "text": "xtask"
              },
              {
                "k": "text",
                "text": ", unaffiliated with release binaries)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Web researcher and Nika-SOTA architect both flagged this as the #2 impact-per-effort improvement."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Introduce an "
            },
            {
              "k": "code",
              "text": "xtask/"
            },
            {
              "k": "text",
              "text": " workspace crate (type "
            },
            {
              "k": "code",
              "text": "bin"
            },
            {
              "k": "text",
              "text": ") that owns all Diamond automation:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "cargo xtask gate <crate>"
              },
              {
                "k": "text",
                "text": " — run 12 gates against one crate"
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo xtask hygiene"
              },
              {
                "k": "text",
                "text": " — the 15-vector-adjacent parent hygiene"
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo xtask loc"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "xtask crate-size"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "xtask fn-length"
              },
              {
                "k": "text",
                "text": " — ratchet checks (replacing "
              },
              {
                "k": "code",
                "text": "scripts/ci/check-*.sh"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo xtask bench"
              },
              {
                "k": "text",
                "text": " — orchestrate criterion + divan runs"
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo xtask release <crate> <version>"
              },
              {
                "k": "text",
                "text": " — tag + CHANGELOG sync"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Shell scripts stay as "
            },
            {
              "k": "strong",
              "text": "thin shims"
            },
            {
              "k": "text",
              "text": " calling "
            },
            {
              "k": "code",
              "text": "cargo xtask <subcmd>"
            },
            {
              "k": "text",
              "text": " until fully retired. "
            },
            {
              "k": "code",
              "text": "xtask"
            },
            {
              "k": "text",
              "text": " crate is excluded from the 100-crate cap (industry convention — xtask is not a library crate)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Cross-platform (Windows for "
              },
              {
                "k": "code",
                "text": "nika init"
              },
              {
                "k": "text",
                "text": " users)."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo_metadata"
              },
              {
                "k": "text",
                "text": " crate for manifest parsing — typed, no regex."
              }
            ],
            [
              {
                "k": "text",
                "text": "Subcommand composition (can call other xtask subcmds programmatically)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Unit-testable automation logic (bash is not)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "New crate adds compile time (cold build ~15s extra one-time)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Migration is ~2 sessions of focused work for the 20+ scripts."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Convention-only exclusion from crate cap. Documented in ADR-004."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Pattern source: "
              },
              {
                "k": "link",
                "text": "rust-lang/rust x tool",
                "href": "https://github.com/rust-lang/rust/tree/master/src/tools/x"
              },
              {
                "k": "text",
                "text": ", rust-analyzer "
              },
              {
                "k": "code",
                "text": "xtask"
              },
              {
                "k": "text",
                "text": ", tokio "
              },
              {
                "k": "code",
                "text": "xtask"
              },
              {
                "k": "text",
                "text": " (upstream examples, not local paths)"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/*.sh"
              },
              {
                "k": "text",
                "text": " — 10 scripts currently"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/hygiene/*.sh"
              },
              {
                "k": "text",
                "text": " (in supernovae-hq parent) — 11 scripts"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Keep bash",
          "id": "alt-a--keep-bash"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — portability + type safety."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Python scripts",
          "id": "alt-b--python-scripts"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — adds Python runtime to CI. We're a Rust workspace."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — `just` (justfile)",
          "id": "alt-c--just-justfile"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Considered. Lower friction than xtask but still not-Rust. Loses "
            },
            {
              "k": "code",
              "text": "cargo_metadata"
            },
            {
              "k": "text",
              "text": " access. xtask is idiomatic for our scale."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-003 — 12-gate admission (xtask "
              },
              {
                "k": "code",
                "text": "gate"
              },
              {
                "k": "text",
                "text": " subcommand hosts the gates)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-004 — context-window sizing (xtask is the documented exclusion from the 100-crate cap; this ADR formalises the convention ADR-004 references)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-009 — ADR process ("
              },
              {
                "k": "code",
                "text": "check-adr-coverage.sh"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "xtask check-adr-coverage"
              },
              {
                "k": "text",
                "text": ")"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Migration is non-urgent. Execute before Phase 3 admissions (when verb crates start landing and gate complexity grows). Until then bash shims remain authoritative."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-012",
      "file": "adr-012-typestate-runtime.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 488,
      "sha256": "e589af5f6c0f8fbb7dc7199866a14f3094aa18a36dc70473ddf3d7a2bdda9bdd",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-012: Typestate for `nika-runtime` workflow lifecycle",
          "id": "adr-012-typestate-for-nika-runtime-workflow-lifecycle"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A workflow in Nika transitions through four distinct states: parsed → validated → resolved → executable. Legacy Nika tracked this at runtime with a "
            },
            {
              "k": "code",
              "text": "WorkflowState"
            },
            {
              "k": "text",
              "text": " enum — users could call "
            },
            {
              "k": "code",
              "text": ".execute()"
            },
            {
              "k": "text",
              "text": " on any workflow and the runtime would return an error if the state was wrong. The failure mode is late, at call time, after side effects may already have kicked in."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Typestate (PhantomData-parameterized structs, state transitions consuming "
            },
            {
              "k": "code",
              "text": "self"
            },
            {
              "k": "text",
              "text": ") is now canonical in serious Rust codebases: axum 0.8 (handler typestate), Embassy (HAL driver typestate), rustls (TLS handshake typestate), hyper 1.x (connection state). It turns a class of runtime bugs into compile errors."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Committing the pattern "
            },
            {
              "k": "strong",
              "text": "before"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "nika-runtime"
            },
            {
              "k": "text",
              "text": " admits prevents the team writing a "
            },
            {
              "k": "code",
              "text": "WorkflowState"
            },
            {
              "k": "text",
              "text": " enum first and retrofitting typestate later."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-runtime"
            },
            {
              "k": "text",
              "text": "'s public "
            },
            {
              "k": "code",
              "text": "Workflow<S>"
            },
            {
              "k": "text",
              "text": " type is parameterized by lifecycle state. States:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "pub struct Workflow<S: WorkflowState> { /* ... */ _s: PhantomData<S> }\n\n// Marker types (zero-sized)\npub struct Parsed;     pub struct Validated;\npub struct Resolved;   pub struct Executable;\n\n// Transitions consume self:\nimpl Workflow<Parsed> {\n    pub fn validate(self, catalog: &Catalog) -> Result<Workflow<Validated>, NikaError>;\n}\nimpl Workflow<Validated> {\n    pub fn resolve(self, ctx: &ResolveContext) -> Result<Workflow<Resolved>, NikaError>;\n}\nimpl Workflow<Resolved> {\n    pub fn bind_kernel(self, kernel: &dyn Kernel) -> Workflow<Executable>;\n}\nimpl Workflow<Executable> {\n    pub async fn execute(self) -> RunReport;\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": ".execute()"
            },
            {
              "k": "text",
              "text": " exists "
            },
            {
              "k": "strong",
              "text": "only"
            },
            {
              "k": "text",
              "text": " on "
            },
            {
              "k": "code",
              "text": "Workflow<Executable>"
            },
            {
              "k": "text",
              "text": ". It is a compile error to call it on any other state."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "\"Did we validate before running?\" becomes a type-system question, not a runtime check."
              }
            ],
            [
              {
                "k": "text",
                "text": "Skips the class of bugs flagged in shadow zone #1 ("
              },
              {
                "k": "code",
                "text": "nika serve"
              },
              {
                "k": "text",
                "text": " input trust — skipped validation path)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-cli"
              },
              {
                "k": "text",
                "text": " flow becomes "
              },
              {
                "k": "code",
                "text": "parse(...)?.validate(&cat)?.resolve(&ctx)?.bind_kernel(&k).execute().await"
              },
              {
                "k": "text",
                "text": " — self-documenting."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Learning curve for new contributors. Mitigated by an example in "
              },
              {
                "k": "code",
                "text": "docs/crate-specs/nika-runtime.md"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Some gymnastics if code wants to \"keep a workflow around\" in generic state — solved by "
              },
              {
                "k": "code",
                "text": "Box<dyn AnyWorkflow>"
              },
              {
                "k": "text",
                "text": " erasure trait where needed."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Builder-style typestate adds surface compared to a single struct. Worth the compile-time safety."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Pattern sources: axum 0.8 typed-extractor "
              },
              {
                "k": "code",
                "text": "Handler<T>"
              },
              {
                "k": "text",
                "text": ", Embassy HAL "
              },
              {
                "k": "code",
                "text": "Uart<NotInitialized> → Uart<Initialized>"
              },
              {
                "k": "text",
                "text": ", rustls "
              },
              {
                "k": "code",
                "text": "ServerConnection<Awaiting*>"
              }
            ],
            [
              {
                "k": "text",
                "text": "Shadow zone #1 ("
              },
              {
                "k": "code",
                "text": "PRE_LAUNCH_GATES.md"
              },
              {
                "k": "text",
                "text": ") — HTTP-sourced inputs auto-trusted, partially mitigated by typestate enforcement of validation step"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Runtime enum (legacy approach)",
          "id": "alt-a--runtime-enum-legacy-approach"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — pushes the check to call time."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Builder pattern without typestate",
          "id": "alt-b--builder-pattern-without-typestate"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — builder returns same type regardless of state, so compile-time guard is lost."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Phantom-lifetime instead of phantom-type",
          "id": "alt-c--phantom-lifetime-instead-of-phantom-type"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — less expressive for 4-state machine."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants (each state variant is "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP traits ("
              },
              {
                "k": "code",
                "text": "bind_kernel"
              },
              {
                "k": "text",
                "text": " takes "
              },
              {
                "k": "code",
                "text": "&dyn Kernel"
              },
              {
                "k": "text",
                "text": " per sealed-trait pattern, see ADR-014)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/crate-specs/nika-runtime.md"
              },
              {
                "k": "text",
                "text": " (to be created before admission)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "During "
            },
            {
              "k": "code",
              "text": "nika-runtime"
            },
            {
              "k": "text",
              "text": " admission spec (Phase 3), define "
            },
            {
              "k": "code",
              "text": "AnyWorkflow"
            },
            {
              "k": "text",
              "text": " erasure trait for the rare cases where state needs to be stored in a collection. Until then, keep the typestate rigid."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-013",
      "file": "adr-013-loom-concurrency-verification.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 499,
      "sha256": "de9fb3e63d7dc617ad82f27e74810d4d38c42ae4e847a2034fd55b7e74c3eba3",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-013: Loom-based concurrency verification for L0.5 + L3",
          "id": "adr-013-loom-based-concurrency-verification-for-l05--l3"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Diamond has "
            },
            {
              "k": "code",
              "text": "unsafe_code = \"forbid\""
            },
            {
              "k": "text",
              "text": " workspace-wide — no raw "
            },
            {
              "k": "code",
              "text": "unsafe"
            },
            {
              "k": "text",
              "text": " blocks. This catches one class of UB. It does "
            },
            {
              "k": "strong",
              "text": "not"
            },
            {
              "k": "text",
              "text": " catch concurrency bugs in safe-Rust primitives: a deadlock between two "
            },
            {
              "k": "code",
              "text": "parking_lot::RwLock"
            },
            {
              "k": "text",
              "text": "s, a missed wakeup on a "
            },
            {
              "k": "code",
              "text": "tokio::sync::Notify"
            },
            {
              "k": "text",
              "text": ", a lost message on an "
            },
            {
              "k": "code",
              "text": "mpsc::channel"
            },
            {
              "k": "text",
              "text": " under selective receive."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Tokio itself uses Loom (model-checks all possible interleavings of "
            },
            {
              "k": "code",
              "text": "Atomic*"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Mutex"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "mpsc"
            },
            {
              "k": "text",
              "text": ", etc.) as the "
            },
            {
              "k": "strong",
              "text": "only"
            },
            {
              "k": "text",
              "text": " reliable way to catch these bugs in its scheduler. Nika-runtime (Phase 3) will have channels, cancellation tokens, and a task graph — the exact territory where Loom has caught real bugs at tokio, crossbeam, parking_lot."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The SOTA audit recommends landing the harness pattern "
            },
            {
              "k": "strong",
              "text": "now"
            },
            {
              "k": "text",
              "text": " in "
            },
            {
              "k": "code",
              "text": "nika-kernel-mock"
            },
            {
              "k": "text",
              "text": " (which already uses tokio channels for test mocks) so that when "
            },
            {
              "k": "code",
              "text": "nika-runtime"
            },
            {
              "k": "text",
              "text": " ships, Loom is muscle memory."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-kernel-mock"
              },
              {
                "k": "text",
                "text": " gains a "
              },
              {
                "k": "code",
                "text": "tests/loom/"
              },
              {
                "k": "text",
                "text": " directory with at least one demo test proving the pattern (a 2-thread mock channel drain)."
              }
            ],
            [
              {
                "k": "code",
                "text": "[dev-dependencies]"
              },
              {
                "k": "text",
                "text": " gains "
              },
              {
                "k": "code",
                "text": "loom = \"0.7\""
              },
              {
                "k": "text",
                "text": " behind a "
              },
              {
                "k": "code",
                "text": "loom"
              },
              {
                "k": "text",
                "text": " cfg flag."
              }
            ],
            [
              {
                "k": "text",
                "text": "CI workflow "
              },
              {
                "k": "code",
                "text": "diamond-ci.yml"
              },
              {
                "k": "text",
                "text": " adds a weekly (not per-PR — too slow) Loom job:"
              },
              {
                "k": "text",
                "text": "loom:\n  schedule: cron: '0 4 * * 1'\n  run: RUSTFLAGS=\"--cfg loom\" cargo test --lib --test 'loom_*' -p nika-kernel-mock"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Every"
              },
              {
                "k": "text",
                "text": " future crate with concurrent primitives ("
              },
              {
                "k": "code",
                "text": "nika-runtime"
              },
              {
                "k": "text",
                "text": " with scheduler, "
              },
              {
                "k": "code",
                "text": "nika-daemon"
              },
              {
                "k": "text",
                "text": " with IPC, "
              },
              {
                "k": "code",
                "text": "nika-mcp"
              },
              {
                "k": "text",
                "text": " with stdio sessions) MUST ship Loom tests for the primitive at admission. Codified as invariant #29."
              }
            ],
            [
              {
                "k": "text",
                "text": "Loom failures block release tags ("
              },
              {
                "k": "code",
                "text": "git tag v0.X.0"
              },
              {
                "k": "text",
                "text": " gated on Loom green)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Catches concurrency bugs proptest cannot reach (interleaving enumeration, not property generation)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Aligns Diamond with tokio-scheduler-grade quality."
              }
            ],
            [
              {
                "k": "text",
                "text": "Forces concurrent primitive authors to design for testability (Loom hates over-coupled primitives)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Loom is slow. 2-thread tests take ~10s; 3-thread ~10 minutes. Hence weekly, not per-PR."
              }
            ],
            [
              {
                "k": "text",
                "text": "Requires a "
              },
              {
                "k": "code",
                "text": "--cfg loom"
              },
              {
                "k": "text",
                "text": " parallel build of the crate — some dep tree duplication."
              }
            ],
            [
              {
                "k": "text",
                "text": "Learning curve for Loom semantics (atomic orderings, spuriousness)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Shuttle (AWS, PCT-based, faster than Loom) is complementary. Revisit in Phase 4 when "
              },
              {
                "k": "code",
                "text": "nika-runtime"
              },
              {
                "k": "text",
                "text": " has >3-thread state spaces that exceed Loom's reach."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "tokio's "
              },
              {
                "k": "code",
                "text": "mpsc"
              },
              {
                "k": "text",
                "text": " Loom tests: "
              },
              {
                "k": "code",
                "text": "tokio-stream/tests/"
              }
            ],
            [
              {
                "k": "text",
                "text": "crossbeam's "
              },
              {
                "k": "code",
                "text": "loom_*"
              },
              {
                "k": "text",
                "text": " modules"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-kernel-mock/src/"
              },
              {
                "k": "text",
                "text": " — already uses tokio channels for test fixtures (direct beneficiary)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Proptest only",
          "id": "alt-a--proptest-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — proptest cannot enumerate interleavings."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Shuttle only",
          "id": "alt-b--shuttle-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected for now — Loom is more mature (AWS Shuttle is production but smaller community)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — No concurrency verification",
          "id": "alt-c--no-concurrency-verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — it is 2026 and we are building a workflow engine. Inexcusable."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP traits (Loom validates trait "
              },
              {
                "k": "em",
                "text": "impls"
              },
              {
                "k": "text",
                "text": ", not traits themselves)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Shadow zone #4 — L1 taint runtime (will need Loom when resolved)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Invariant #29 — \"every crate with concurrent primitives ships Loom tests\" (added to "
              },
              {
                "k": "code",
                "text": "nika-invariants.md"
              },
              {
                "k": "text",
                "text": ")"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "If weekly Loom run takes > 30 minutes, switch the Loom job to nightly-only and Shuttle per-PR. Both tools pay their keep at this tier of quality."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-014",
      "file": "adr-014-sealed-kernel-traits.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 508,
      "sha256": "bd096491222e9e1a0b44423cbdf03e62bfbf13ad9ccd4eafb5fe75247d649adc",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-014: Sealed kernel traits with explicit adapter registration",
          "id": "adr-014-sealed-kernel-traits-with-explicit-adapter-registration"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " (L0.5) defines ~20 public async traits — "
            },
            {
              "k": "code",
              "text": "Provider"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ToolExecutor"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "FsRead"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "HttpGet"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ShellExecutor"
            },
            {
              "k": "text",
              "text": ", etc. Today only "
            },
            {
              "k": "code",
              "text": "nika-kernel-mock"
            },
            {
              "k": "text",
              "text": " implements them (L0.5 test). As Phase 2 admits L1 effect crates ("
            },
            {
              "k": "code",
              "text": "nika-http"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-fs"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "nika-process"
            },
            {
              "k": "text",
              "text": "), each trait gains real implementors."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "The open question"
            },
            {
              "k": "text",
              "text": ": can an "
            },
            {
              "k": "em",
              "text": "external"
            },
            {
              "k": "text",
              "text": " crate (outside supernovae-st/nika workspace) implement these traits? If yes, every method signature becomes load-bearing across semver — adding a default-method parameter is a breaking change for external implementors. If no, we preserve flexibility to evolve method signatures as we learn."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "2026 Rust practice (axum, serde, uv) uses the "
            },
            {
              "k": "strong",
              "text": "sealed-trait pattern"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "pub trait Foo: private::Sealed { ... }"
            },
            {
              "k": "text",
              "text": ") to prevent external impls while keeping the trait public for users who depend on it via "
            },
            {
              "k": "code",
              "text": "dyn Foo"
            },
            {
              "k": "text",
              "text": ". rust-analyzer, rustls, tokio all seal core traits."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "All public traits in "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " are "
            },
            {
              "k": "strong",
              "text": "sealed"
            },
            {
              "k": "text",
              "text": " via the "
            },
            {
              "k": "code",
              "text": "private::Sealed"
            },
            {
              "k": "text",
              "text": " supertrait pattern:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "// nika-kernel/src/lib.rs\nmod private {\n    pub trait Sealed {}\n}\n\n// nika-kernel/src/provider.rs\npub trait ProviderInfer: private::Sealed + Send + Sync { /* ... */ }\n\n// nika-kernel/src/provider.rs (internal, for blanket Sealed impls)\nimpl<T: crate::Provider + ?Sized> private::Sealed for T {}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "External crates "
            },
            {
              "k": "strong",
              "text": "cannot"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "impl ProviderInfer for MyType"
            },
            {
              "k": "text",
              "text": " (compile error: "
            },
            {
              "k": "code",
              "text": "Sealed"
            },
            {
              "k": "text",
              "text": " not implemented, and "
            },
            {
              "k": "code",
              "text": "Sealed"
            },
            {
              "k": "text",
              "text": " is private). Instead, external integrations route through:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Adapter macros"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "nika_kernel::provider_impl!"
              },
              {
                "k": "text",
                "text": ") for workspace-local crates ("
              },
              {
                "k": "code",
                "text": "nika-provider-rig"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-provider-native"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-provider-mock"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`nika-builtin-*` wrappers"
              },
              {
                "k": "text",
                "text": " for ecosystem integrations (GitHub, Cloud, Workspace — ADR tbd)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Future pck packages"
              },
              {
                "k": "text",
                "text": " (plugin protocol, Phase 4+) for community extensions."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Adding a trait method still requires care, but the set of implementors is "
            },
            {
              "k": "strong",
              "text": "finite and controlled by the workspace"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Method signatures and default methods can evolve within v0.x without breaking external code (there isn't any external impl by construction)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Clear path for ecosystem extensions (adapter macros > plugin protocol > pck) — enforced architecturally."
              }
            ],
            [
              {
                "k": "text",
                "text": "Matches Rust 2026 canonical pattern."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Some friction for hypothetical out-of-workspace adapters — but there are none today, and when they emerge they belong as "
              },
              {
                "k": "code",
                "text": "nika-builtin-*"
              },
              {
                "k": "text",
                "text": " or pck packages per ADR-004's crate-count discipline."
              }
            ],
            [
              {
                "k": "text",
                "text": "Adds a "
              },
              {
                "k": "code",
                "text": "private::Sealed"
              },
              {
                "k": "text",
                "text": " module + blanket impl per trait group."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Language-level "
              },
              {
                "k": "code",
                "text": "sealed"
              },
              {
                "k": "text",
                "text": " keyword RFC is still unmerged as of 2026-Q1 — the supertrait pattern is idiomatic."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Pattern sources: axum, serde ("
              },
              {
                "k": "code",
                "text": "private::Sealed"
              },
              {
                "k": "text",
                "text": "), rustls ("
              },
              {
                "k": "code",
                "text": "server::ServerConfig"
              },
              {
                "k": "text",
                "text": " sealed), tokio-stream"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/src/lib.rs:19-34"
              },
              {
                "k": "text",
                "text": " — trait hierarchy (to be updated with sealed pattern)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Open traits (no sealing)",
          "id": "alt-a--open-traits-no-sealing"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — every method change becomes potential breakage."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Opaque impl trait only (no trait at all)",
          "id": "alt-b--opaque-impl-trait-only-no-trait-at-all"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — loses "
            },
            {
              "k": "code",
              "text": "dyn Trait"
            },
            {
              "k": "text",
              "text": " + breaks test substitutability (kernel-mock)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — `#[non_exhaustive]` on the trait",
          "id": "alt-c--non_exhaustive-on-the-trait"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Not valid in Rust — "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " is structs/enums only."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP traits (which this seals)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-012 — typestate runtime (uses sealed kernel via "
              },
              {
                "k": "code",
                "text": "bind_kernel(&dyn Kernel)"
              },
              {
                "k": "text",
                "text": ")"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Migration: sealing can be added to "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " in a single commit before any L1 effect crate admits. No breaking change to "
            },
            {
              "k": "code",
              "text": "nika-kernel-mock"
            },
            {
              "k": "text",
              "text": " (workspace-local, blanket-Sealed applies). Plan for Phase 2 Session 1."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-015",
      "file": "adr-015-expect-test-inline-snapshots.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 662,
      "sha256": "1609c5d901acdef7cc8e064b57ae578e31e29804d078f7034c09cec5035d3c14",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-015: `expect-test` for inline snapshot assertions on rendered output",
          "id": "adr-015-expect-test-for-inline-snapshot-assertions-on-rendered-output"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-010 locked "
            },
            {
              "k": "code",
              "text": "miette"
            },
            {
              "k": "text",
              "text": " as the L4 presentation layer for user-visible errors. The natural follow-on question is: "
            },
            {
              "k": "em",
              "text": "how do we test that rendering is stable?"
            },
            {
              "k": "text",
              "text": " Today we have two patterns living side-by-side:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "assert_eq!(format!(\"{}\", x), \"literal\")"
              },
              {
                "k": "text",
                "text": " — fine for one-line outputs but the literal is buried in code, no diff on failure, no easy refresh."
              }
            ],
            [
              {
                "k": "code",
                "text": "insta"
              },
              {
                "k": "text",
                "text": " — already pinned in "
              },
              {
                "k": "code",
                "text": "[workspace.dependencies]"
              },
              {
                "k": "text",
                "text": " (1.42, "
              },
              {
                "k": "code",
                "text": "yaml"
              },
              {
                "k": "text",
                "text": " feature). Excellent for medium/large structured payloads (full catalog snapshots, error-tree YAML, generated code dumps) but the snapshot file lives elsewhere, which adds a hop when reviewing PR diffs that change the rendered output."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The Rust 2026 ecosystem (rust-analyzer, ruff-equivalent crates, hashbrown, rustls test suites) overwhelmingly uses "
            },
            {
              "k": "strong",
              "text": "`expect-test`"
            },
            {
              "k": "text",
              "text": " for the third gap: short-to-medium textual outputs that you want pinned "
            },
            {
              "k": "em",
              "text": "next to the assertion"
            },
            {
              "k": "text",
              "text": ", with a one-shot refresh via "
            },
            {
              "k": "code",
              "text": "UPDATE_EXPECT=1 cargo test"
            },
            {
              "k": "text",
              "text": ". It's a 200-LOC dep with zero macros at runtime and no proc-macro compile cost."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Add "
              },
              {
                "k": "code",
                "text": "expect-test = \"1.5\""
              },
              {
                "k": "text",
                "text": " to "
              },
              {
                "k": "code",
                "text": "[workspace.dependencies]"
              },
              {
                "k": "text",
                "text": " (dev-only line)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Crate "
              },
              {
                "k": "code",
                "text": "[dev-dependencies]"
              },
              {
                "k": "text",
                "text": " opt in via "
              },
              {
                "k": "code",
                "text": "expect-test = { workspace = true }"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Use `expect-test` for"
              },
              {
                "k": "text",
                "text": ": inline assertions on rendered text where the snapshot is ≤50 lines and lives best next to the test (Display/Debug output, miette rendering, generated lints, single-error formatting)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Use `insta` for"
              },
              {
                "k": "text",
                "text": ": large structured payloads (≥50 lines, YAML/JSON serialization, full catalog dumps), redactions, multi-snapshot test cases, anything the reviewer wants to scan in a dedicated "
              },
              {
                "k": "code",
                "text": "*.snap"
              },
              {
                "k": "text",
                "text": " file."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Plain `assert_eq!` stays"
              },
              {
                "k": "text",
                "text": " for trivial one-liners with no risk of churn (e.g. "
              },
              {
                "k": "code",
                "text": "assert_eq!(NikaCode::FAILED_PRECONDITION.to_string(), \"NIKA-001\")"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Refresh workflow is "
              },
              {
                "k": "code",
                "text": "UPDATE_EXPECT=1 cargo test --workspace --lib"
              },
              {
                "k": "text",
                "text": " (mirrors "
              },
              {
                "k": "code",
                "text": "cargo insta accept"
              },
              {
                "k": "text",
                "text": "). Documented in the testing section of the agent self-serve docs (Phase E)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Closes the \"buried literal\" gap without introducing a new tool — "
              },
              {
                "k": "code",
                "text": "expect-test"
              },
              {
                "k": "text",
                "text": " is the de-facto rust-analyzer-tier choice."
              }
            ],
            [
              {
                "k": "text",
                "text": "Reviewers see the exact expected text in the same diff hunk as the code change."
              }
            ],
            [
              {
                "k": "text",
                "text": "Refresh is one env var, no extra binary install (unlike "
              },
              {
                "k": "code",
                "text": "cargo insta"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Zero runtime cost; zero proc-macro overhead at build time."
              }
            ],
            [
              {
                "k": "text",
                "text": "Diff failures print colourised side-by-side, matches developer ergonomics."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "One more dev-dep to keep in sync (offset by removing ad-hoc "
              },
              {
                "k": "code",
                "text": "assert!(s.contains(...))"
              },
              {
                "k": "text",
                "text": " clusters that grew because "
              },
              {
                "k": "code",
                "text": "assert_eq!"
              },
              {
                "k": "text",
                "text": " felt too rigid)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Two snapshot crates in the tree ("
              },
              {
                "k": "code",
                "text": "insta"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "expect-test"
              },
              {
                "k": "text",
                "text": ") — small risk of \"which one do I use\" friction, mitigated by the rule above."
              }
            ],
            [
              {
                "k": "code",
                "text": "UPDATE_EXPECT=1"
              },
              {
                "k": "text",
                "text": " requires reviewer discipline (don't bless without reading the diff)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "If "
              },
              {
                "k": "code",
                "text": "expect-test"
              },
              {
                "k": "text",
                "text": " ever stagnates the swap surface is tiny — every call site is "
              },
              {
                "k": "code",
                "text": "expect![[…]].assert_eq(&actual)"
              },
              {
                "k": "text",
                "text": ", mechanically rewritable to "
              },
              {
                "k": "code",
                "text": "insta::assert_snapshot!"
              },
              {
                "k": "text",
                "text": " or plain "
              },
              {
                "k": "code",
                "text": "assert_eq!"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Cargo.toml:90"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "expect-test = \"1.5\""
              },
              {
                "k": "text",
                "text": " pinned in "
              },
              {
                "k": "code",
                "text": "[workspace.dependencies]"
              },
              {
                "k": "text",
                "text": " dev block."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/nika_error.rs"
              },
              {
                "k": "text",
                "text": " — first use site: "
              },
              {
                "k": "code",
                "text": "display_format_pinned_inline"
              },
              {
                "k": "text",
                "text": " test pinning the "
              },
              {
                "k": "code",
                "text": "NIKA-002: not found: task foo"
              },
              {
                "k": "text",
                "text": " rendering."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/Cargo.toml"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "[dev-dependencies] expect-test.workspace = true"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Keep only `insta`",
          "id": "alt-a--keep-only-insta"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. "
            },
            {
              "k": "code",
              "text": "insta"
            },
            {
              "k": "text",
              "text": " shines on large payloads but inflates the diff burden when the snapshot is a single line. Rust ecosystem leaders co-use both for that exact reason."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — `goldenfile` / `k9` / `pretty_assertions` only",
          "id": "alt-b--goldenfile--k9--pretty_assertions-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. "
            },
            {
              "k": "code",
              "text": "goldenfile"
            },
            {
              "k": "text",
              "text": " duplicates "
            },
            {
              "k": "code",
              "text": "insta"
            },
            {
              "k": "text",
              "text": ". "
            },
            {
              "k": "code",
              "text": "k9"
            },
            {
              "k": "text",
              "text": " brings snapshotting but with heavier macros and weaker bless workflow. "
            },
            {
              "k": "code",
              "text": "pretty_assertions"
            },
            {
              "k": "text",
              "text": " improves diff output but doesn't solve the buried-literal or refresh problem."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Roll our own",
          "id": "alt-c--roll-our-own"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected. The pattern is 200 LOC and three macros. Reinventing it costs more than the dep."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-010 — miette as the L4 presentation layer (this ADR pins regression coverage "
              },
              {
                "k": "em",
                "text": "of"
              },
              {
                "k": "text",
                "text": " that layer)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants (snapshot tests are how we detect when "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " types grow new variants that change rendered output)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-catalog/src/data/snapshots/"
              },
              {
                "k": "text",
                "text": " — existing "
              },
              {
                "k": "code",
                "text": "insta"
              },
              {
                "k": "text",
                "text": " snapshots for the larger structured cases (pattern preserved)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When "
            },
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": " lands (Phase F+), the "
            },
            {
              "k": "code",
              "text": "--explain NIKA-NNN"
            },
            {
              "k": "text",
              "text": " golden test mentioned in ADR-010 §Notes should use "
            },
            {
              "k": "code",
              "text": "expect-test"
            },
            {
              "k": "text",
              "text": " for the per-code rendering and "
            },
            {
              "k": "code",
              "text": "insta"
            },
            {
              "k": "text",
              "text": " for the multi-code "
            },
            {
              "k": "code",
              "text": "--explain --all"
            },
            {
              "k": "text",
              "text": " dump. That split is the canonical example of the rule."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-016",
      "file": "adr-016-cancellation-model.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 939,
      "sha256": "588271115d446329f11b1651ddd692f2d9b4a42c29938162188e00809e7e70db",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-016: Cancellation model: future-drop primary, CancelCtx cooperative module",
          "id": "adr-016-cancellation-model-future-drop-primary-cancelctx-cooperative-module"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel today has exactly one cancellation mechanism — "
            },
            {
              "k": "code",
              "text": "ShellCancel"
            },
            {
              "k": "text",
              "text": " — and it\nis intentionally narrow."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Design decision #9 in "
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/io/process.rs:9-11"
            },
            {
              "k": "text",
              "text": " states:\n\"cancel is a "
            },
            {
              "k": "code",
              "text": "fn cancel(&self, id)"
            },
            {
              "k": "text",
              "text": " method, not a "
            },
            {
              "k": "code",
              "text": "CancellationToken"
            },
            {
              "k": "text",
              "text": " field on\n"
            },
            {
              "k": "code",
              "text": "ShellCommand"
            },
            {
              "k": "text",
              "text": ". This keeps "
            },
            {
              "k": "code",
              "text": "tokio-util"
            },
            {
              "k": "text",
              "text": " out of "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": ".\" The trait method\nitself is "
            },
            {
              "k": "code",
              "text": "ShellCancel::cancel(&self, id: &str)"
            },
            {
              "k": "text",
              "text": " at\n"
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/io/process.rs:156-159"
            },
            {
              "k": "text",
              "text": " and exists for subprocess\nmanagement (kill the child by id), not for cooperative task cancellation."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "No other cancellation mechanism existed before Wave 2:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:197-220"
              },
              {
                "k": "text",
                "text": ", 11 fields\npre-S1-B) had no cancel field."
              }
            ],
            [
              {
                "k": "code",
                "text": "ContextCompressor::compress"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/context.rs:48-58"
              },
              {
                "k": "text",
                "text": ") takes "
              },
              {
                "k": "code",
                "text": "messages"
              },
              {
                "k": "text",
                "text": " and\n"
              },
              {
                "k": "code",
                "text": "policy"
              },
              {
                "k": "text",
                "text": " only — no cancel parameter."
              }
            ],
            [
              {
                "k": "code",
                "text": "MemoryStore::recall"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs:275-279"
              },
              {
                "k": "text",
                "text": ") is\npurely query in / hits out."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "For v0.95 (DAG executor, multi-step agent loops, streaming inference) we need\ncooperative cancellation that propagates through a tree of in-flight tasks.\nTokio's "
            },
            {
              "k": "code",
              "text": "select!"
            },
            {
              "k": "text",
              "text": " plus future-drop already covers single futures, but agent\nloops and streams must check a cancellation flag between iterations to flush\npartial work cleanly."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel sits at L0.5 and must remain runtime-agnostic. Today's\n"
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/Cargo.toml:18-26"
            },
            {
              "k": "text",
              "text": " lists only "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "trait-variant"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "thiserror"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "miette"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "serde"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "serde_json"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "bytes"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "futures-core"
            },
            {
              "k": "text",
              "text": ". Adding "
            },
            {
              "k": "code",
              "text": "tokio"
            },
            {
              "k": "text",
              "text": " or "
            },
            {
              "k": "code",
              "text": "tokio-util"
            },
            {
              "k": "text",
              "text": " here would violate the layer\ncontract ("
            },
            {
              "k": "code",
              "text": "scripts/ci/check-layering.sh"
            },
            {
              "k": "text",
              "text": ")."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Cancellation is layered, not unified."
            },
            {
              "k": "text",
              "text": " Three coexisting mechanisms, each\nsolving a different problem:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Primary: future drop."
              },
              {
                "k": "text",
                "text": " Dropping a task's future stops its work.\n"
              },
              {
                "k": "code",
                "text": "tokio::select!"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "tokio::time::timeout"
              },
              {
                "k": "text",
                "text": " are the day-to-day tools.\nNo new API is needed for the common case."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Cooperative: `CancelCtx`"
              },
              {
                "k": "text",
                "text": " (new in S1-B,\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/cancel.rs:35-67"
              },
              {
                "k": "text",
                "text": "). "
              },
              {
                "k": "code",
                "text": "Arc<AtomicBool>"
              },
              {
                "k": "text",
                "text": "-backed\ntoken that all clones share. "
              },
              {
                "k": "code",
                "text": "cancel()"
              },
              {
                "k": "text",
                "text": " flips the flag; long-running\nloops poll "
              },
              {
                "k": "code",
                "text": "is_cancelled()"
              },
              {
                "k": "text",
                "text": " between iterations. Reserved field\n"
              },
              {
                "k": "code",
                "text": "InferRequest.cancel: Option<CancelCtx>"
              },
              {
                "k": "text",
                "text": " is pre-planted at v0.81 so\nfuture activation is purely additive ("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " ratchet)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Process management: `ShellCancel`"
              },
              {
                "k": "text",
                "text": " (existing,\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/process.rs:156-159"
              },
              {
                "k": "text",
                "text": "). Kill-by-id for\nsubprocesses. Stays as-is — different problem class."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "CancelCtx"
            },
            {
              "k": "text",
              "text": " deliberately uses "
            },
            {
              "k": "code",
              "text": "std::sync::atomic::AtomicBool"
            },
            {
              "k": "text",
              "text": " (no "
            },
            {
              "k": "code",
              "text": "tokio"
            },
            {
              "k": "text",
              "text": ",\nno "
            },
            {
              "k": "code",
              "text": "tokio-util"
            },
            {
              "k": "text",
              "text": "). v0.95 may "
            },
            {
              "k": "em",
              "text": "internally"
            },
            {
              "k": "text",
              "text": " wrap a\n"
            },
            {
              "k": "code",
              "text": "tokio_util::CancellationToken"
            },
            {
              "k": "text",
              "text": " if we need richer behavior (timeout-as-cancel,\nparent-child trees), but the public API of "
            },
            {
              "k": "code",
              "text": "CancelCtx"
            },
            {
              "k": "text",
              "text": " will not change."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Two explicit cancel traits planned for v0.95: "
            },
            {
              "k": "code",
              "text": "Cancellable"
            },
            {
              "k": "text",
              "text": " (poll-style check)\nand "
            },
            {
              "k": "code",
              "text": "CancelPropagator"
            },
            {
              "k": "text",
              "text": " (tree-wide propagation). Both will be defined when the\nDAG executor lands; defining them now would lock in shape choices we have not\nbenchmarked."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Pre-planting "
              },
              {
                "k": "code",
                "text": "InferRequest.cancel"
              },
              {
                "k": "text",
                "text": " at v0.81 turns \"wire cancellation\" into a\nzero-breaking-change activation later (Pattern 2, ROI 6.7x per ADR-007)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Kernel stays free of "
              },
              {
                "k": "code",
                "text": "tokio-util"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "cargo deny"
              },
              {
                "k": "text",
                "text": " continues to enforce L0.5\npurity."
              }
            ],
            [
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " is testable in isolation today ("
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/cancel.rs"
              },
              {
                "k": "text",
                "text": "\nships with 6 unit tests including clone-state propagation and Send+Sync)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Three orthogonal mechanisms keep mental model clean: drop = default,\ncooperative = loops, process = subprocess."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Two cancellation idioms (drop vs cooperative) means contributors must learn\nwhen to use which. Mitigated by docs in "
              },
              {
                "k": "code",
                "text": "cancel.rs"
              },
              {
                "k": "text",
                "text": " module header."
              }
            ],
            [
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " does not propagate timeouts. v0.95 must layer\n"
              },
              {
                "k": "code",
                "text": "tokio::time::timeout"
              },
              {
                "k": "text",
                "text": " on top, not replace it."
              }
            ],
            [
              {
                "k": "text",
                "text": "Pre-planted "
              },
              {
                "k": "code",
                "text": "Option<CancelCtx>"
              },
              {
                "k": "text",
                "text": " field is dead weight until v0.95 — accepted\ncost, ~16 bytes per "
              },
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ShellCancel's id-based signature stays unchanged. It is not migrated to\n"
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " because subprocess management is a different problem (the OS\nkills, not cooperative checking)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/cancel.rs:35-67"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " struct + impl\n(Wave 2 / S1-B)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/cancel.rs:78-128"
              },
              {
                "k": "text",
                "text": " — 6 unit tests, including\nclone-state propagation, idempotent cancel, Send+Sync."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:21"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "use crate::cancel::CancelCtx;"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "InferRequest.cancel: Option<CancelCtx>"
              },
              {
                "k": "text",
                "text": " reserved field (S1-B)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/process.rs:9-11"
              },
              {
                "k": "text",
                "text": " — design decision #9 rationale\n(no "
              },
              {
                "k": "code",
                "text": "tokio-util"
              },
              {
                "k": "text",
                "text": " in kernel)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/process.rs:156-159"
              },
              {
                "k": "text",
                "text": " —\n"
              },
              {
                "k": "code",
                "text": "ShellCancel::cancel(&self, id: &str)"
              },
              {
                "k": "text",
                "text": " method."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/Cargo.toml:18-26"
              },
              {
                "k": "text",
                "text": " — kernel deps (no tokio, no\ntokio-util)."
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — Pattern 1 (kernel traits\nupfront), Pattern 2 ("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- `tokio_util::CancellationToken` directly in kernel",
          "id": "alt-a----tokio_utilcancellationtoken-directly-in-kernel"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Use "
            },
            {
              "k": "code",
              "text": "CancellationToken"
            },
            {
              "k": "text",
              "text": " as the public type in "
            },
            {
              "k": "code",
              "text": "InferRequest.cancel"
            },
            {
              "k": "text",
              "text": ".\nRejected: pulls "
            },
            {
              "k": "code",
              "text": "tokio-util"
            },
            {
              "k": "text",
              "text": " into L0.5. Kernel must remain\nruntime-agnostic; "
            },
            {
              "k": "code",
              "text": "CancellationToken"
            },
            {
              "k": "text",
              "text": " is not "
            },
            {
              "k": "code",
              "text": "no_std"
            },
            {
              "k": "text",
              "text": " compatible and\nblocks future WASM compilation of kernel types. Wrapping it inside a\nprivate "
            },
            {
              "k": "code",
              "text": "CancelCtx"
            },
            {
              "k": "text",
              "text": " is an option for v0.95 — that keeps the public API\nruntime-agnostic."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- No cancellation field until v0.95",
          "id": "alt-b----no-cancellation-field-until-v095"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Add "
            },
            {
              "k": "code",
              "text": "cancel"
            },
            {
              "k": "text",
              "text": " only when the DAG executor needs it.\nRejected: even with "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " we must update every call site\nthat builds "
            },
            {
              "k": "code",
              "text": "InferRequest"
            },
            {
              "k": "text",
              "text": ". Pre-planting the "
            },
            {
              "k": "code",
              "text": "Option<CancelCtx>"
            },
            {
              "k": "text",
              "text": " is\n~16 bytes and zero ergonomic cost (constructor sets "
            },
            {
              "k": "code",
              "text": "None"
            },
            {
              "k": "text",
              "text": "). ROI 6.7x\nper ADR-007's calculation."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- Cancel as a method on `ProviderInfer`",
          "id": "alt-c----cancel-as-a-method-on-providerinfer"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Expose "
            },
            {
              "k": "code",
              "text": "provider.cancel(request_id)"
            },
            {
              "k": "text",
              "text": " instead of a field on the request.\nRejected: providers do not own request lifetimes; the runtime does. A\nfield on the DTO lets the runtime cancel without round-tripping through\nthe provider trait, and it composes (a workflow can hand the same\n"
            },
            {
              "k": "code",
              "text": "CancelCtx"
            },
            {
              "k": "text",
              "text": " to many providers in parallel)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- Single global cancel signal",
          "id": "alt-d----single-global-cancel-signal"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Process-wide cancellation flag (e.g., on receipt of SIGINT).\nRejected: too coarse. We need per-task and per-DAG-subtree cancellation\nso the user can cancel one branch of a workflow without killing the rest."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP trait design. ADR-016 adds "
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " as a\ndata type, not a kernel trait, so it does not extend ADR-006's trait\nlist."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants. The pre-planted "
              },
              {
                "k": "code",
                "text": "cancel"
              },
              {
                "k": "text",
                "text": " field is\nPattern 2 ("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ") applied to a v0.95 hook."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-014 — sealed kernel traits. "
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " is a struct, not a trait,\nso sealing does not apply."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-018 — runtime + sync primitives (chooses tokio rt + parking_lot).\nADR-016 explicitly does not depend on the runtime choice."
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — Pattern 1, Pattern 2."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "v0.95 DAG executor: define "
              },
              {
                "k": "code",
                "text": "Cancellable"
              },
              {
                "k": "text",
                "text": " (poll-check trait) and\n"
              },
              {
                "k": "code",
                "text": "CancelPropagator"
              },
              {
                "k": "text",
                "text": " (tree-wide propagation trait)."
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.95 may wrap "
              },
              {
                "k": "code",
                "text": "tokio_util::CancellationToken"
              },
              {
                "k": "text",
                "text": " inside "
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " —\ndecision deferred until benchmarks show whether the wrap is worth the\nadded dep."
              }
            ],
            [
              {
                "k": "text",
                "text": "Document the \"drop vs cooperative\" choice in the agent self-serve\ndocs (Phase E)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-017",
      "file": "adr-017-streaming-policy.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 870,
      "sha256": "de0a86b8522cd9f98f51d96d7d4c26f34197800a3c2d6db1990512b0ca6e1dba",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-017: Streaming policy: bounded mpsc(32), ReceiverStream at kernel boundary",
          "id": "adr-017-streaming-policy-bounded-mpsc32-receiverstream-at-kernel-boundary"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel exposes one streaming type alias and one streaming trait method."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Type alias ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/provider.rs:380"
            },
            {
              "k": "text",
              "text": "):"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "pub type InferEventStream =\n    Pin<Box<dyn Stream<Item = Result<InferEvent, ProviderError>> + Send>>;"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Trait method ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/provider.rs:451-455"
            },
            {
              "k": "text",
              "text": "):"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[trait_variant::make(ProviderStreamDyn: Send)]\npub trait ProviderStream: Send + Sync {\n    async fn infer_stream(&self, request: InferRequest)\n        -> Result<InferEventStream, ProviderError>;\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "InferEventStream"
            },
            {
              "k": "text",
              "text": " is a boxed "
            },
            {
              "k": "code",
              "text": "dyn Stream"
            },
            {
              "k": "text",
              "text": " from "
            },
            {
              "k": "code",
              "text": "futures_core::Stream"
            },
            {
              "k": "text",
              "text": "\n("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/provider.rs:18"
            },
            {
              "k": "text",
              "text": " import). The kernel deliberately\ndepends on "
            },
            {
              "k": "code",
              "text": "futures-core"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/Cargo.toml:25"
            },
            {
              "k": "text",
              "text": ") but not on\n"
            },
            {
              "k": "code",
              "text": "tokio"
            },
            {
              "k": "text",
              "text": " — channel implementation lives in L1/L2."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The mock provider ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-mock/src/provider.rs"
            },
            {
              "k": "text",
              "text": ") demonstrates the\npattern by hand-rolling an inline "
            },
            {
              "k": "code",
              "text": "EmptyStream"
            },
            {
              "k": "text",
              "text": " struct (no channel needed for\nan always-empty mock). Real providers will internally use a Tokio "
            },
            {
              "k": "code",
              "text": "mpsc"
            },
            {
              "k": "text",
              "text": "\nchannel and wrap the receiver via "
            },
            {
              "k": "code",
              "text": "tokio_stream::wrappers::ReceiverStream"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "LLM streaming responses can produce thousands of SSE events. Anthropic's\nextended thinking traces and OpenAI's "
            },
            {
              "k": "code",
              "text": "o3"
            },
            {
              "k": "text",
              "text": " reasoning-token streams already\nexceed 100k tokens per request in the wild. Unbounded queues at the boundary\nrisk OOM if the consumer is slower than the wire (typical when the consumer\nalso runs tool calls between deltas)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Invariant #16 ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/src/lib.rs:31"
            },
            {
              "k": "text",
              "text": " reference) reminds that\n"
            },
            {
              "k": "code",
              "text": "parking_lot::RwLockReadGuard"
            },
            {
              "k": "text",
              "text": " is "
            },
            {
              "k": "code",
              "text": "!Send"
            },
            {
              "k": "text",
              "text": " — guards must never be held across\n"
            },
            {
              "k": "code",
              "text": ".await"
            },
            {
              "k": "text",
              "text": ". This applies to stream consumers: don't hold a lock while pulling\nfrom a stream."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "All streaming in Nika is bounded; the kernel boundary erases the channel\ntype."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "At the kernel boundary, "
              },
              {
                "k": "code",
                "text": "ProviderStream::infer_stream"
              },
              {
                "k": "text",
                "text": " returns\n"
              },
              {
                "k": "code",
                "text": "InferEventStream = Pin<Box<dyn Stream<Item = Result<InferEvent, ProviderError>> + Send>>"
              },
              {
                "k": "text",
                "text": ".\nThis is "
              },
              {
                "k": "code",
                "text": "futures_core::Stream"
              },
              {
                "k": "text",
                "text": " (runtime-agnostic), not a Tokio channel."
              }
            ],
            [
              {
                "k": "text",
                "text": "L1/L2 implementations internally use "
              },
              {
                "k": "code",
                "text": "tokio::sync::mpsc::channel(32)"
              },
              {
                "k": "text",
                "text": "\npaired with "
              },
              {
                "k": "code",
                "text": "tokio_stream::wrappers::ReceiverStream"
              },
              {
                "k": "text",
                "text": " to produce the boxed\nstream returned to the kernel."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Buffer size is 32."
              },
              {
                "k": "text",
                "text": " Big enough to absorb bursty SSE chunks from\nAnthropic/OpenAI without blocking the HTTP reader. Small enough to enforce\nbackpressure when the consumer pauses (e.g., to run a tool call). Tested\nagainst rig-core's typical SSE rate."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Unbounded channels are banned"
              },
              {
                "k": "text",
                "text": " anywhere in the workspace\n("
              },
              {
                "k": "code",
                "text": "tokio::sync::mpsc::unbounded_channel"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "flume::unbounded"
              },
              {
                "k": "text",
                "text": ", etc.). Future\ncustom clippy lint will enforce."
              }
            ],
            [
              {
                "k": "text",
                "text": "Consumer side: "
              },
              {
                "k": "code",
                "text": "StreamExt::next()"
              },
              {
                "k": "text",
                "text": " loop with explicit timeout\n("
              },
              {
                "k": "code",
                "text": "tokio::time::timeout"
              },
              {
                "k": "text",
                "text": "). "
              },
              {
                "k": "strong",
                "text": "No `.collect::<Vec<_>>()`"
              },
              {
                "k": "text",
                "text": " on streams (unbounded\nmemory). If the caller wants the whole response, they must fold with a\nbounded buffer or use the non-streaming "
              },
              {
                "k": "code",
                "text": "ProviderInfer::infer"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Bounded buffer ⇒ predictable memory usage even on extended thinking\nresponses with 100k+ tokens."
              }
            ],
            [
              {
                "k": "text",
                "text": "Backpressure flows from consumer to provider naturally; slow consumers\npause the HTTP reader, no manual flow-control knobs."
              }
            ],
            [
              {
                "k": "text",
                "text": "Boxed "
              },
              {
                "k": "code",
                "text": "dyn Stream"
              },
              {
                "k": "text",
                "text": " keeps "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": " trait object-safe — needed for the\nregistry of providers in "
              },
              {
                "k": "code",
                "text": "nika-runtime"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Kernel stays runtime-agnostic. Channel choice is an L1/L2 implementation\ndetail; swapping "
              },
              {
                "k": "code",
                "text": "mpsc"
              },
              {
                "k": "text",
                "text": " for another bounded channel does not break the\ntrait."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Pin<Box<dyn Stream>>"
              },
              {
                "k": "text",
                "text": " allocates once per inference. Not free, but cheap\nnext to the inference itself (~ns vs. seconds)."
              }
            ],
            [
              {
                "k": "text",
                "text": "\"32\" is a magic number. We accept it because benchmarking with rig-core\nshowed no throughput difference at 64 or 128, and 32 saves memory in the\ncommon case (most streams are <32 events/sec)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Hand-rolled "
              },
              {
                "k": "code",
                "text": "EmptyStream"
              },
              {
                "k": "text",
                "text": " in the mock duplicates the "
              },
              {
                "k": "code",
                "text": "Stream"
              },
              {
                "k": "text",
                "text": " impl\nboilerplate — accepted because pulling "
              },
              {
                "k": "code",
                "text": "tokio"
              },
              {
                "k": "text",
                "text": " into the mock dev-deps\njust for this would be heavier."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "futures-core"
              },
              {
                "k": "text",
                "text": " (not "
              },
              {
                "k": "code",
                "text": "futures"
              },
              {
                "k": "text",
                "text": " or "
              },
              {
                "k": "code",
                "text": "futures-util"
              },
              {
                "k": "text",
                "text": ") stays the only stream\ndep in kernel. L1/L2 may add "
              },
              {
                "k": "code",
                "text": "tokio-stream"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "futures-util"
              },
              {
                "k": "text",
                "text": " as needed."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:380"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "InferEventStream"
              },
              {
                "k": "text",
                "text": " type alias."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:18"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "use futures_core::Stream"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:451-455"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "ProviderStream"
              },
              {
                "k": "text",
                "text": " trait."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/Cargo.toml:25"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "futures-core = { workspace = true }"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/provider.rs"
              },
              {
                "k": "text",
                "text": " — hand-rolled "
              },
              {
                "k": "code",
                "text": "EmptyStream"
              },
              {
                "k": "text",
                "text": "\n(no channel) in the mock."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/src/lib.rs"
              },
              {
                "k": "text",
                "text": " — INV-016 reference (locks not across\n"
              },
              {
                "k": "code",
                "text": ".await"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- Unbounded channels (simpler API)",
          "id": "alt-a----unbounded-channels-simpler-api"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "tokio::sync::mpsc::unbounded_channel"
            },
            {
              "k": "text",
              "text": " everywhere.\nRejected: OOM risk on long streaming responses. Real Anthropic extended\nthinking traces have already hit 100k+ tokens; an unbounded queue would\nbuffer all of them in RAM."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- `async-channel` instead of Tokio mpsc",
          "id": "alt-b----async-channel-instead-of-tokio-mpsc"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Third-party crate with simpler ergonomics.\nRejected: Tokio mpsc is already in the dep tree (via "
            },
            {
              "k": "code",
              "text": "tokio"
            },
            {
              "k": "text",
              "text": " itself in L1).\nAdding another channel impl is dependency bloat for no functional gain."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- Return `impl Stream` instead of `Pin<Box<dyn Stream>>`",
          "id": "alt-c----return-impl-stream-instead-of-pinboxdyn-stream"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Avoid the box allocation.\nRejected: not object-safe. The provider registry stores "
            },
            {
              "k": "code",
              "text": "Box<dyn Provider>"
            },
            {
              "k": "text",
              "text": "\ntrait objects; method-level "
            },
            {
              "k": "code",
              "text": "impl Stream"
            },
            {
              "k": "text",
              "text": " would force every consumer to be\ngeneric, which kills the registry pattern."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- Larger buffer (64 or 128)",
          "id": "alt-d----larger-buffer-64-or-128"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"More buffer = more throughput.\"\nRejected: benchmarking showed no throughput change. Bigger buffer means more\nin-flight events held in RAM under slow-consumer scenarios."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt E -- Smaller buffer (8 or 16)",
          "id": "alt-e----smaller-buffer-8-or-16"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Tighter backpressure.\nRejected: bursty SSE from Anthropic can deliver 20+ events in a single TCP\nread. A buffer of 16 would block the HTTP reader on every burst, hurting\nTTFT."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — Provider trait ISP design (defines "
              },
              {
                "k": "code",
                "text": "ProviderStream"
              },
              {
                "k": "text",
                "text": " separately\nfrom "
              },
              {
                "k": "code",
                "text": "ProviderInfer"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-016 — Cancellation. Stream consumers should hold a "
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " and\npoll "
              },
              {
                "k": "code",
                "text": "is_cancelled()"
              },
              {
                "k": "text",
                "text": " between events for cooperative shutdown."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-018 — Runtime + sync primitives. Locking guidance applies to stream\nconsumers (no guards across "
              },
              {
                "k": "code",
                "text": ".await"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "When verb-* crates (verb-infer, verb-agent) are admitted, audit them for\nunbounded channels."
              }
            ],
            [
              {
                "k": "text",
                "text": "Add a custom clippy lint ("
              },
              {
                "k": "code",
                "text": "nika-lints::no_unbounded_channels"
              },
              {
                "k": "text",
                "text": ") when\n"
              },
              {
                "k": "code",
                "text": "nika-lints"
              },
              {
                "k": "text",
                "text": " ships (Phase 4+)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Document the \"no "
              },
              {
                "k": "code",
                "text": ".collect()"
              },
              {
                "k": "text",
                "text": " on streams\" rule in the agent self-serve\ndocs (Phase E)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-018",
      "file": "adr-018-runtime-sync-primitives.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 917,
      "sha256": "eff13e852e044c13af09efe555bbb61251d86b33ee29dd87ff145427ccc45819",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-018: Runtime + sync primitives: single Tokio rt, parking_lot default",
          "id": "adr-018-runtime--sync-primitives-single-tokio-rt-parking_lot-default"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The workspace has an opinionated lint policy\n("
            },
            {
              "k": "code",
              "text": "Cargo.toml [workspace.lints.clippy]"
            },
            {
              "k": "text",
              "text": ") that already denies "
            },
            {
              "k": "code",
              "text": "unwrap_used"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "expect_used"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "panic"
            },
            {
              "k": "text",
              "text": ", and the full "
            },
            {
              "k": "code",
              "text": "correctness"
            },
            {
              "k": "text",
              "text": " group. The "
            },
            {
              "k": "code",
              "text": "suspicious"
            },
            {
              "k": "text",
              "text": "\ngroup, which contains "
            },
            {
              "k": "code",
              "text": "await_holding_lock"
            },
            {
              "k": "text",
              "text": ", is currently set to "
            },
            {
              "k": "code",
              "text": "warn"
            },
            {
              "k": "text",
              "text": " not\n"
            },
            {
              "k": "code",
              "text": "deny"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel's "
            },
            {
              "k": "code",
              "text": "Clock"
            },
            {
              "k": "text",
              "text": " trait ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/io/clock.rs"
            },
            {
              "k": "text",
              "text": ") demonstrates the\nhybrid sync/async rule: "
            },
            {
              "k": "code",
              "text": "Clock::now()"
            },
            {
              "k": "text",
              "text": " is sync, "
            },
            {
              "k": "code",
              "text": "Clock::sleep(Duration)"
            },
            {
              "k": "text",
              "text": " is\nasync. Async appears only where it is mandatory (timer wakeups). Everything\nelse is sync."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel's process module\n("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/io/process.rs:9-11"
            },
            {
              "k": "text",
              "text": ") explicitly states design decision\n#9: cancel is "
            },
            {
              "k": "code",
              "text": "fn cancel(&self, id)"
            },
            {
              "k": "text",
              "text": ", not a "
            },
            {
              "k": "code",
              "text": "CancellationToken"
            },
            {
              "k": "text",
              "text": " field. The\nintent is \"tokio-util out of nika-kernel\"."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "All kernel collections use "
            },
            {
              "k": "code",
              "text": "BTreeMap"
            },
            {
              "k": "text",
              "text": " (per ADR-006) for deterministic\niteration. "
            },
            {
              "k": "code",
              "text": "HashMap"
            },
            {
              "k": "text",
              "text": " is allowed only in private internals where iteration\norder does not leak."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "INV-016 reminds: "
            },
            {
              "k": "code",
              "text": "parking_lot::RwLockReadGuard"
            },
            {
              "k": "text",
              "text": " is "
            },
            {
              "k": "code",
              "text": "!Send"
            },
            {
              "k": "text",
              "text": ". Holding a guard\nacross "
            },
            {
              "k": "code",
              "text": ".await"
            },
            {
              "k": "text",
              "text": " is unsound; INV-029 adds the Loom test requirement (ADR-013)\nfor any crate with concurrent primitives."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "One runtime, one synchronous lock library, one rule for guards across\n`.await`."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Runtime: a single Tokio multi-threaded runtime per process."
              },
              {
                "k": "text",
                "text": " Daemons\n(nika-daemon) spawn the runtime in "
              },
              {
                "k": "code",
                "text": "main"
              },
              {
                "k": "text",
                "text": ". The CLI uses\n"
              },
              {
                "k": "code",
                "text": "#[tokio::main(flavor = \"multi_thread\")]"
              },
              {
                "k": "text",
                "text": ". No "
              },
              {
                "k": "code",
                "text": "new_current_thread"
              },
              {
                "k": "text",
                "text": " in\nproduction code paths. Tests may use either flavor."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Synchronous locks: `parking_lot::Mutex` / `parking_lot::RwLock`"
              },
              {
                "k": "text",
                "text": " by\ndefault. "
              },
              {
                "k": "code",
                "text": "std::sync::Mutex"
              },
              {
                "k": "text",
                "text": " is banned in production code (future\n"
              },
              {
                "k": "code",
                "text": "nika-lints"
              },
              {
                "k": "text",
                "text": " custom rule will enforce)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`clippy::await_holding_lock` will be elevated to `deny`"
              },
              {
                "k": "text",
                "text": " in\n"
              },
              {
                "k": "code",
                "text": "[workspace.lints.clippy]"
              },
              {
                "k": "text",
                "text": ". Currently caught by "
              },
              {
                "k": "code",
                "text": "suspicious = \"warn\""
              },
              {
                "k": "text",
                "text": ";\nthe upgrade lands in a separate \"lint hardening\" commit so this ADR can\nship docs-only."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Async sync primitives:"
              },
              {
                "k": "text",
                "text": "- `tokio::sync::Semaphore` for concurrency limiting (e.g., `for_each`\n  parallelism cap, max parallel provider calls).\n- `tokio::sync::oneshot` for single-value async handoff.\n- `tokio::sync::watch` for configuration hot-reload broadcasts.\n- `tokio::sync::Notify` for wake-up coordination without a value.\n- `tokio::sync::Mutex` only when the critical section spans `.await` —\n  otherwise `parking_lot::Mutex` (no async overhead)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No third-party runtimes."
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "async-std"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "smol"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "glommio"
              },
              {
                "k": "text",
                "text": ": not in the\ndep tree, not allowed."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Lock-across-`.await` is forbidden."
              },
              {
                "k": "text",
                "text": " INV-016 is the day-to-day rule.\nGuards live in tight scopes; if data is needed across yield points, clone\nit out, drop the guard, then "
              },
              {
                "k": "code",
                "text": ".await"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Single runtime ⇒ predictable scheduling, no cross-runtime deadlocks, one\nset of metrics to monitor."
              }
            ],
            [
              {
                "k": "code",
                "text": "parking_lot"
              },
              {
                "k": "text",
                "text": " locks are faster under contention than stdlib (no\npoisoning, smaller guard size, faster uncontended path)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Banning "
              },
              {
                "k": "code",
                "text": "std::sync::Mutex"
              },
              {
                "k": "text",
                "text": " removes the poisoning footgun and aligns the\nwhole workspace on one lock library."
              }
            ],
            [
              {
                "k": "text",
                "text": "Elevating "
              },
              {
                "k": "code",
                "text": "await_holding_lock"
              },
              {
                "k": "text",
                "text": " to "
              },
              {
                "k": "code",
                "text": "deny"
              },
              {
                "k": "text",
                "text": " makes INV-016 a compile-time\nerror, not a runtime hazard."
              }
            ],
            [
              {
                "k": "text",
                "text": "Small async-sync surface (Semaphore / oneshot / watch / Notify) is easy\nto teach."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "New contributors familiar with "
              },
              {
                "k": "code",
                "text": "std::sync::Mutex"
              },
              {
                "k": "text",
                "text": " will hit the lint and\nneed orientation. Mitigated by the workspace-level deny + module-level\ndocs."
              }
            ],
            [
              {
                "k": "code",
                "text": "tokio::sync::Mutex"
              },
              {
                "k": "text",
                "text": " is intentionally rare; teams used to it as a default\nmust adjust. Documented in module headers."
              }
            ],
            [
              {
                "k": "text",
                "text": "Single runtime means the daemon and the CLI share a tokio version. Forces\nworkspace dep alignment but that is already enforced by Cargo."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Loom tests (ADR-013, INV-029) verify the lock discipline for crates with\nconcurrent primitives. ADR-018 does not change the Loom strategy, just\nthe lock library on top."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Cargo.toml [workspace.lints.clippy]"
              },
              {
                "k": "text",
                "text": " — current lint policy\n("
              },
              {
                "k": "code",
                "text": "unwrap_used = \"deny\""
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "panic = \"deny\""
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "correctness = { level = \"deny\" }"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "code",
                "text": "Cargo.toml [workspace.dependencies] parking_lot"
              },
              {
                "k": "text",
                "text": " — already pinned."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/clock.rs"
              },
              {
                "k": "text",
                "text": " — sync/async hybrid pattern."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/process.rs:9-11"
              },
              {
                "k": "text",
                "text": " — design decision #9 (no\ntokio-util in kernel)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/Cargo.toml:21"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "parking_lot = { workspace = true }"
              },
              {
                "k": "text",
                "text": "\nin mocks (validates the rule)."
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — INV-016, INV-029."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-013 — Loom concurrency verification."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- `std::sync::Mutex` everywhere",
          "id": "alt-a----stdsyncmutex-everywhere"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"It's in the standard library, no extra dep.\"\nRejected: stdlib "
            },
            {
              "k": "code",
              "text": "Mutex"
            },
            {
              "k": "text",
              "text": " poisons on panic (every "
            },
            {
              "k": "code",
              "text": "lock()"
            },
            {
              "k": "text",
              "text": " returns "
            },
            {
              "k": "code",
              "text": "Result"
            },
            {
              "k": "text",
              "text": ",\nforcing "
            },
            {
              "k": "code",
              "text": ".unwrap()"
            },
            {
              "k": "text",
              "text": " patterns we have already banned). "
            },
            {
              "k": "code",
              "text": "parking_lot"
            },
            {
              "k": "text",
              "text": " is\nfaster, smaller, and panics-clean."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- `async-std` runtime",
          "id": "alt-b----async-std-runtime"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Single-file runtime, looks tempting.\nRejected: ecosystem consolidation. "
            },
            {
              "k": "code",
              "text": "rig-core"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "reqwest"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "axum"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "tokio-postgres"
            },
            {
              "k": "text",
              "text": "\nall require Tokio. Adding "
            },
            {
              "k": "code",
              "text": "async-std"
            },
            {
              "k": "text",
              "text": " would force compatibility shims for\nevery L1 crate."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- Lock-free data structures everywhere",
          "id": "alt-c----lock-free-data-structures-everywhere"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "crossbeam"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "dashmap"
            },
            {
              "k": "text",
              "text": ", atomics-only.\nRejected: overkill for kernel data. "
            },
            {
              "k": "code",
              "text": "parking_lot"
            },
            {
              "k": "text",
              "text": " is the right complexity\nlevel for our access patterns. "
            },
            {
              "k": "code",
              "text": "DashMap"
            },
            {
              "k": "text",
              "text": " is fine in specific hotspots\n(catalog lookup), but is not the default."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- `tokio::sync::Mutex` as default",
          "id": "alt-d----tokiosyncmutex-as-default"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Async-aware, never blocks.\"\nRejected: every "
            },
            {
              "k": "code",
              "text": "lock().await"
            },
            {
              "k": "text",
              "text": " is a yield point; using it as default would\nsprinkle yields in code that does not need them, and slows uncontended\npaths. Reserve "
            },
            {
              "k": "code",
              "text": "tokio::sync::Mutex"
            },
            {
              "k": "text",
              "text": " for the (rare) case where the critical\nsection legitimately spans "
            },
            {
              "k": "code",
              "text": ".await"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt E -- Multiple runtimes per process",
          "id": "alt-e----multiple-runtimes-per-process"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "One runtime for I/O, one for compute (ML inference).\nRejected: cross-runtime channels are subtle and prone to deadlock; the\nbenefit (CPU-bound work isolation) is better served by "
            },
            {
              "k": "code",
              "text": "tokio::task::spawn_blocking"
            },
            {
              "k": "text",
              "text": "\nor a dedicated "
            },
            {
              "k": "code",
              "text": "rayon"
            },
            {
              "k": "text",
              "text": " pool — neither requires a second runtime."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP trait design. ADR-018 is the runtime substrate\nunderneath ADR-006's traits."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-013 — Loom concurrency verification (INV-029). ADR-018 chooses the\nlock library; ADR-013 verifies it under concurrent stress."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-016 — Cancellation model. Both ADRs share the \"no tokio-util in\nkernel\" rule."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-017 — Streaming policy. Bounded mpsc(32) is the streaming face of\nthis runtime choice."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Elevate "
              },
              {
                "k": "code",
                "text": "clippy::await_holding_lock"
              },
              {
                "k": "text",
                "text": " from "
              },
              {
                "k": "code",
                "text": "suspicious = \"warn\""
              },
              {
                "k": "text",
                "text": " to\n"
              },
              {
                "k": "code",
                "text": "deny"
              },
              {
                "k": "text",
                "text": " in the workspace lints. Land in a separate \"lint hardening\"\ncommit so that any preexisting violations are surfaced + fixed in\nisolation."
              }
            ],
            [
              {
                "k": "text",
                "text": "Add "
              },
              {
                "k": "code",
                "text": "nika-lints::no_std_sync_mutex"
              },
              {
                "k": "text",
                "text": " custom lint when "
              },
              {
                "k": "code",
                "text": "nika-lints"
              },
              {
                "k": "text",
                "text": "\nships (Phase 4+)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Document daemon vs CLI runtime spawning rules in the agent self-serve\ndocs (Phase E)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-019",
      "file": "adr-019-retry-timeout-ownership.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 1076,
      "sha256": "6b91e0f14c5016c1e1f8c9284bac3e6fc36861d2815212efed10ccd98201e85c",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-019: Retry + timeout ownership: stratified by layer",
          "id": "adr-019-retry--timeout-ownership-stratified-by-layer"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel already exposes building blocks for retry decisions but does not\nown a retry policy."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "ProviderError"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/provider.rs:386-424"
            },
            {
              "k": "text",
              "text": ") carries the\ninformation needed to decide retryability:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "RateLimited { retry_after_ms: Option<u64> }"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "provider.rs:407-410"
              },
              {
                "k": "text",
                "text": ") — explicit hint from the provider."
              }
            ],
            [
              {
                "k": "code",
                "text": "Api { status: u16, message: String }"
              },
              {
                "k": "text",
                "text": " — generic API failure with an HTTP\nstatus code."
              }
            ],
            [
              {
                "k": "code",
                "text": "is_transient(&self) -> bool"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "provider.rs:428-439"
              },
              {
                "k": "text",
                "text": ") — returns "
              },
              {
                "k": "code",
                "text": "true"
              },
              {
                "k": "text",
                "text": " for\n"
              },
              {
                "k": "code",
                "text": "RateLimited"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "Api { status: 500..=599, .. }"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "ShellCommand"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/io/process.rs"
            },
            {
              "k": "text",
              "text": ") already has a "
            },
            {
              "k": "code",
              "text": "timeout: Option<Duration>"
            },
            {
              "k": "text",
              "text": " field; "
            },
            {
              "k": "code",
              "text": "ShellError::Timeout { duration_ms: u64 }"
            },
            {
              "k": "text",
              "text": " is the\nmatching error variant."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "ToolExecError"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-runtime/src/tool_executor.rs"
            },
            {
              "k": "text",
              "text": ") has "
            },
            {
              "k": "code",
              "text": "NotAvailable"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "NotFound"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Timeout"
            },
            {
              "k": "text",
              "text": ", but no "
            },
            {
              "k": "code",
              "text": "is_transient()"
            },
            {
              "k": "text",
              "text": " — tool failures are\npredominantly semantic (wrong args, tool missing). Retrying them masks bugs."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "AgentLoopConfig"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-runtime/src/agent.rs"
            },
            {
              "k": "text",
              "text": ") controls iterations and\nbudgets, but has no "
            },
            {
              "k": "code",
              "text": "retry:"
            },
            {
              "k": "text",
              "text": " block — agent loop control and provider retry\nare different concerns."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Workflow YAML supports a "
            },
            {
              "k": "code",
              "text": "retry:"
            },
            {
              "k": "text",
              "text": " block at the task level today (legacy).\nEngine-level retry needs to respect it without re-implementing the policy at\nevery layer."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "NIKA-100 + NIKA-107 (MCP connection errors) already surface to users with\nguidance to retry; a sane default retry helps avoid noisy false negatives."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Retry and timeout ownership is stratified by architectural layer. Each\nlayer owns the policy that matches its failure mode."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Retry layers",
          "id": "retry-layers"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "L1 -- HTTP transport retry"
              },
              {
                "k": "text",
                "text": " (owned by "
              },
              {
                "k": "code",
                "text": "nika-http"
              },
              {
                "k": "text",
                "text": "):"
              },
              {
                "k": "text",
                "text": "- Connect / DNS / TLS handshake failures.\n- 3 attempts, exponential backoff (250ms / 1s / 4s), 50% jitter.\n- Configurable per-request via `RequestOptions`.\n- Respects `Retry-After` header on 429/503.\n"
              }
            ],
            [
              {
                "k": "strong",
                "text": "L2 -- Provider transient retry"
              },
              {
                "k": "text",
                "text": " (owned by verb-* crates):"
              },
              {
                "k": "text",
                "text": "- 429 (RateLimited) and 5xx (Api) failures from `ProviderError::is_transient`.\n- Default: 3 attempts, exponential backoff (1s / 4s / 16s), jitter.\n- Respects `RateLimited.retry_after_ms` when present (overrides backoff).\n- Configurable per-task via workflow YAML `retry:` block.\n"
              }
            ],
            [
              {
                "k": "strong",
                "text": "L2 -- ToolExecutor: no automatic retry."
              },
              {
                "k": "text",
                "text": "- Tool failures are semantic (wrong params, tool missing, semantic error).\n- Retrying masks bugs.\n- User opts in explicitly via workflow `retry:` block on the task."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Timeout ownership",
          "id": "timeout-ownership"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Boundary · Owner · Default · Source"
              }
            ],
            [
              {
                "k": "text",
                "text": "HTTP connect · L1 (`nika-http`) · 10 s · `RequestOptions.connect_timeout`"
              }
            ],
            [
              {
                "k": "text",
                "text": "HTTP read · L1 (`nika-http`) · 120 s · `RequestOptions.read_timeout` (long LLM responses)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Tool execution · L2 (`nika-process` or tool impl) · per `ShellCommand.timeout` · workflow YAML"
              }
            ],
            [
              {
                "k": "text",
                "text": "Task · L2 (runtime) · none · workflow YAML `timeout:` field"
              }
            ],
            [
              {
                "k": "text",
                "text": "Agent loop total · L2 (verb-agent) · none · `AgentLoopConfig.limits.max_duration_secs`"
              }
            ],
            [
              {
                "k": "text",
                "text": "Whole workflow · none · unlimited · by design"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Shared types (planned, ships in S1-C)",
          "id": "shared-types-planned-ships-in-s1-c"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "RetryConfig"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "ErrorCategory"
            },
            {
              "k": "text",
              "text": " will live in "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " (L0) so every\nlayer can speak the same vocabulary without depending on L0.5 kernel:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "// Sketch (real impl in S1-C / ADR-033)\n#[non_exhaustive]\npub struct RetryConfig {\n    pub max_attempts: u32,\n    pub initial_backoff: Duration,\n    pub max_backoff: Duration,\n    pub jitter: f32,\n}\n\n#[non_exhaustive]\npub enum ErrorCategory {\n    Transient,\n    RateLimited,\n    Auth,\n    NotFound,\n    Permanent,\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The implementation chooses "
            },
            {
              "k": "em",
              "text": "when"
            },
            {
              "k": "text",
              "text": " to apply a "
            },
            {
              "k": "code",
              "text": "RetryConfig"
            },
            {
              "k": "text",
              "text": " based on\n"
            },
            {
              "k": "code",
              "text": "ErrorCategory"
            },
            {
              "k": "text",
              "text": "; the kernel does not pick a policy."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Each retry layer has a single responsibility — easy to reason about\nfailures."
              }
            ],
            [
              {
                "k": "text",
                "text": "HTTP retries cannot mask provider-level rate limits (which need\n"
              },
              {
                "k": "code",
                "text": "retry-after"
              },
              {
                "k": "text",
                "text": " respect)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Tool failures are not silently retried — bugs surface immediately."
              }
            ],
            [
              {
                "k": "text",
                "text": "One vocabulary ("
              },
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": ") shared across crates,\nno per-crate retry types."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Three retry layers means the user must understand which one is firing\nwhen something goes wrong. Mitigated by tagging retry events with\nthe layer name ("
              },
              {
                "k": "code",
                "text": "http"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "provider"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "task"
              },
              {
                "k": "text",
                "text": ") in the EventLog."
              }
            ],
            [
              {
                "k": "text",
                "text": "Cumulative latency under cascading failures (HTTP retried 3× then\nprovider retried 3× = up to 9 round-trips). Accepted because each\nlayer's max-attempts is small and bounded."
              }
            ],
            [
              {
                "k": "text",
                "text": "Workflow-level retry config has to flow through to the right layer —\nneeds careful documentation in the YAML reference."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "ProviderError::is_transient"
              },
              {
                "k": "text",
                "text": " stays as the kernel's only retry\nclassifier. No new traits needed at L0.5."
              }
            ],
            [
              {
                "k": "code",
                "text": "tower::retry::Retry"
              },
              {
                "k": "text",
                "text": " is not adopted (see Alt D); we may revisit at\nv0.100 when tower's middleware gets seriously evaluated."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:407-410"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "RateLimited { retry_after_ms }"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:428-439"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "is_transient()"
              },
              {
                "k": "text",
                "text": " impl."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/process.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "ShellCommand.timeout"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "ShellError::Timeout"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-runtime/src/tool_executor.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "ToolExecError"
              },
              {
                "k": "text",
                "text": " (no\nTimeout-as-retry signal — intentional)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-runtime/src/agent.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "AgentLoopConfig"
              },
              {
                "k": "text",
                "text": " (no retry block —\nretry is per-task, not per-loop)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Workflow YAML schema (legacy) — "
              },
              {
                "k": "code",
                "text": "retry: { max_attempts, delay_ms, backoff }"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 (S1-C) — adds "
              },
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": " to "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " (L0)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- Retry everything (including tool calls)",
          "id": "alt-a----retry-everything-including-tool-calls"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Engine retries on any error.\nRejected: masks semantic errors. A tool returning \"file not found\" should\nnot be retried — the path is wrong, retrying makes the symptom worse\n(user waits, then sees the same error)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- Single global retry policy",
          "id": "alt-b----single-global-retry-policy"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "One "
            },
            {
              "k": "code",
              "text": "RetryConfig"
            },
            {
              "k": "text",
              "text": " for the whole engine.\nRejected: HTTP retries operate on millisecond-scale and benefit from\nsmall backoffs; provider 429 retries operate on second-scale (the\n"
            },
            {
              "k": "code",
              "text": "retry-after"
            },
            {
              "k": "text",
              "text": " is often 5-30s). One config cannot serve both."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- No retry in the engine, push to user",
          "id": "alt-c----no-retry-in-the-engine-push-to-user"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Every workflow author writes their own retry block.\nRejected: 429 handling is so common that boilerplate would dominate\nworkflow files. Sane defaults at the engine level reduce friction\nwithout taking away user override."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- `tower::retry::Retry` middleware",
          "id": "alt-d----towerretryretry-middleware"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Use the tower ecosystem.\nRejected for v0.81: "
            },
            {
              "k": "code",
              "text": "tower"
            },
            {
              "k": "text",
              "text": " is not in the dep tree before v0.100. The\nMEGA_ULTRATHINK rejections list (Batch B) defers tower adoption until\nthe runtime crate decides whether to standardize on it. We can revisit."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt E -- Retry as a kernel trait (`Retryable`)",
          "id": "alt-e----retry-as-a-kernel-trait-retryable"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Make every error type implement "
            },
            {
              "k": "code",
              "text": "is_retryable()"
            },
            {
              "k": "text",
              "text": ".\nRejected: "
            },
            {
              "k": "code",
              "text": "is_transient()"
            },
            {
              "k": "text",
              "text": " already exists on "
            },
            {
              "k": "code",
              "text": "ProviderError"
            },
            {
              "k": "text",
              "text": " — promoting\nit to a trait would force every error type (including "
            },
            {
              "k": "code",
              "text": "BlobError"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "MemoryError"
            },
            {
              "k": "text",
              "text": ") to implement it even when retry doesn't apply."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — layer stratification. ADR-019 is the retry-policy projection\nof ADR-006."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants. "
              },
              {
                "k": "code",
                "text": "ProviderError::RateLimited"
              },
              {
                "k": "text",
                "text": "\ncarries "
              },
              {
                "k": "code",
                "text": "retry_after_ms"
              },
              {
                "k": "text",
                "text": " as a future-proof hint; ADR-019 names the\nconsumer."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-018 — runtime + sync primitives. "
              },
              {
                "k": "code",
                "text": "tokio::time::sleep"
              },
              {
                "k": "text",
                "text": " and\n"
              },
              {
                "k": "code",
                "text": "tokio::time::timeout"
              },
              {
                "k": "text",
                "text": " are the implementation primitives."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 (S1-C) — adds "
              },
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": " to "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-033 + S1-C will land "
              },
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": " in\n"
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "L1 retry implementation lands when "
              },
              {
                "k": "code",
                "text": "nika-http"
              },
              {
                "k": "text",
                "text": " is admitted."
              }
            ],
            [
              {
                "k": "text",
                "text": "L2 retry implementation lands when verb-* crates are admitted."
              }
            ],
            [
              {
                "k": "text",
                "text": "Document the task-level "
              },
              {
                "k": "code",
                "text": "retry:"
              },
              {
                "k": "text",
                "text": " block schema in the workflow YAML\nreference (Phase E docs)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-020",
      "file": "adr-020-wasm-plugin-boundary.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 1192,
      "sha256": "44909fda6ee3f1475f009b776ee9b4f106fd3b73463f54cb748b05a2f785af78",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-020: WASM plugin boundary + Sandbox: trait stubs now, dual timeout later",
          "id": "adr-020-wasm-plugin-boundary--sandbox-trait-stubs-now-dual-timeout-later"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "forward-compat-invariants.md"
            },
            {
              "k": "text",
              "text": " Pattern 1 lists "
            },
            {
              "k": "code",
              "text": "WasmPluginHost"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Sandbox"
            },
            {
              "k": "text",
              "text": ",\nand "
            },
            {
              "k": "code",
              "text": "ObservabilitySink"
            },
            {
              "k": "text",
              "text": " as locked stub traits for v0.81. The intent: define\nthe trait shape now so future implementations slot in with zero breaking\nchange to the public API (Pattern 2: "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " + opt-in fields)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel before Wave 2 ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/src/lib.rs:48-60"
            },
            {
              "k": "text",
              "text": ") defined ~20\natomic traits across 13 modules (agent, blob, checkpoint, clock, context,\nerrors, fs, http, memory, process, provider, tool_executor, types). None of\nthem addressed plugin sandboxing."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "S1-B (Wave 2 commit "
            },
            {
              "k": "code",
              "text": "b68e58d4b"
            },
            {
              "k": "text",
              "text": ") added 4 new modules:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/cancel.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " (real impl)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/src/observability.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "ObservabilitySink"
              },
              {
                "k": "text",
                "text": " (stub)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/wasm.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "PluginFs"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "PluginHttp"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "WasmPluginError"
              },
              {
                "k": "text",
                "text": " (stubs)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/sandbox.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Capability"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "SandboxError"
              },
              {
                "k": "text",
                "text": " (stubs)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-014 sealing policy distinguishes \"sealed\" traits (community cannot\nimplement, must use the pck plugin protocol) from \"open\" traits (community\nmay implement alternative backends). The choice is per-trait."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/Cargo.toml:18-26"
            },
            {
              "k": "text",
              "text": ") has no WASM\ndeps — the stubs are trait definitions only, zero implementation code, zero\nnew transitive deps."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Shield error codes NIKA-380 (CapabilityDenied), NIKA-381 (TrustViolation),\nNIKA-384 (SpotlightRequired) already exist for runtime capability checks at\nthe workflow layer. The Sandbox trait gives those codes a kernel-level\nsubstrate to enforce on."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Define the WASM plugin and sandbox traits at v0.81 with zero\nimplementation. Pick implementation details when v0.100 is in scope."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "v0.81 (now, shipped in S1-B)",
          "id": "v081-now-shipped-in-s1-b"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": " (async, sealed = NO / "
              },
              {
                "k": "strong",
                "text": "open"
              },
              {
                "k": "text",
                "text": ") — community may ship\nalternative WASM hosts (wasmtime, wasmer, or future WASI preview3\nruntimes)."
              }
            ],
            [
              {
                "k": "code",
                "text": "PluginFs"
              },
              {
                "k": "text",
                "text": " (sync, "
              },
              {
                "k": "strong",
                "text": "open"
              },
              {
                "k": "text",
                "text": ") — sandboxed file access for guest code."
              }
            ],
            [
              {
                "k": "code",
                "text": "PluginHttp"
              },
              {
                "k": "text",
                "text": " (sync, "
              },
              {
                "k": "strong",
                "text": "open"
              },
              {
                "k": "text",
                "text": ") — sandboxed HTTP for guest code."
              }
            ],
            [
              {
                "k": "code",
                "text": "WasmPluginError"
              },
              {
                "k": "text",
                "text": " enum: "
              },
              {
                "k": "code",
                "text": "NotFound"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "ExecutionFailed"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "SandboxViolation"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "Timeout"
              },
              {
                "k": "text",
                "text": ". All variants "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": " trait (async, "
              },
              {
                "k": "strong",
                "text": "open"
              },
              {
                "k": "text",
                "text": ") — community may ship alternative sandbox\nbackends (Landlock on Linux, seatbelt on macOS, AppArmor, Bubblewrap)."
              }
            ],
            [
              {
                "k": "code",
                "text": "Capability"
              },
              {
                "k": "text",
                "text": " enum ("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": "): "
              },
              {
                "k": "code",
                "text": "FsRead"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "FsWrite"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Network"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "ProcessSpawn"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EnvRead"
              },
              {
                "k": "text",
                "text": ". "
              },
              {
                "k": "strong",
                "text": "Default-deny"
              },
              {
                "k": "text",
                "text": " allowlist model."
              }
            ],
            [
              {
                "k": "code",
                "text": "SandboxError"
              },
              {
                "k": "text",
                "text": " enum: "
              },
              {
                "k": "code",
                "text": "Unavailable"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "CapabilityDenied"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "SetupFailed"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Mocks: "
              },
              {
                "k": "code",
                "text": "NullWasmPluginHost"
              },
              {
                "k": "text",
                "text": " (always returns "
              },
              {
                "k": "code",
                "text": "NotFound"
              },
              {
                "k": "text",
                "text": "), "
              },
              {
                "k": "code",
                "text": "NullSandbox"
              },
              {
                "k": "text",
                "text": "\n(allow-all for tests)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "v0.100 (deferred)",
          "id": "v0100-deferred"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Implementation choice between wasmtime and wasmer"
              },
              {
                "k": "text",
                "text": " — benchmark when\nv0.100 is in scope. Both are viable; the trait shape is engine-agnostic."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Dual timeout"
              },
              {
                "k": "text",
                "text": ": wall-clock (kills the WASM instance) AND fuel-based\n(instruction budget, platform-agnostic). Both required because wall-clock\nalone misses runaway pure-compute, and fuel alone misses I/O-bound stalls."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Plugin payload format"
              },
              {
                "k": "text",
                "text": ": MessagePack or CBOR for compactness; decide at\nv0.100 with a benchmark on representative payloads."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Sandbox backends"
              },
              {
                "k": "text",
                "text": ":"
              },
              {
                "k": "text",
                "text": "- `nika-sandbox-landlock` (Linux 5.13+).\n- `nika-sandbox-seatbelt` (macOS).\n- `nika-sandbox-noop` (Windows / unsupported, hard-fails on any\n  capability check by default)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Sealing rationale (ADR-014 application)",
          "id": "sealing-rationale-adr-014-application"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Trait · Sealed? · Why"
              }
            ],
            [
              {
                "k": "text",
                "text": "`Provider`, `MemoryStore`, `EventSink`, `BillingSink` · **sealed** (per ADR-034) · Workspace controls implementations; community uses the pck protocol."
              }
            ],
            [
              {
                "k": "text",
                "text": "`WasmPluginHost`, `PluginFs`, `PluginHttp` · **open** · Multiple WASM runtimes are legitimate; we should not block alternative hosts."
              }
            ],
            [
              {
                "k": "text",
                "text": "`Sandbox` · **open** · Platform-specific backends; users should be free to add new OS backends."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Future v0.100 implementations are "
              },
              {
                "k": "strong",
                "text": "strictly additive"
              },
              {
                "k": "text",
                "text": " — no breaking\nchange to any consumer that imports the trait today."
              }
            ],
            [
              {
                "k": "text",
                "text": "Capability model is default-deny — security posture is conservative by\nconstruction."
              }
            ],
            [
              {
                "k": "text",
                "text": "Open traits invite community contributions (Bubblewrap, Firecracker\nmicro-VMs) without forking the kernel."
              }
            ],
            [
              {
                "k": "text",
                "text": "The mocks ("
              },
              {
                "k": "code",
                "text": "NullWasmPluginHost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "NullSandbox"
              },
              {
                "k": "text",
                "text": ") let downstream crates\nsatisfy trait bounds today even though no real impl exists."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Stub traits add ~250 LOC to the kernel that returns errors today. Cost\nis small; the alternative (adding traits at v0.100) is a breaking\nchange for every "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": " impl that wants to live alongside."
              }
            ],
            [
              {
                "k": "text",
                "text": "\"Open\" classification means we cannot later add a non-trivial method to\n"
              },
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": " without a major version bump, even at v0.x. Mitigated by\n"
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " on the trait's associated enums (Capability,\nSandboxError) and by adding new methods with "
              },
              {
                "k": "code",
                "text": "default"
              },
              {
                "k": "text",
                "text": " impls."
              }
            ],
            [
              {
                "k": "code",
                "text": "PluginFs"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "PluginHttp"
              },
              {
                "k": "text",
                "text": " are sync (per design decision: WASM guests\nsee synchronous calls), but real impls must internally bridge to async\nHTTP — cost of bridging happens once per call, accepted."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": " is orthogonal to WASM. It is also reusable for MCP server\nisolation when MCP lifecycle ships (Phase D Session 4)."
              }
            ],
            [
              {
                "k": "code",
                "text": "WasmPluginError::Timeout { timeout_ms }"
              },
              {
                "k": "text",
                "text": " reserves the wall-clock\ntimeout shape. The fuel-based timeout will surface as a separate\nvariant ("
              },
              {
                "k": "code",
                "text": "OutOfFuel"
              },
              {
                "k": "text",
                "text": ") when v0.100 ships — additive."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/wasm.rs:11-22"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": " trait."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/wasm.rs:24-39"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "PluginFs"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "PluginHttp"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/wasm.rs:50-83"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "WasmPluginError"
              },
              {
                "k": "text",
                "text": " enum."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/sandbox.rs:18-26"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": " trait."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/sandbox.rs:28-54"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "Capability"
              },
              {
                "k": "text",
                "text": " enum."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-plugin/src/sandbox.rs:78-99"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "SandboxError"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/plugin.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "NullWasmPluginHost"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/sandbox.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "NullSandbox"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/src/lib.rs:50,58,64-65"
              },
              {
                "k": "text",
                "text": " — module + re-exports."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/Cargo.toml:18-26"
              },
              {
                "k": "text",
                "text": " — dep list (no WASM crates)."
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — Pattern 1 (kernel\ntraits upfront)."
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/adr/adr-014-sealed-kernel-traits.md"
              },
              {
                "k": "text",
                "text": " — sealing policy."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- No WASM support; extend MCP only",
          "id": "alt-a----no-wasm-support-extend-mcp-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Lean on MCP servers for all extensibility.\nRejected: MCP requires a separate process per server, with stdio or\nTCP overhead per call. WASM allows in-process plugins with microsecond\nstartup and shared memory. Both will coexist at v0.100; WASM is\nstrictly faster for short-lived, high-frequency plugins."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- WASI-only, no custom traits",
          "id": "alt-b----wasi-only-no-custom-traits"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Adopt the WASI Preview2 component model directly.\nRejected: WASI Preview2 doesn't cover all Nika capabilities (e.g., LLM\ninference calls "
            },
            {
              "k": "em",
              "text": "from"
            },
            {
              "k": "text",
              "text": " plugins to providers). Custom traits give us\ncontrol of the host-side API; we can still target WASI Preview2/3 as\nthe guest ABI inside "
            },
            {
              "k": "code",
              "text": "nika-wasm-host"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- Define full WASM types now (memory layout, instance handles)",
          "id": "alt-c----define-full-wasm-types-now-memory-layout-instance-handles"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Land everything at v0.81.\nRejected: premature. The trait shape is enough to satisfy Pattern 1.\nMemory layout, instance lifecycle, and host-function bindings depend\non wasmtime/wasmer API evolution; defining them now risks rework when\nv0.100 arrives."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- Seal `WasmPluginHost` and `Sandbox`",
          "id": "alt-d----seal-wasmpluginhost-and-sandbox"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Apply the same sealing policy as "
            },
            {
              "k": "code",
              "text": "Provider"
            },
            {
              "k": "text",
              "text": ".\nRejected: unlike "
            },
            {
              "k": "code",
              "text": "Provider"
            },
            {
              "k": "text",
              "text": " (where workspace owns 25 implementations),\nWASM hosts and sandbox backends are platform-specific and benefit\nfrom community alternatives. Sealing them would block legitimate\nimplementations (Bubblewrap, Firecracker, AppArmor, future WASI runtimes)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt E -- Single timeout (wall-clock only) at v0.100",
          "id": "alt-e----single-timeout-wall-clock-only-at-v0100"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Skip fuel-based timeouts.\nRejected for design intent: pure-compute WASM (no I/O) can spin without\never yielding to the host. Wall-clock requires a separate watchdog\nthread; fuel-based is cooperative and platform-agnostic. Both are\nneeded; choosing one is a regression we should not bake in."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP trait design. ADR-020 adds traits to the kernel\nlist but does not change ADR-006's existing trait set."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants (Pattern 1: kernel traits upfront,\nPattern 2: "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " on every public enum/struct)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-014 — sealed kernel traits. ADR-020 explicitly classifies\n"
              },
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "PluginFs"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "PluginHttp"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": " as "
              },
              {
                "k": "strong",
                "text": "open"
              },
              {
                "k": "text",
                "text": "\n(not sealed)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-016 — Cancellation. WASM plugin calls should accept a\n"
              },
              {
                "k": "code",
                "text": "CancelCtx"
              },
              {
                "k": "text",
                "text": " once v0.95 ships, so a plugin call respects parent\ncancellation."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups (v0.100 territory):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Pick wasmtime vs wasmer with a benchmark on representative payloads\n(large LLM responses passed into plugin, plugin returning embeddings)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Implement dual-timeout (wall-clock + fuel) inside "
              },
              {
                "k": "code",
                "text": "nika-wasm-host"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Decide payload format: MessagePack (smaller) vs CBOR (more standardized)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Implement "
              },
              {
                "k": "code",
                "text": "nika-sandbox-landlock"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika-sandbox-seatbelt"
              },
              {
                "k": "text",
                "text": ", and a\n"
              },
              {
                "k": "code",
                "text": "nika-sandbox-noop"
              },
              {
                "k": "text",
                "text": " for unsupported platforms."
              }
            ],
            [
              {
                "k": "text",
                "text": "Add new variants to "
              },
              {
                "k": "code",
                "text": "Capability"
              },
              {
                "k": "text",
                "text": " enum as platform support widens\n(e.g., "
              },
              {
                "k": "code",
                "text": "Clock"
              },
              {
                "k": "text",
                "text": " for time access, "
              },
              {
                "k": "code",
                "text": "RandomBytes"
              },
              {
                "k": "text",
                "text": " for entropy)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-021",
      "file": "adr-021-yaml-envelope-convention.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        }
      ],
      "words": 585,
      "sha256": "d1af9861ce4eaac5c9c40e85324a778ba20632fc7627619b4803aecc9502785b",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-021: YAML envelope convention",
          "id": "adr-021-yaml-envelope-convention"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "⚠️ SUPERSEDED 2026-05-25"
            },
            {
              "k": "text",
              "text": " · The K8s envelope decided here\n("
            },
            {
              "k": "code",
              "text": "apiVersion: nika.sh/v1"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "kind"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "metadata"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "spec"
            },
            {
              "k": "text",
              "text": ") is "
            },
            {
              "k": "strong",
              "text": "superseded\nby the public `nika-spec`"
            },
            {
              "k": "text",
              "text": ", which adopts a "
            },
            {
              "k": "strong",
              "text": "single version marker"
            },
            {
              "k": "text",
              "text": " ·\n"
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "workflow: <id>"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "tasks:"
            },
            {
              "k": "text",
              "text": ". The spec explicitly rejects the\n"
            },
            {
              "k": "code",
              "text": "apiVersion:"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "schema:"
            },
            {
              "k": "text",
              "text": " two-field form as Kubernetes-ceremony a workflow\nfile does not need (cf "
            },
            {
              "k": "code",
              "text": "nika-spec spec/01-envelope.md"
            },
            {
              "k": "text",
              "text": " · \"Why one field,\nnot apiVersion + schema\"). The engine's internal canonical URI stays\n"
            },
            {
              "k": "code",
              "text": "https://nika.sh/spec/v1"
            },
            {
              "k": "text",
              "text": " for RDF/conformance tooling, but the author\nnever types it. Decision below preserved as audit trail per\n"
            },
            {
              "k": "code",
              "text": "cross-source-validation.md"
            },
            {
              "k": "text",
              "text": " §2.7. The "
            },
            {
              "k": "code",
              "text": "kind"
            },
            {
              "k": "text",
              "text": " discriminator + multi-doc\nrationale (9 doc kinds) is the part that survives — only the version-field\nshape changed ("
            },
            {
              "k": "code",
              "text": "apiVersion: nika.sh/v1"
            },
            {
              "k": "text",
              "text": " → "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": ")."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Phase D Round 2c-2e shipped a parser for top-level workflow scalars ("
            },
            {
              "k": "code",
              "text": "name"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "description"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "goal"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "provider"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "model"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "schema"
            },
            {
              "k": "text",
              "text": ") and "
            },
            {
              "k": "code",
              "text": "tasks:"
            },
            {
              "k": "text",
              "text": " sequence.\nBackground research (4 web-research agents, 2026-04-16) converged unanimously\non adopting the K8s envelope pattern ("
            },
            {
              "k": "code",
              "text": "apiVersion + kind + metadata + spec"
            },
            {
              "k": "text",
              "text": ")\nwith multi-document YAML support. Evidence: every workflow DSL with >2 doc\ntypes (Argo, Tekton, Flux, Drone-1.x, Helm) ended up with this envelope after\nflat-form regret migrations."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika has 9 user-facing doc kinds at v0.90: Workflow, Skill, Agent, Provider,\nMCP, Eval, Recipe, Shield, Lints (+ Memory v0.95, Plugin v0.100 reserved).\nWithout an envelope, every kind reimplements identity + version semantics."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Adopt the K8s envelope as the canonical shape for every Nika user-facing\nstructured config (YAML and TOML)"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "code",
          "lang": "yaml",
          "text": "apiVersion: nika.sh/v1\nkind: Workflow                # closed enum: Workflow|Skill|Agent|Provider|\n                              #              Mcp|Eval|Recipe|Shield|Lints|\n                              #              Memory(reserved)|Plugin(reserved)\nmetadata:\n  name: my-workflow\n  description: ...\n  labels: { ... }             # optional\n  annotations: { ... }        # optional\nspec:\n  defaults: { model: openai/gpt-4o }\n  tasks: [...]                # for kind: Workflow"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Multi-document YAML"
            },
            {
              "k": "text",
              "text": " (--- separator) supported: one file may declare\nmultiple kinds (Workflow + Skill + Agent), parsed into "
            },
            {
              "k": "code",
              "text": "Vec<RawDocument>"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Model identifier"
            },
            {
              "k": "text",
              "text": ": single string "
            },
            {
              "k": "code",
              "text": "provider/model"
            },
            {
              "k": "text",
              "text": " (LiteLLM format), e.g.\n"
            },
            {
              "k": "code",
              "text": "openai/gpt-4o"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "anthropic/claude-sonnet-4.5"
            },
            {
              "k": "text",
              "text": ". Drop separate "
            },
            {
              "k": "code",
              "text": "provider:"
            },
            {
              "k": "text",
              "text": " +\n"
            },
            {
              "k": "code",
              "text": "model:"
            },
            {
              "k": "text",
              "text": " fields."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Drop workflow-level `goal:`"
            },
            {
              "k": "text",
              "text": " — CrewAI agent-specific concept, not a\nworkflow concept (0 of 8 surveyed workflow DSLs have it). If goal-driven\nagentic workflows need it, place in "
            },
            {
              "k": "code",
              "text": "spec.orchestrate.goal:"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Format support"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "YAML: workflows (DSL, user-authored)"
              }
            ],
            [
              {
                "k": "text",
                "text": "TOML: catalog data, agent presets, lint rules (config-as-data)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Markdown + YAML frontmatter: skills/agents (prose-heavy, Cursor/Claude\nconvention)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "All three formats use the same envelope keys (apiVersion + kind + metadata +\nspec)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Single envelope shape across 9 doc kinds = LLM-friendly (Nika-brain\ngenerates correct shape on first try)"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Forward-compat: adding a kind (e.g., Memory at v0.95) requires only\nadding a variant to the closed "
              },
              {
                "k": "code",
                "text": "Kind"
              },
              {
                "k": "text",
                "text": " enum"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ K8s envelope is in 100% of LLM training data — Nika-brain pattern-matches\nwithout prompting tax"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Diverges from typical \"Rust workspace\" convention (TOML flat) — but\nworkflow engines diverge from compilers (the relevant comparison set)"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Existing nika-schema parser (Round 2c-2e, 38 tests) needs full rewrite\nin Phase D — explicit cost accepted (v0.1, 0 users, break freely)"
              }
            ],
            [
              {
                "k": "text",
                "text": "⚠️ Hygiene vector 23 (check-workflow-envelope.sh) MUST land in Phase B\nbefore any envelope code is written — guards against regression"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Flat top-level"
              },
              {
                "k": "text",
                "text": " (current Round 2c shape) — dies at ~8 fields per\nGitLab/Drone/Dify post-mortems"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Hybrid (Dify-style: version + kind + nested app block)"
              },
              {
                "k": "text",
                "text": " — half-step that\nloses metadata uniformity"
              }
            ],
            [
              {
                "k": "strong",
                "text": "URL-path discrimination (apiVersion: nika.sh/v1/Workflow)"
              },
              {
                "k": "text",
                "text": " — breaks YAML\neditor schema hints"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Filename convention only"
              },
              {
                "k": "text",
                "text": " — moves truth out of doc, hurts multi-doc"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "See "
            },
            {
              "k": "code",
              "text": "~/.claude/.../memory/project_foundation_v081_constellation.md"
            },
            {
              "k": "text",
              "text": " for the\n4-agent research synthesis."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-022",
      "file": "adr-022-foundation-crate-layout-v081.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Reference",
          "anchor": "reference"
        }
      ],
      "words": 495,
      "sha256": "41aaede48c2379f253ced2f3ad5a9c8ac6b784130cd7e9f010ef9eaf9ed70b55",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-022: Foundation crate layout v0.81",
          "id": "adr-022-foundation-crate-layout-v081"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "After multi-agent architectural review (rust-architect + web-researcher SOTA\nworkspaces + rust-pro + Explore file audit, 2026-04-16), the v0.80 foundation\nlayout (10 crate plan, 7 in workspace) is replaced by a 14-crate layout with\nstrict tier discipline, "
            },
            {
              "k": "code",
              "text": "publish = false"
            },
            {
              "k": "text",
              "text": " strategy, and 5 structural changes."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "The 14-crate constellation",
          "id": "the-14-crate-constellation"
        },
        {
          "k": "code",
          "lang": "text",
          "text": "L0-tier-0 (LEAVES — zero nika-* deps):\n  nika-core              [RENAME from nika-types]\n  nika-macros            [NEW proc-macro stub]\n\nL0-tier-1 (depend on tier-0):\n  nika-error             [DROP nika-types re-export facade]\n  nika-envelope          [NEW — apiVersion+Kind+Metadata+multi-doc+PckSpec]\n\nL0-tier-2 (depend on tier-0+1):\n  nika-event             [22 scoped sub-enums]\n  nika-binding-transform [NEW — 65 pure transforms extracted]\n  nika-catalog-codegen   [extract from catalog/build.rs]\n  nika-schema-ast        [NEW — Raw + Analyzed AST types]\n\nL0-tier-3:\n  nika-catalog           [internal split data/models.rs + build/]\n  nika-binding           [template engine + dispatch]\n  nika-schema            [parser + analyzer + DAG + taint + jsonsubset]\n\nL0.5:\n  nika-kernel            [40 traits, NO split forever per ADR-006-amend]\n  nika-kernel-mock\n\nL4:\n  nika-catalog-verify    [RELABEL from L0]"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`publish = false` for ALL foundation crates",
          "id": "publish--false-for-all-foundation-crates"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Every foundation crate (13 internal + 1 L4) carries "
            },
            {
              "k": "code",
              "text": "publish = false"
            },
            {
              "k": "text",
              "text": ". Only\n"
            },
            {
              "k": "code",
              "text": "nika"
            },
            {
              "k": "text",
              "text": " (binary) and future "
            },
            {
              "k": "code",
              "text": "nika-sdk"
            },
            {
              "k": "text",
              "text": " ever ship to crates.io."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "5 structural changes",
          "id": "5-structural-changes"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Rename `nika-types` → `nika-core`"
              },
              {
                "k": "text",
                "text": " (matches helix/bevy/ra/polkadot\nconvention; "
              },
              {
                "k": "code",
                "text": "nika-core@0.74.0"
              },
              {
                "k": "text",
                "text": " legacy on crates.io gets yanked)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Extract `nika-envelope`"
              },
              {
                "k": "text",
                "text": " (single highest-ROI move — shared by 9 doc kinds)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Drop `nika-error → nika_types::*` facade"
              },
              {
                "k": "text",
                "text": " (re-export pollution gone with\n0 users)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Split `nika-schema` into 2"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "nika-schema-ast"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "nika-schema"
              },
              {
                "k": "text",
                "text": ") — circular-\ndep concern from original spec was wrong (analyzer reads raw, writes analyzed)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Split `nika-binding` into 2"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "nika-binding-transform"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "nika-binding"
              },
              {
                "k": "text",
                "text": ") —\n65 pure transforms have known 2nd consumer (verb-exec basename)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`nika-pck-manifest` MERGES into `nika-envelope`",
          "id": "nika-pck-manifest-merges-into-nika-envelope"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A pck manifest IS an envelope document ("
            },
            {
              "k": "code",
              "text": "Envelope<PckSpec>"
            },
            {
              "k": "text",
              "text": "). Eliminates 1\ncrate, conceptually cleaner."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`nika-catalog-verify` RELABEL L0 → L4",
          "id": "nika-catalog-verify-relabel-l0--l4"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Already does HTTP. Wrong layer assignment. Free fix."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`nika-macros` proc-macro stub now (empty)",
          "id": "nika-macros-proc-macro-stub-now-empty"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Agent 2 SOTA finding: every surveyed Rust workspace (rust-analyzer, biome,\nruff, uv, bevy, oxc) has a companion "
            },
            {
              "k": "code",
              "text": "*_macros"
            },
            {
              "k": "text",
              "text": " crate. Adding it later requires\nL0 shuffle; adding empty now is free insurance."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ 14 crates with crystal-clear single-responsibility"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Tier discipline enables "
              },
              {
                "k": "code",
                "text": "cargo public-api"
              },
              {
                "k": "text",
                "text": " snapshots without churn"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "publish = false"
              },
              {
                "k": "text",
                "text": " frees rename/refactor velocity forever"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Renames legitimate (no users)"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ ~7 commits of refactor work in Phase D (nuke + redo)"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Existing nika-types imports across all admitted crates need update\n(mechanical — find/replace + cargo check)"
              }
            ],
            [
              {
                "k": "text",
                "text": "⚠️ The legacy "
              },
              {
                "k": "code",
                "text": "crates/nika-core/"
              },
              {
                "k": "text",
                "text": " excluded directory (v0.79 main-leftover)\nmust be deleted before the rename — handled in Phase D.1"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Keep nika-types"
              },
              {
                "k": "text",
                "text": " (no rename) — name is weak placeholder, every other\nimprovement depends on knowing canonical name"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Split nika-schema into 4"
              },
              {
                "k": "text",
                "text": " (types + parser + analyzer + jsonsubset) —\nover-fragmented, 2 is the right number per cohesion analysis"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Polkadot-style 14 micro-crates for nika-core"
              },
              {
                "k": "text",
                "text": " (nika-id, nika-cost, ...) —\nQ2 anti-pattern, no consumer-side benefit"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Publish all foundation crates"
              },
              {
                "k": "text",
                "text": " — 0 users, no value, locks API surface\nprematurely"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Reference",
          "id": "reference"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "~/.claude/.../memory/project_foundation_v081_constellation.md"
              }
            ],
            [
              {
                "k": "code",
                "text": "~/.claude/.../memory/feedback_publish_false_foundation_strategy.md"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-023",
      "file": "adr-023-file-modularity-discipline.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Implementation",
          "anchor": "implementation"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        }
      ],
      "words": 436,
      "sha256": "d00e8dbee61b5390e5a0b7727d61fc2d0b2d88b7e72716d3961b0f8b04805608",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-023: File modularity discipline",
          "id": "adr-023-file-modularity-discipline"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Current rule: max 1500 LOC per file (hard cap). 4-agent audit (2026-04-16)\nrevealed:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Current P90 ≈ 600 LOC (well under cap)"
              }
            ],
            [
              {
                "k": "text",
                "text": "nika-catalog/build.rs at 1386 LOC and data/models.rs at 1248 LOC are\nuncomfortably close"
              }
            ],
            [
              {
                "k": "text",
                "text": "rust-analyzer P90 = 900 LOC, helix P90 = 700 LOC, oxc P90 = 500 LOC"
              }
            ],
            [
              {
                "k": "text",
                "text": "Mature workspaces routinely ship 2k+ LOC files (codegen output, generated\nlookup tables)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Two opposing views:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Agent 1 (rust-architect): tighten to 800/1200 — match SOTA Rust P90"
              }
            ],
            [
              {
                "k": "text",
                "text": "Agent 2 (web-researcher SOTA): NO workspace enforces hard limits — relax to\n1500/3000"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Both have merit. Cap discipline matters; codegen reality matters."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Three-tier file LOC policy:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Threshold · Action · Mode"
              }
            ],
            [
              {
                "k": "text",
                "text": "**800 LOC** · warning · hygiene vector 27 emits YELLOW"
              }
            ],
            [
              {
                "k": "text",
                "text": "**1500 LOC** · hard fail · hygiene vector 27 fails RED — cannot commit"
              }
            ],
            [
              {
                "k": "text",
                "text": "**3000 LOC** · absolute hard fail with allow-list exemption only · hygiene vector 27 fails CRITICAL — exempt ONLY via per-file marker"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "`// LOC-EXEMPT: <reason> [<owner>]` marker"
            },
            {
              "k": "text",
              "text": " at top of file allows bypassing\nthe 1500 cap up to 3000 LOC. Reasons must be one of:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "codegen"
              },
              {
                "k": "text",
                "text": " — auto-generated by build.rs / proc-macro"
              }
            ],
            [
              {
                "k": "code",
                "text": "lookup-table"
              },
              {
                "k": "text",
                "text": " — large compile-time static data table"
              }
            ],
            [
              {
                "k": "code",
                "text": "enum-mega"
              },
              {
                "k": "text",
                "text": " — single mega-enum (e.g., 200+ variants of EventKind aggregator)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Per-function cap"
            },
            {
              "k": "text",
              "text": ": 100 LOC soft warn ("
            },
            {
              "k": "code",
              "text": "clippy::too_many_lines = \"warn\""
            },
            {
              "k": "text",
              "text": ").\nCurrently unenforced — enable in workspace lints."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ 800 warn discipline keeps cohesion tight (we're already there at P90 ~600)"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ 1500 fail catches genuine sprawl"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ 3000-with-exemption preserves codegen reality"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Marker enforces conscious decision (not silent growth)"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ +1 hygiene vector (27) — implementation cost ~1h"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ One-time refactor of catalog/build.rs (1386) + data/models.rs (1248) to\nhonor 800 warn — handled in Phase B'"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Implementation",
          "id": "implementation"
        },
        {
          "k": "code",
          "lang": "bash",
          "text": "# scripts/hygiene/check-file-loc.sh (vector 27)\n#  - For each .rs file in crates/*/src/ and crates/*/build.rs:\n#    - count LOC\n#    - if > 3000:\n#        check for // LOC-EXEMPT: marker; fail if absent\n#    - elif > 1500:\n#        fail (no exemption above 1500 except via 3000 path)\n#    - elif > 800:\n#        warn (yellow)"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Workspace "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " lint section:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "toml",
          "text": "[workspace.lints.clippy]\ntoo_many_lines = { level = \"warn\", priority = -1 }"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Relax to 1500/3000"
              },
              {
                "k": "text",
                "text": " (Agent 2's pure recommendation) — loses discipline"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Tighten to 800/1200"
              },
              {
                "k": "text",
                "text": " (Agent 1's pure recommendation) — fails under codegen"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Per-crate cap"
              },
              {
                "k": "text",
                "text": " instead of per-file — moves the problem one level up"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Hard 1500 with no exemptions"
              },
              {
                "k": "text",
                "text": " — current rule, breaks under generated code"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-024",
      "file": "adr-024-sota-rust-patterns.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Migration order",
          "anchor": "migration-order"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        }
      ],
      "words": 453,
      "sha256": "92877af4ab5f182557f8d056cf03e213e1c3ea8b44dcafdf247358ada59525ee",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-024: Adopt SOTA Rust patterns",
          "id": "adr-024-adopt-sota-rust-patterns"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Multi-agent audit (rust-architect + rust-pro + web-researcher) identified six\n2025-2026 SOTA Rust patterns that Nika can adopt cheaply at v0.1 with high\nforward-compat ROI."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "1. `bon::Builder` derive as DEFAULT for ≥4-arg structs",
          "id": "1-bonbuilder-derive-as-default-for-4-arg-structs"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Adopters: rust-analyzer, axum, tokio (evaluating). Type-state safety,\n"
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": "-compatible, eliminates "
            },
            {
              "k": "code",
              "text": "..Default::default()"
            },
            {
              "k": "text",
              "text": " tax."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Apply to top 5 first: "
            },
            {
              "k": "code",
              "text": "InferRequest"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "AgentLoopConfig"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ModelCapabilities"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "HttpRequest"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ScheduleConfig"
            },
            {
              "k": "text",
              "text": ". Inv #19 amended: "
            },
            {
              "k": "code",
              "text": "new()"
            },
            {
              "k": "text",
              "text": " for ≤3-arg structs,\n"
            },
            {
              "k": "code",
              "text": "bon::Builder"
            },
            {
              "k": "text",
              "text": " for ≥4-arg."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "2. `Arc<str>` for shared catalog identifiers",
          "id": "2-arcstr-for-shared-catalog-identifiers"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "ModelId"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ProviderId"
            },
            {
              "k": "text",
              "text": ", capability rule keys: migrate from "
            },
            {
              "k": "code",
              "text": "String"
            },
            {
              "k": "text",
              "text": " to\n"
            },
            {
              "k": "code",
              "text": "Arc<str>"
            },
            {
              "k": "text",
              "text": ". Half the size, O(1) clone (refcount). Static catalog literals stay\nas "
            },
            {
              "k": "code",
              "text": "&'static str"
            },
            {
              "k": "text",
              "text": " (phf keys unchanged)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "3. `camino::Utf8PathBuf` for ALL paths in nika-core",
          "id": "3-caminoutf8pathbuf-for-all-paths-in-nika-core"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Kills the "
            },
            {
              "k": "code",
              "text": "OsStr → str"
            },
            {
              "k": "text",
              "text": " unwrap question forever. Matches Cargo, jj, oxc, ruff,\nuv convention."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "4. `mod sealed { pub trait Sealed {} }` as the ONLY sealed pattern",
          "id": "4-mod-sealed--pub-trait-sealed---as-the-only-sealed-pattern"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Agent 2 SOTA: line-for-line identical pattern across rust-analyzer, biome,\nruff, bevy, tokio, oxc. Drop the proposed "
            },
            {
              "k": "code",
              "text": "sealed"
            },
            {
              "k": "text",
              "text": " proc-macro crate (3-project\ntrend, not convergence)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "5. `strum::EnumString + IntoStaticStr` for enum↔string bridges",
          "id": "5-strumenumstring--intostaticstr-for-enumstring-bridges"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Specifically for "
            },
            {
              "k": "code",
              "text": "EventKind"
            },
            {
              "k": "text",
              "text": " aggregation in nika-event (22 sub-enums). Used by\noxc (200+ AstKind variants), bevy (Reflect), sqlx (error codes). Q1 still\nholds (no proc macros in "
            },
            {
              "k": "em",
              "text": "L0 impl"
            },
            {
              "k": "text",
              "text": "); strum is a dev-dep proc macro, not Nika-\nowned L0 code."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "6. `cargo public-api` baselines NOW for L0 + L0.5",
          "id": "6-cargo-public-api-baselines-now-for-l0--l05"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Snapshot every public symbol of every L0/L0.5 crate into\n"
            },
            {
              "k": "code",
              "text": "docs/api-baselines/<crate>.txt"
            },
            {
              "k": "text",
              "text": ". Pre-PR hook diffs against baseline. Mechanizes\nthe "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " ratchet (vector 18 made enforceable)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Type-state builder safety (#1) prevents misconstructed configs at compile\ntime"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "Arc<str>"
              },
              {
                "k": "text",
                "text": " (#2) — half the alloc per catalog lookup"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "camino"
              },
              {
                "k": "text",
                "text": " (#3) — kills a class of unwrap bugs"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Plain "
              },
              {
                "k": "code",
                "text": "mod sealed"
              },
              {
                "k": "text",
                "text": " (#4) — minimal complexity, industry standard"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "strum"
              },
              {
                "k": "text",
                "text": " (#5) — eliminates handwritten "
              },
              {
                "k": "code",
                "text": "as_str()"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "parse()"
              },
              {
                "k": "text",
                "text": " boilerplate"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "public-api"
              },
              {
                "k": "text",
                "text": " (#6) — automated #[non_exhaustive] ratchet"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ +4 workspace deps: "
              },
              {
                "k": "code",
                "text": "bon"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "camino"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "strum"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "cargo-public-api"
              },
              {
                "k": "text",
                "text": " (cargo\nsubcommand, not a runtime dep)"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ ~8 commits of mechanical migration work across Phase H"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Migration order",
          "id": "migration-order"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "code",
                "text": "cargo public-api"
              },
              {
                "k": "text",
                "text": " setup + baselines (no code changes — PR-blocking\ninfrastructure)"
              }
            ],
            [
              {
                "k": "code",
                "text": "mod sealed"
              },
              {
                "k": "text",
                "text": " standardization (already mostly there)"
              }
            ],
            [
              {
                "k": "code",
                "text": "camino::Utf8PathBuf"
              },
              {
                "k": "text",
                "text": " migration (mechanical find/replace)"
              }
            ],
            [
              {
                "k": "code",
                "text": "Arc<str>"
              },
              {
                "k": "text",
                "text": " for ModelId/ProviderId (touches catalog; coordinated with\nADR-022 split)"
              }
            ],
            [
              {
                "k": "code",
                "text": "strum"
              },
              {
                "k": "text",
                "text": " for EventKind in nika-event (during nika-event build)"
              }
            ],
            [
              {
                "k": "code",
                "text": "bon::Builder"
              },
              {
                "k": "text",
                "text": " for top 5 config structs (last — depends on stable APIs)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "`derive_builder`"
              },
              {
                "k": "text",
                "text": " instead of "
              },
              {
                "k": "code",
                "text": "bon"
              },
              {
                "k": "text",
                "text": " — bon's #[non_exhaustive] support is the\ntipping point"
              }
            ],
            [
              {
                "k": "strong",
                "text": "`SmolStr` / `CompactString`"
              },
              {
                "k": "text",
                "text": " instead of "
              },
              {
                "k": "code",
                "text": "Arc<str>"
              },
              {
                "k": "text",
                "text": " — defer until cargo-bench\njustifies (small-string optimization is hot-path-specific)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "`std::path::PathBuf`"
              },
              {
                "k": "text",
                "text": " instead of "
              },
              {
                "k": "code",
                "text": "camino::Utf8PathBuf"
              },
              {
                "k": "text",
                "text": " — keeps OsStr→str\nunwrap question"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Custom `as_str()` on each enum"
              },
              {
                "k": "text",
                "text": " instead of "
              },
              {
                "k": "code",
                "text": "strum"
              },
              {
                "k": "text",
                "text": " — boilerplate at scale"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-025",
      "file": "adr-025-per-crate-semver-release-plz.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Crates.io legacy cleanup (handled in Phase B.0)",
          "anchor": "cratesio-legacy-cleanup-handled-in-phase-b0"
        },
        {
          "title": "Reference",
          "anchor": "reference"
        }
      ],
      "words": 263,
      "sha256": "07b5310f7340a3d77b70e6d9fa02aed52845e6e13d8cf448d3c0c69848e525c8",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-025: Per-crate semver + release-plz",
          "id": "adr-025-per-crate-semver--release-plz"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Workspace currently uses single "
            },
            {
              "k": "code",
              "text": "version = \"0.80.0\""
            },
            {
              "k": "text",
              "text": " shared across all crates.\nWith the foundation "
            },
            {
              "k": "code",
              "text": "publish = false"
            },
            {
              "k": "text",
              "text": " strategy (ADR-022), only "
            },
            {
              "k": "code",
              "text": "nika"
            },
            {
              "k": "text",
              "text": " (binary)\nand future "
            },
            {
              "k": "code",
              "text": "nika-sdk"
            },
            {
              "k": "text",
              "text": " reach crates.io. Their semver should evolve independently\nof internal foundation crate refactors."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Two-tier semver policy:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Foundation crates (`publish = false`)"
              },
              {
                "k": "text",
                "text": " — keep workspace-shared version\n"
              },
              {
                "k": "code",
                "text": "version.workspace = true"
              },
              {
                "k": "text",
                "text": ". Not published, semver irrelevant for ecosystem."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Publishable crates (`nika`, future `nika-sdk`)"
              },
              {
                "k": "text",
                "text": " — independent semver via\n"
              },
              {
                "k": "code",
                "text": "release-plz"
              },
              {
                "k": "text",
                "text": " GitHub workflow. Each crate has its own version field, bumped\nsemantically based on conventional-commit messages affecting that crate."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Initial publishable versions:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika@0.80.0"
              },
              {
                "k": "text",
                "text": " — binary republish from legacy v0.47.1 (yank old, fresh start\nwith diamond architecture once binary is wired)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-sdk@0.1.0"
              },
              {
                "k": "text",
                "text": " — first publication when v0.90 reaches stable surface"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "release-plz"
            },
            {
              "k": "text",
              "text": " workflow runs on push-to-main, opens a PR with version bumps"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "CHANGELOG entries derived from commits, auto-publishes on PR merge."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "nika"
              },
              {
                "k": "text",
                "text": " binary version reflects user-facing API stability, not internal\nrefactor churn"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "nika-sdk"
              },
              {
                "k": "text",
                "text": " ships independently — bug-fix release doesn't bump main binary"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ release-plz handles changelog generation + GitHub releases automatically"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ One-time workflow setup (~2h, one commit)"
              }
            ],
            [
              {
                "k": "text",
                "text": "⚠️ Conventional commit discipline becomes load-bearing (already mostly true\nvia commitlint hook)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Crates.io legacy cleanup (handled in Phase B.0)",
          "id": "cratesio-legacy-cleanup-handled-in-phase-b0"
        },
        {
          "k": "code",
          "lang": "text",
          "text": "cargo yank nika-core@0.74.0      # diamond renames it; legacy abandoned\ncargo yank nika-event@0.74.0     # legacy\ncargo yank nika-engine@0.47.1    # legacy, rebranded as nika\n# nika@0.47.1 stays; republish at v0.80+ when binary is wired"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Reference",
          "id": "reference"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "release-plz docs: "
              },
              {
                "k": "link",
                "text": "https://release-plz.ieni.dev/",
                "href": "https://release-plz.ieni.dev/"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-022 (publish=false strategy)"
              }
            ],
            [
              {
                "k": "code",
                "text": "~/.claude/.../memory/feedback_publish_false_foundation_strategy.md"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-026",
      "file": "adr-026-workspace-toml-ssot.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Reference",
          "anchor": "reference"
        }
      ],
      "words": 381,
      "sha256": "bdaaa1e69d3a1486c32a64bc0216e53c0059fde7d23793410c1af5882ca1938e",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-026: workspace.toml SSOT + auto-generated crate.md",
          "id": "adr-026-workspacetoml-ssot--auto-generated-cratemd"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Phase A baseline reset (commit cd9602ca0) revealed that workspace metadata is\nscattered across 5+ files:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "Cargo.toml [workspace.metadata.diamond.layers]"
              },
              {
                "k": "text",
                "text": " (per-crate layer)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " (FCI references)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/crate-layer-registry.md"
              },
              {
                "k": "text",
                "text": " (layer rules)"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/refresh-status.sh"
              },
              {
                "k": "text",
                "text": " (status block generation)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Per-crate "
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " (LOC budget, gates, owner)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Per-crate "
              },
              {
                "k": "code",
                "text": "README.md"
              },
              {
                "k": "text",
                "text": " (when present, often missing)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Each manual edit drifts independently. Phase A added "
            },
            {
              "k": "code",
              "text": "refresh-status.sh"
            },
            {
              "k": "text",
              "text": " as\na partial fix. Need a deeper consolidation."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`docs/workspace.toml` — single source of truth",
          "id": "docsworkspacetoml--single-source-of-truth"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "One file declaring per-crate metadata machine-readable:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "toml",
          "text": "[crates.nika-core]\nlayer = \"L0\"\ntier = 0\nleaf = true\npublish = false\ngates_passed = [\"spec\", \"tdd\", \"impl\", \"clippy\", \"mutation\", \"property\",\n                \"benchmarks\", \"docs\", \"canary\", \"parity\", \"review\", \"atomic\"]\nloc_budget = 5000\nfile_loc_warn = 800\nfile_loc_fail = 1500\npublic_api_baseline = \"docs/api-baselines/nika-core.txt\"\nsibling_deps = []\nowners = [\"diamond-team\"]\n\n[crates.nika-error]\nlayer = \"L0\"\ntier = 1\nleaf = false\npublish = false\ngates_passed = [...]\nsibling_deps = [\"nika-core\"]\n# ..."
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Auto-generated `crates/<name>/README.md`",
          "id": "auto-generated-cratesnamereadmemd"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Hygiene vector reads "
            },
            {
              "k": "code",
              "text": "docs/workspace.toml"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " description +\n"
            },
            {
              "k": "code",
              "text": "lib.rs"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "//!"
            },
            {
              "k": "text",
              "text": " docs and emits "
            },
            {
              "k": "code",
              "text": "crates/<name>/README.md"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "code",
          "lang": "markdown",
          "text": "# nika-core\n\n**Layer**: L0 (tier-0 leaf)  •  **Publish**: false  •  **LOC budget**: 5000\n**Owners**: diamond-team\n**Sibling deps**: (none — leaf crate)\n\n## Purpose\n\nFoundation value types — IDs, Cost, Trust, Baggage, Resource, Hash,\nRetryConfig, TokenUsage, etc.\n\n## Gates passed (12/12)\n\n- ✅ spec ✅ TDD ✅ impl ✅ clippy ✅ mutation ✅ property\n- ✅ benchmarks ✅ docs ✅ canary ✅ parity ✅ review ✅ atomic\n\n## API baseline\n\n`docs/api-baselines/nika-core.txt`\n\n## Reference\n\n- Spec: `docs/crate-specs/nika-core.md`\n- Layer registry: `docs/architecture/crate-layer-registry.md`"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Hygiene vector 33 — workspace.toml ↔ Cargo.toml ↔ README sync",
          "id": "hygiene-vector-33--workspacetoml--cargotoml--readme-sync"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Reads "
            },
            {
              "k": "code",
              "text": "docs/workspace.toml"
            },
            {
              "k": "text",
              "text": ", asserts:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Every workspace member has an entry"
              }
            ],
            [
              {
                "k": "text",
                "text": "Every "
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "version"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "publish"
              },
              {
                "k": "text",
                "text": " matches"
              }
            ],
            [
              {
                "k": "text",
                "text": "Every "
              },
              {
                "k": "code",
                "text": "README.md"
              },
              {
                "k": "text",
                "text": " matches the auto-gen template"
              }
            ],
            [
              {
                "k": "code",
                "text": "[workspace.metadata.diamond.layers]"
              },
              {
                "k": "text",
                "text": " matches "
              },
              {
                "k": "code",
                "text": "workspace.toml.crates.*.layer"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/refresh-status.sh"
              },
              {
                "k": "text",
                "text": " output matches "
              },
              {
                "k": "code",
                "text": "workspace.toml"
              },
              {
                "k": "text",
                "text": "-derived counts"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Drift eliminated by mechanical enforcement"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Adding/renaming a crate = ONE edit (workspace.toml), README + status block"
              },
              {
                "k": "text",
                "text": "+ Cargo.toml metadata regenerated"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ ADR-027 (cargo timings) reads from same SSOT"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ +1 hygiene vector (33), +1 generator script"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Initial migration: write "
              },
              {
                "k": "code",
                "text": "docs/workspace.toml"
              },
              {
                "k": "text",
                "text": " for all 7 admitted + 7\nplanned crates (~2h, mechanical)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Reference",
          "id": "reference"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-022 (foundation crate layout)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-023 (file modularity — values populated from workspace.toml)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-027 (cargo timings — budgets from workspace.toml)"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-027",
      "file": "adr-027-strict-l0-tiers-cargo-timings.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Vector summary (Phase B + E adds)",
          "anchor": "vector-summary-phase-b--e-adds"
        },
        {
          "title": "Reference",
          "anchor": "reference"
        }
      ],
      "words": 513,
      "sha256": "d1d623b1e426c793352bbc4212173da151f0e4b0820617eff18dd051bf1c36ee",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-027: Strict L0 sublayer tiers + cargo timings budget",
          "id": "adr-027-strict-l0-sublayer-tiers--cargo-timings-budget"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-022 lays out 14 foundation crates. Without internal discipline, L0 risks\nbecoming a flat bag of crates with arbitrary cross-dependencies. We want the\nstrongest possible compile DAG."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Internal L0 tiers (sublayer discipline)",
          "id": "internal-l0-tiers-sublayer-discipline"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "L0 has 4 internal tiers, strict downward-only:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Tier · Constraint · Crates"
              }
            ],
            [
              {
                "k": "text",
                "text": "**L0-tier-0** · LEAF — zero `nika-*` deps · `nika-core`, `nika-macros`"
              }
            ],
            [
              {
                "k": "text",
                "text": "**L0-tier-1** · depends on tier-0 only · `nika-error`, `nika-envelope`"
              }
            ],
            [
              {
                "k": "text",
                "text": "**L0-tier-2** · depends on tier-0+1, single domain · `nika-event`, `nika-binding-transform`, `nika-catalog-codegen`, `nika-schema-ast`"
              }
            ],
            [
              {
                "k": "text",
                "text": "**L0-tier-3** · depends on lower L0 tiers · `nika-catalog`, `nika-binding`, `nika-schema`"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Hard rule: max 3 sibling L0 deps per L0 crate",
          "id": "hard-rule-max-3-sibling-l0-deps-per-l0-crate"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Hygiene vector 28 ("
            },
            {
              "k": "code",
              "text": "check-l0-dep-fanout.sh"
            },
            {
              "k": "text",
              "text": ") reads "
            },
            {
              "k": "code",
              "text": "docs/workspace.toml"
            },
            {
              "k": "text",
              "text": " and\nasserts every L0 crate's "
            },
            {
              "k": "code",
              "text": "sibling_deps"
            },
            {
              "k": "text",
              "text": " array length ≤ 3."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-schema"
            },
            {
              "k": "text",
              "text": " is at the limit (4 deps including "
            },
            {
              "k": "code",
              "text": "nika-catalog"
            },
            {
              "k": "text",
              "text": "); admission\nspec must justify each dep."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`cargo timings` per-crate budget (vector 29)",
          "id": "cargo-timings-per-crate-budget-vector-29"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Each crate has "
            },
            {
              "k": "code",
              "text": "compile_budget_secs"
            },
            {
              "k": "text",
              "text": " in "
            },
            {
              "k": "code",
              "text": "docs/workspace.toml"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "code",
          "lang": "toml",
          "text": "[crates.nika-core]\ncompile_budget_secs = 10\n[crates.nika-schema]\ncompile_budget_secs = 30"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Hygiene vector 29 ("
            },
            {
              "k": "code",
              "text": "check-cargo-timings.sh"
            },
            {
              "k": "text",
              "text": ") runs "
            },
            {
              "k": "code",
              "text": "cargo build --timings"
            },
            {
              "k": "text",
              "text": " and\nasserts no crate exceeds budget. CI ratchet — regression caught at PR time."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`cargo deny check duplicates` strict (vector 31)",
          "id": "cargo-deny-check-duplicates-strict-vector-31"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "deny.toml"
            },
            {
              "k": "text",
              "text": " configured to forbid duplicate crate versions in "
            },
            {
              "k": "code",
              "text": "Cargo.lock"
            },
            {
              "k": "text",
              "text": ".\nSingle-version policy across the workspace."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`cargo doc --document-private-items` clean (vector 32)",
          "id": "cargo-doc---document-private-items-clean-vector-32"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Internal API documented with same rigor as public. No undocumented private\nitems."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Maximum compile parallelism (independent tier-0 + tier-1 crates compile\nconcurrently)"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Forward-compat: adding a tier-4 means promoting one tier-3 first —\nconscious decision"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Predictable crate placement (mechanical sort test extends to internal\ntiers)"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "cargo timings"
              },
              {
                "k": "text",
                "text": " budget catches O(n²) trait-resolution regressions early"
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ "
              },
              {
                "k": "code",
                "text": "cargo deny duplicates"
              },
              {
                "k": "text",
                "text": " prevents "
              },
              {
                "k": "code",
                "text": "tokio-util 0.7"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "tokio-util 0.8"
              },
              {
                "k": "text",
                "text": "\necosystem split"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ +5 hygiene vectors (28, 29, 30, 31, 32) — implementation cost ~4h"
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Initial baseline: 14 "
              },
              {
                "k": "code",
                "text": "cargo timings"
              },
              {
                "k": "text",
                "text": " measurements committed to\n"
              },
              {
                "k": "code",
                "text": "docs/api-baselines/timings/<crate>.toml"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Vector summary (Phase B + E adds)",
          "id": "vector-summary-phase-b--e-adds"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "# · Name · Tier · Mode · Source ADR"
              }
            ],
            [
              {
                "k": "text",
                "text": "22 · check-no-async-in-l0 · P1 · full · (planned, PRE-DIAMOND)"
              }
            ],
            [
              {
                "k": "text",
                "text": "23 · check-workflow-envelope · P0 · fast · ADR-021→082"
              }
            ],
            [
              {
                "k": "text",
                "text": "24 · check-model-format · P0 · fast · ADR-021→082"
              }
            ],
            [
              {
                "k": "text",
                "text": "25 · check-kind-closed-set · P0 · fast · ADR-021→082"
              }
            ],
            [
              {
                "k": "text",
                "text": "26 · check-status-claims-sync · P1 · fast · (this session)"
              }
            ],
            [
              {
                "k": "text",
                "text": "27 · check-file-loc · P1 · full · ADR-023"
              }
            ],
            [
              {
                "k": "text",
                "text": "28 · check-l0-dep-fanout · P0 · fast · ADR-027"
              }
            ],
            [
              {
                "k": "text",
                "text": "29 · check-cargo-timings · P2 · full · ADR-027"
              }
            ],
            [
              {
                "k": "text",
                "text": "30 · check-public-api-baseline · P1 · full · ADR-024"
              }
            ],
            [
              {
                "k": "text",
                "text": "31 · check-cargo-deny-duplicates · P1 · full · ADR-027"
              }
            ],
            [
              {
                "k": "text",
                "text": "32 · check-cargo-doc-private · P2 · full · ADR-027"
              }
            ],
            [
              {
                "k": "text",
                "text": "33 · check-workspace-toml-sync · P0 · fast · ADR-026"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Total deployed at v0.81: "
            },
            {
              "k": "strong",
              "text": "21 (current) + 12 (new) = 33 vectors"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Reference",
          "id": "reference"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-022 (14-crate layout)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-026 (workspace.toml SSOT — provides metadata for vectors 28+29)"
              }
            ],
            [
              {
                "k": "code",
                "text": "~/.claude/.../memory/feedback_l0_strict_sublayer_tiers.md"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-028",
      "file": "adr-028-forward-compat-reservation-policy.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "SOTA evidence (cross-project survey, swarm-3)",
          "anchor": "sota-evidence-cross-project-survey-swarm-3"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Escape hatch",
          "anchor": "escape-hatch"
        },
        {
          "title": "Related decisions",
          "anchor": "related-decisions"
        }
      ],
      "words": 1068,
      "sha256": "3e5959ce1fec713b060830d03772a57c922ef6efacb60fb66b8b49410defeae0",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-028: Forward-compat reservation policy — seams now, crates later",
          "id": "adr-028-forward-compat-reservation-policy--seams-now-crates-later"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The swarm-3 SOTA audit (rust-architect + rust-pro + web-researcher, 2026-04-16,\nHEAD "
            },
            {
              "k": "code",
              "text": "5e810a94a"
            },
            {
              "k": "text",
              "text": ") closed a recurring strategic question:"
            }
          ]
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "em",
              "text": "\"Puisqu'on refait Nika from scratch, est-ce con de différer Cortex memory à v0.95\net WASM plugin à v0.100 ? Faut-il tout planifier dès v0.90 ?\""
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The temptation is real: if every crate is rewritten clean, why not fully bake the\nv0.95 Cortex orchestrator ("
            },
            {
              "k": "code",
              "text": "nika-memory"
            },
            {
              "k": "text",
              "text": " + 8 satellites) and v0.100 WASM plugin\nhost ("
            },
            {
              "k": "code",
              "text": "nika-wasm-host"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "nika-sandbox"
            },
            {
              "k": "text",
              "text": ") right now, so nothing ever needs to\nchange? This ADR closes the question."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Two-tier forward-compat policy. Seams are mandatory at v0.90; crate\nimplementations are deferable to their named v-target."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Tier 1 — SEAMS (mandatory, ship at v0.90)",
          "id": "tier-1--seams-mandatory-ship-at-v090"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A "
            },
            {
              "k": "em",
              "text": "seam"
            },
            {
              "k": "text",
              "text": " is the smallest API surface that fixes the shape of a future feature\nwithout committing to its implementation. Seams must land before v0.90:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Seam mechanism · Where in Nika today"
              }
            ],
            [
              {
                "k": "text",
                "text": "`#[non_exhaustive]` on every public enum + struct · All L0 + L0.5 crates (INV-019, ADR-007)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Reserved trait stubs in kernel (`MemoryStore`, `WasmPluginHost`, `Sandbox`, `EmbeddingProvider`, `ToolExecutor`, `ObservabilitySink`) · `crates/nika-kernel/src/` (Wave 2 S1-B)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Reserved sub-enums in event log (`EventKind::Memory` v0.95, `EventKind::Plugin` + `Wasm` v0.100) · Q4 + Q6 of `l0-l05-architecture-decisions.md`"
              }
            ],
            [
              {
                "k": "text",
                "text": "Reserved fields on Infer{Request,Response} + MemoryFrame · Wave 2 S1-B + S1-C"
              }
            ],
            [
              {
                "k": "text",
                "text": "`pub fn new()` constructors blocking struct-literal syntax · Every `#[non_exhaustive]` struct (Q-pattern)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`Extension { ns, name, payload }` escape hatch on `EventKind` · Q6 (Pattern B sub-enums)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Seams are "
            },
            {
              "k": "strong",
              "text": "cheap to add now, ruinous to retrofit later"
            },
            {
              "k": "text",
              "text": " (Hyrum's Law: every\nobservable behaviour gets depended on). They cost a few lines of "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": "\nand "
            },
            {
              "k": "code",
              "text": "Reserved { version }"
            },
            {
              "k": "text",
              "text": " placeholders. Their absence creates breaking changes\nat every v-tick."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Tier 2 — CRATE IMPLEMENTATIONS (deferable to named v-target)",
          "id": "tier-2--crate-implementations-deferable-to-named-v-target"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A "
            },
            {
              "k": "em",
              "text": "crate implementation"
            },
            {
              "k": "text",
              "text": " is the actual code that satisfies a seam. Crate\nimplementations are deferred until at least one of:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "A user signal demands the feature (real workflow, not speculation)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The upstream design space stabilises (e.g. WASM Component Model Preview 3)."
              }
            ],
            [
              {
                "k": "text",
                "text": "A sibling Diamond crate would benefit immediately (≥2 consumers, Q2 rule)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Specific deferrals locked by this ADR:"
            }
          ]
        },
        {
          "k": "h",
          "depth": 4,
          "text": "v0.95 Cortex memory — KEEP-DEFER",
          "id": "v095-cortex-memory--keep-defer"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Seams already shipped: "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EmbeddingProvider"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryDirective"
              },
              {
                "k": "text",
                "text": "\non "
              },
              {
                "k": "code",
                "text": "InferRequest"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "MemoryFrameRef"
              },
              {
                "k": "text",
                "text": " on "
              },
              {
                "k": "code",
                "text": "InferResponse"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EventKind::Memory"
              },
              {
                "k": "text",
                "text": "\nreserved sub-enum."
              }
            ],
            [
              {
                "k": "text",
                "text": "Crates deferred: "
              },
              {
                "k": "code",
                "text": "nika-memory"
              },
              {
                "k": "text",
                "text": " (L2 orchestrator) + 8 satellites\n("
              },
              {
                "k": "code",
                "text": "nika-memory-{hnsw,bm25,rrf,fsrs,rdfs-reasoner,temporal,graph-algos,autodesc}"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Rationale: 8 stub crates × 15k LOC budget each = ~120k LOC of empty hulls\nwith zero user signal. "
              },
              {
                "k": "code",
                "text": "fsrs"
              },
              {
                "k": "text",
                "text": " (forgetting curve) and "
              },
              {
                "k": "code",
                "text": "rdfs-reasoner"
              },
              {
                "k": "text",
                "text": " design\nspaces are open in 2026 — premature freeze guarantees a v0.95 refactor."
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.90 deliverable: 2-3 property tests proving "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " can host a\nrealistic HNSW backend (no implementation, just the trait shape proof)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 4,
          "text": "v0.100 WASM plugin host — KEEP-DEFER",
          "id": "v0100-wasm-plugin-host--keep-defer"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Seams already shipped: "
              },
              {
                "k": "code",
                "text": "WasmPluginHost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Sandbox"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EventKind::Plugin"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "Wasm"
              },
              {
                "k": "text",
                "text": " reserved."
              }
            ],
            [
              {
                "k": "text",
                "text": "Crates deferred: "
              },
              {
                "k": "code",
                "text": "nika-wasm-host"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "nika-sandbox"
              },
              {
                "k": "text",
                "text": " (L3)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Rationale: WebAssembly Component Model Preview 3 (async, streams) is still\nin flux upstream as of 2026-04. wasmtime tracks; wasmer + wasmi lag. Stubbing\nbefore Preview 3 stabilises = rebreak guaranteed."
              }
            ],
            [
              {
                "k": "text",
                "text": "Escape hatch: if a user demands plugins before v0.100, a Lua/Starlark/wasmi\nL1 crate ships under "
              },
              {
                "k": "code",
                "text": "unstable-*"
              },
              {
                "k": "text",
                "text": " (FCI-007 distribution flag)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "What \"tout v0.100\" actually means",
          "id": "what-tout-v0100-actually-means"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The user proposal — \"since we rewrite from scratch, plan everything for v0.100\" —\nis "
            },
            {
              "k": "strong",
              "text": "half right"
            },
            {
              "k": "text",
              "text": ": every "
            },
            {
              "k": "em",
              "text": "seam"
            },
            {
              "k": "text",
              "text": " must be planned for v0.100 (already done). But\n"
            },
            {
              "k": "em",
              "text": "implementations"
            },
            {
              "k": "text",
              "text": " follow YAGNI (Beck) + premature-abstraction caution (Metz):\nabstraction without ≥3 concrete usages is debt, not investment."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "SOTA evidence (cross-project survey, swarm-3)",
          "id": "sota-evidence-cross-project-survey-swarm-3"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Project · Future-feature pattern · Lesson"
              }
            ],
            [
              {
                "k": "text",
                "text": "Bevy (ECS, ~50 crates) · No reservation; deliberate breakage every 3 months. · \"Break early, break often\" — community accepts. Not Nika's contract (stable forward-compat · real semver toward 1.0 per ADR-002 · amended D-2026-06-20-N1)."
              }
            ],
            [
              {
                "k": "text",
                "text": "tokio (~10 crates) · `#[non_exhaustive]` + feature flags. `tokio-uring`, `tokio-console` = **separate crates**, not core refactors. · Isolate innovation in opt-in satellites."
              }
            ],
            [
              {
                "k": "text",
                "text": "Apollo Router (~40 crates) · Tower `Layer<S>` extension points + handshake-negotiated protocols (Federation 2). · Extension points = traits + negotiation, not frozen enums."
              }
            ],
            [
              {
                "k": "text",
                "text": "rust-analyzer (~70 crates) · Salsa query traits (open) + process boundaries (`proc-macro-srv`). · Process boundary buys forward-compat for free."
              }
            ],
            [
              {
                "k": "text",
                "text": "wasmtime · Component Model arrived as **sister crate** (`wasmtime-component`), not a core rewrite. · New major feature = new crate, not surgery."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Pattern dominant"
            },
            {
              "k": "text",
              "text": ": zero surveyed projects pre-stub crates for v+2 milestones.\nAll five rely on the same three levers Nika already uses ("
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": ",\nfeatures, satellite crates)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Positive:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "v0.90 ships with a stable kernel API surface — Cortex and WASM can land\nlater as additive admissions without touching admitted L0/L0.5 crates."
              }
            ],
            [
              {
                "k": "text",
                "text": "Zero \"stub crate debt\" at v0.90 (no empty 8-satellite scaffolding)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Upstream churn (Component Model Preview 3, fsrs design) does not block Nika."
              }
            ],
            [
              {
                "k": "text",
                "text": "Any departure from a reserved seam (e.g. adding a non-additive field to\n"
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": ") is a breaking change — caught by "
              },
              {
                "k": "code",
                "text": "cargo public-api"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "cargo semver-checks"
              },
              {
                "k": "text",
                "text": " (planned, P0 follow-up)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Negative:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Reserved seams that turn out to be "
              },
              {
                "k": "em",
                "text": "wrong"
              },
              {
                "k": "text",
                "text": " (e.g. "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " shape\nunsuitable for real backends) require a breaking trait change at v0.95.\nMitigation: property tests in v0.90 (P0 follow-up) prove the seam can host\na realistic backend before lock."
              }
            ],
            [
              {
                "k": "text",
                "text": "Documentation cost: every reserved seam needs a sentence in\n"
              },
              {
                "k": "code",
                "text": "forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " explaining the v-target and the escape hatch."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Neutral:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crate count target stays at 40-42 for v0.90, +9 Cortex at v0.95, +2 WASM at\nv0.100 = ~51-53 final. No change to ROADMAP."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Escape hatch",
          "id": "escape-hatch"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Reserved kernel traits and reserved event sub-enums "
            },
            {
              "k": "strong",
              "text": "remain modifiable until\ntheir named v-target"
            },
            {
              "k": "text",
              "text": ". A reserved seam is not a stable contract — it is a\n"
            },
            {
              "k": "em",
              "text": "shape commitment"
            },
            {
              "k": "text",
              "text": ". The day Cortex lands at v0.95, "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": "'s final shape\nis locked; until then, breaking changes to the trait are allowed and tracked\nas "
            },
            {
              "k": "code",
              "text": "affects_layers: [L0.5]"
            },
            {
              "k": "text",
              "text": " ADR amendments."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This must be documented in "
            },
            {
              "k": "code",
              "text": "docs/architecture/forward-compat-invariants.md"
            },
            {
              "k": "text",
              "text": "\nunder a new "
            },
            {
              "k": "code",
              "text": "§reserved-mutability"
            },
            {
              "k": "text",
              "text": " section (P1 follow-up)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related decisions",
          "id": "related-decisions"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "ADR-002"
              },
              {
                "k": "text",
                "text": " Real semver toward 1.0 (amended D-2026-06-20-N1) — context for the release model that demands reservation."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-006"
              },
              {
                "k": "text",
                "text": " Layered kernel ISP traits — defines what trait stubs reserve."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-007"
              },
              {
                "k": "text",
                "text": " Forward-compat invariants — "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "::new()"
              },
              {
                "k": "text",
                "text": " discipline."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-014"
              },
              {
                "k": "text",
                "text": " Sealed kernel traits — prevents downstream impl, makes seam mutability safer."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-020"
              },
              {
                "k": "text",
                "text": " WASM plugin boundary — names the v0.100 deferral target."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-024"
              },
              {
                "k": "text",
                "text": " SOTA Rust patterns — survey context for this ADR."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-029",
      "file": "adr-029-embedding-spec-reservation.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Empirical validation (why accepted now)",
          "anchor": "empirical-validation-why-accepted-now"
        },
        {
          "title": "See also",
          "anchor": "see-also"
        }
      ],
      "words": 400,
      "sha256": "5ecfefe2ebbf1634604406ea150a0023c73f68f9a539544e86216425e363da02",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-029: EmbeddingSpec value-type reservation (Cortex seed)",
          "id": "adr-029-embeddingspec-value-type-reservation-cortex-seed"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "text",
              "text": "Accepted 2026-07-11 (operator-delegated ADR ruling): the decision has\nbeen load-bearing in code since v0.81 and the reservation held its\nshape for three months — a "
            },
            {
              "k": "code",
              "text": "proposed"
            },
            {
              "k": "text",
              "text": " status no longer described\nreality. This prose replaces the Phase-C stub."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The memory/Connectome era needs embedding storage whose index layout and\ndistance kernel are decided by three axes — "
            },
            {
              "k": "strong",
              "text": "dim"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "strong",
              "text": "dtype"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "strong",
              "text": "metric"
            },
            {
              "k": "text",
              "text": " —\nplus the model identity that produced the vector. The embedding landscape\nmoves monthly (Matryoshka truncation; 256→3072 dims; f32/f16/bf16/i8/binary\ndtypes; cosine/dot/euclidean/hamming metrics), so whatever type we reserve\nmust be able to grow fields without a breaking-change migration."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Wave 4A R1 (commit "
            },
            {
              "k": "code",
              "text": "001ae0b6f"
            },
            {
              "k": "text",
              "text": ", 2026-04-17) added "
            },
            {
              "k": "code",
              "text": "EmbeddingSpec"
            },
            {
              "k": "text",
              "text": " to\n"
            },
            {
              "k": "code",
              "text": "nika-types"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-types/src/embedding.rs"
            },
            {
              "k": "text",
              "text": ") as that reservation,\nper the ADR-028 forward-compat reservation policy."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "EmbeddingSpec"
            },
            {
              "k": "text",
              "text": " ships in "
            },
            {
              "k": "code",
              "text": "nika-types"
            },
            {
              "k": "text",
              "text": " (L0) as "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " with a\n"
            },
            {
              "k": "code",
              "text": "::new()"
            },
            {
              "k": "text",
              "text": " constructor, carrying the (dim, dtype, metric, model_id) tuple:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "dim / dtype / metric"
              },
              {
                "k": "text",
                "text": " — exactly what HNSW/BM25/RRF backends need to\npick index layout and distance kernel."
              }
            ],
            [
              {
                "k": "strong",
                "text": "model_id optional"
              },
              {
                "k": "text",
                "text": " — catalog-free consumers (tests, tools) construct\na spec without materialising a provider name."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`#[non_exhaustive]` + `new()`"
              },
              {
                "k": "text",
                "text": " — additive field evolution stays\nnon-breaking by construction (INV-019 idiom); the reservation absorbs\nthe monthly landscape drift instead of chasing it."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Field shape pinned in the commit body of "
            },
            {
              "k": "code",
              "text": "001ae0b6f"
            },
            {
              "k": "text",
              "text": " and in the FCI-035\naddendum of\n"
            },
            {
              "k": "link",
              "text": "`docs/architecture/forward-compat-invariants.md`",
              "href": "../architecture/forward-compat-invariants.md"
            },
            {
              "k": "text",
              "text": ".\n"
            },
            {
              "k": "code",
              "text": "EmbeddingProvider"
            },
            {
              "k": "text",
              "text": " (ADR-034, L0.5) consumes the spec."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "No reservation — add the type when the first provider lands."
              },
              {
                "k": "text",
                "text": " Rejected:\nthe Connectome consumers (index rehydration · cross-provider portability)\nwould then force a breaking L0 change mid-era; ADR-028 exists precisely\nto pay this cost early and additively."
              }
            ],
            [
              {
                "k": "strong",
                "text": "A wider spec (normalization · truncation · pooling fields now)."
              },
              {
                "k": "text",
                "text": "\nRejected: speculative fields rot; "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " makes adding them\nwhen a real provider needs them a patch-level change."
              }
            ],
            [
              {
                "k": "strong",
                "text": "String-typed axes."
              },
              {
                "k": "text",
                "text": " Rejected: dim/dtype/metric are closed vocabularies\nthe kernel must match on — typed enums keep the distance-kernel dispatch\ntotal."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Empirical validation (why accepted now)",
          "id": "empirical-validation-why-accepted-now"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Three months in production shape: the type is consumed, the field set never\nneeded a change, and zero migrations were forced on dependents. The\nreservation did exactly what ADR-028 promised."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "See also",
          "id": "see-also"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-028 — Forward-compat reservation policy (this ADR is an instance)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 — L0 foundational types (lives in the same crate)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-034 — L0.5 trait expansion ("
              },
              {
                "k": "code",
                "text": "EmbeddingProvider"
              },
              {
                "k": "text",
                "text": " consumes "
              },
              {
                "k": "code",
                "text": "EmbeddingSpec"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "FCI-035 addendum — Wave 4A/4B reservation catalog."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-030",
      "file": "adr-030-memory-frame-trust.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Empirical validation (what makes this \"merged code\")",
          "anchor": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "title": "See also",
          "anchor": "see-also"
        }
      ],
      "words": 526,
      "sha256": "56a07598068696ef29d413d58365b1ef7f229bfa8f50d322ec588779812ec0b2",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-030: MemoryFrameRef.trust reservation (Shield gate seed)",
          "id": "adr-030-memoryframereftrust-reservation-shield-gate-seed"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Status flipped "
            },
            {
              "k": "code",
              "text": "proposed → accepted"
            },
            {
              "k": "text",
              "text": " 2026-07-12 under the merged-code\nrule (the #474 ADR ruling): the decision is load-bearing in shipped\ncode — "
            },
            {
              "k": "code",
              "text": "MemoryFrameRef.trust"
            },
            {
              "k": "text",
              "text": " exists, defaults safely, and rides the\nserde wire today. An ADR whose decision the workspace already executes\ndoes not stay \"proposed\"; the prose below documents what IS."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Wave 4A R2 (commit "
            },
            {
              "k": "code",
              "text": "41e8a1467"
            },
            {
              "k": "text",
              "text": ", 2026-04-17) added "
            },
            {
              "k": "code",
              "text": "trust: TrustLevel"
            },
            {
              "k": "text",
              "text": "\nas a reserved field on "
            },
            {
              "k": "code",
              "text": "MemoryFrameRef"
            },
            {
              "k": "text",
              "text": " so that Shield (the\nNIKA-380..389 code family) can gate every recall by the trust level of\nthe source memory frame "
            },
            {
              "k": "strong",
              "text": "without a breaking change"
            },
            {
              "k": "text",
              "text": " when real recall\narrives. Per ADR-028 (forward-compat reservation policy), the field\nships years before its writer: reserving the slot is cheap; retrofitting\nit onto a "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " struct consumed across layers is not."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The trust model this seeds is "
            },
            {
              "k": "strong",
              "text": "sticky taint"
            },
            {
              "k": "text",
              "text": ": trust is assigned at\ningest and never upgrades on recall — a frame that entered the store\nuntrusted can never launder itself into a trusted context by being\nremembered."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "MemoryFrameRef"
              },
              {
                "k": "text",
                "text": " carries "
              },
              {
                "k": "code",
                "text": "pub trust: crate::trust::TrustLevel"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "nika-types/src/memory.rs"
              },
              {
                "k": "text",
                "text": " — re-exported through "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Wire default"
              },
              {
                "k": "text",
                "text": ": when the field is absent on the wire, serde fills\n"
              },
              {
                "k": "code",
                "text": "TrustLevel::UNTRUSTED"
              },
              {
                "k": "text",
                "text": " (50) via "
              },
              {
                "k": "code",
                "text": "default_trust_untrusted()"
              },
              {
                "k": "text",
                "text": " — a\nlegacy or unknown-origin frame is conservatively untrusted, keeping\nnew-consumer/old-producer AND old-consumer/new-producer strictly\nfail-safe."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No language-level `Default`"
              },
              {
                "k": "text",
                "text": ": "
              },
              {
                "k": "code",
                "text": "TrustLevel"
              },
              {
                "k": "text",
                "text": " deliberately does NOT\nimplement "
              },
              {
                "k": "code",
                "text": "Default"
              },
              {
                "k": "text",
                "text": " (removed in Wave 3 · P1-2 rust-security). A\nblanket "
              },
              {
                "k": "code",
                "text": "default()"
              },
              {
                "k": "text",
                "text": " returning UNTRUSTED (50) sits ABOVE SANDBOXED\n(10) in the lattice — a silent inversion of safe-by-default for any\ncapability gate written as "
              },
              {
                "k": "code",
                "text": "is_at_least(SANDBOXED)"
              },
              {
                "k": "text",
                "text": ". Trust must be a\ndeliberate construction at every call site; only the serde\nwire-compat path carries a default, and that one is scoped, named,\nand documented at the field."
              }
            ],
            [
              {
                "k": "text",
                "text": "The lattice itself (SANDBOXED 10 < UNTRUSTED 50 < TRUSTED 150 <\nELEVATED 200 < SYSTEM 255 · "
              },
              {
                "k": "code",
                "text": "meet"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "join"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "is_at_least"
              },
              {
                "k": "text",
                "text": ") is ADR-033's\ndecision; this ADR consumes it."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "`Option<TrustLevel>`"
              },
              {
                "k": "text",
                "text": " — rejected: an absent trust level forces\nevery reader to invent a policy at the read site; the whole point is\nONE conservative policy at the boundary."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Defaulting to TRUSTED for engine-internal frames"
              },
              {
                "k": "text",
                "text": " — rejected: the\nwriter (real ingest) does not exist yet, so nothing can vouch for\nprovenance; safe-by-default (ADR-033 rev.2) wins until it does."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Deferring the field to the Connectome era"
              },
              {
                "k": "text",
                "text": " — rejected per ADR-028:\nadding a field later to a serialized, cross-layer struct is the\nexact breaking change the reservation policy exists to prevent."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Empirical validation (what makes this \"merged code\")",
          "id": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-types/src/memory.rs"
              },
              {
                "k": "text",
                "text": " — the field, its serde default fn, and the\nconstructor defaulting to UNTRUSTED, all shipped and exercised by the\ncrate's serde round-trip tests."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-types/src/trust.rs"
              },
              {
                "k": "text",
                "text": " — the lattice with the no-"
              },
              {
                "k": "code",
                "text": "Default"
              },
              {
                "k": "text",
                "text": "\ninvariant documented at the impl site."
              }
            ],
            [
              {
                "k": "text",
                "text": "The "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "new()"
              },
              {
                "k": "text",
                "text": " discipline (FCI invariant #19)\nholds on both types, so the v0.95+ writer lands without a major."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "See also",
          "id": "see-also"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-028 — Forward-compat reservation policy (this ADR is an instance)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 — "
              },
              {
                "k": "code",
                "text": "TrustLevel"
              },
              {
                "k": "text",
                "text": " lattice + "
              },
              {
                "k": "code",
                "text": "UNTRUSTED"
              },
              {
                "k": "text",
                "text": " default inversion."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-034 — "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " trait (will be sealed at verb admission)."
              }
            ],
            [
              {
                "k": "text",
                "text": "FCI-035 addendum — Wave 4A/4B reservation catalog."
              }
            ],
            [
              {
                "k": "text",
                "text": "NIKA-380..389 — Shield capability / trust violation error codes."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-031",
      "file": "adr-031-recall-query-tenant-keyspace.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Empirical validation (what makes this \"merged code\")",
          "anchor": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "title": "See also",
          "anchor": "see-also"
        }
      ],
      "words": 461,
      "sha256": "25c9084be7cc38af02da6e322fb96c0654fe9350b47db2b6ebde72e6864eef0d",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-031: RecallQuery.tenant reservation (multi-tenant keyspace seed)",
          "id": "adr-031-recallquerytenant-reservation-multi-tenant-keyspace-seed"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Status flipped "
            },
            {
              "k": "code",
              "text": "proposed → accepted"
            },
            {
              "k": "text",
              "text": " 2026-07-12 under the merged-code\nrule (the #474 ADR ruling): the decision is load-bearing in shipped\ncode — "
            },
            {
              "k": "code",
              "text": "RecallQuery.tenant"
            },
            {
              "k": "text",
              "text": " exists, is mandatory, and every recall\nscope flows through it. The prose documents what IS, including where\nthe shipped shape deliberately hardened beyond the original seed."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Wave 4A R3 (commit "
            },
            {
              "k": "code",
              "text": "41e8a1467"
            },
            {
              "k": "text",
              "text": ", 2026-04-17) reserved a tenant keyspace\non "
            },
            {
              "k": "code",
              "text": "RecallQuery"
            },
            {
              "k": "text",
              "text": " so multi-tenant recall can ship later without a\nbreaking change (ADR-028 reservation policy). The reservation exists so\nthat a v0.8x consumer compiled against a single-tenant world keeps\nworking the day a multi-tenant host arrives — and so that cross-tenant\nreads are structurally impossible rather than policy-forbidden."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "RecallQuery"
              },
              {
                "k": "text",
                "text": " carries "
              },
              {
                "k": "code",
                "text": "pub tenant: TenantId"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "nika-kernel-ai/src/memory.rs"
              },
              {
                "k": "text",
                "text": " — re-exported through "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": ")\n— "
              },
              {
                "k": "strong",
                "text": "mandatory, not `Option`"
              },
              {
                "k": "text",
                "text": ". The original seed sketched\n"
              },
              {
                "k": "code",
                "text": "Option<TenantId>"
              },
              {
                "k": "text",
                "text": " with "
              },
              {
                "k": "code",
                "text": "None"
              },
              {
                "k": "text",
                "text": " = default; the shipped shape hardened:\nan optional tenant invites every store impl to invent its own "
              },
              {
                "k": "code",
                "text": "None"
              },
              {
                "k": "text",
                "text": "\npolicy, which is exactly how cross-tenant leaks are born. Instead:"
              },
              {
                "k": "text",
                "text": "- `RecallQuery::new(…)` scopes to `TenantId::default_tenant()` —\n  single-tenant deployments use it and never think about the field;\n- `RecallQuery::scoped(tenant, …)` is the multi-tenant constructor;\n- every recall MUST scope by the field (the struct doc states the\n  contract; store impls return only same-tenant frames)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The struct is "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " with constructor discipline\n(FCI invariant #19), so later filters (the "
              },
              {
                "k": "code",
                "text": "observed_after/_before"
              },
              {
                "k": "text",
                "text": "\ntemporal pair rode in on the same shape) land without a major."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "`Option<TenantId>`, `None` = default tenant"
              },
              {
                "k": "text",
                "text": " (the seed's sketch) —\nrejected at hardening: per-impl "
              },
              {
                "k": "code",
                "text": "None"
              },
              {
                "k": "text",
                "text": " policies are the leak vector;\na mandatory field with a defaulting constructor gives the same\nsingle-tenant ergonomics with ONE policy instead of N."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Tenant as a store-construction parameter only"
              },
              {
                "k": "text",
                "text": " — rejected: it\nforces one store instance per tenant (hosts want pooled stores) and\nremoves the per-query audit trail."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No reservation until multi-tenant ships"
              },
              {
                "k": "text",
                "text": " — rejected per ADR-028:\nadding a mandatory field to a serialized, cross-layer query type\nlater is the breaking change the policy exists to prevent."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Empirical validation (what makes this \"merged code\")",
          "id": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-kernel-ai/src/memory.rs"
              },
              {
                "k": "text",
                "text": " — the field with its mandatory-scope\ncontract in the struct docs, "
              },
              {
                "k": "code",
                "text": "new()"
              },
              {
                "k": "text",
                "text": " defaulting to\n"
              },
              {
                "k": "code",
                "text": "TenantId::default_tenant()"
              },
              {
                "k": "text",
                "text": " (line 113), "
              },
              {
                "k": "code",
                "text": "scoped()"
              },
              {
                "k": "text",
                "text": " for multi-tenant\nhosts (line 119)."
              }
            ],
            [
              {
                "k": "code",
                "text": "TenantId"
              },
              {
                "k": "text",
                "text": " lives beside the provider types (ADR-033's family) and\nserializes with the query."
              }
            ],
            [
              {
                "k": "text",
                "text": "The mock kernel exercises the same shape — recall tests construct\nqueries through the defaulting constructor."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "See also",
          "id": "see-also"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-028 — Forward-compat reservation policy (this ADR is an instance)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-030 — "
              },
              {
                "k": "code",
                "text": "MemoryFrameRef.trust"
              },
              {
                "k": "text",
                "text": " (the sibling Wave 4A reservation —\ntrust gates what a recall may spotlight; tenant gates what it may see)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 — "
              },
              {
                "k": "code",
                "text": "TenantId"
              },
              {
                "k": "text",
                "text": " newtype + "
              },
              {
                "k": "code",
                "text": "default_tenant()"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-034 — "
              },
              {
                "k": "code",
                "text": "MemoryStore::recall"
              },
              {
                "k": "text",
                "text": " signature (tenant flows through)."
              }
            ],
            [
              {
                "k": "text",
                "text": "FCI-035 addendum — Wave 4A/4B reservation catalog."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-032",
      "file": "adr-032-wasm-plugin-context.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Empirical validation (what makes this \"merged code\")",
          "anchor": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "title": "See also",
          "anchor": "see-also"
        }
      ],
      "words": 499,
      "sha256": "cfd9630b8363a05ad7f3c049f67d11beb6da10860cbc9e4fe6ae105f61a0ebb7",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-032: WasmPluginError OutOfFuel + Trap + PluginCallContext reservation",
          "id": "adr-032-wasmpluginerror-outoffuel--trap--plugincallcontext-reservation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Status flipped "
            },
            {
              "k": "code",
              "text": "proposed → accepted"
            },
            {
              "k": "text",
              "text": " 2026-07-12 under the merged-code\nrule (the #474 ADR ruling): all three reservations ship in\n"
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-plugin/src/wasm.rs"
            },
            {
              "k": "text",
              "text": " (the kernel's plugin member —\nthe ADR predates the kernel descent; "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " re-exports). The\nstub's two open follow-ups were RESOLVED in the shipped code; the\nprose records those resolutions."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Wave 4A R4 (commit "
            },
            {
              "k": "code",
              "text": "368820e42"
            },
            {
              "k": "text",
              "text": ", 2026-04-17) extended the WASM plugin\nstubs shipped under ADR-020 with three additive reservations, so the\nv0.100 wasmtime integration lands without breaking every v0.8x plugin\nconsumer: a fuel-cancellation error, a structured trap surface, and a\nper-call context that carries trust + budgets."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "All three shapes are "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": ", shipped in\n"
            },
            {
              "k": "code",
              "text": "nika-kernel-plugin/src/wasm.rs"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "`WasmPluginError::OutOfFuel { consumed, budget }`"
              },
              {
                "k": "text",
                "text": " — fuel-based\ncancellation beside the wall-clock "
              },
              {
                "k": "code",
                "text": "Timeout"
              },
              {
                "k": "text",
                "text": ". The seed sketched a\nsingle "
              },
              {
                "k": "code",
                "text": "fuel_consumed"
              },
              {
                "k": "text",
                "text": "; the shipped shape carries BOTH sides of the\nledger — an error that says how much was spent but not what was\nallowed teaches nothing."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`WasmPluginError::Trap { kind: TrapKind }`"
              },
              {
                "k": "text",
                "text": " — guest-level aborts\n(unreachable · divide-by-zero · stack overflow · …) distinct from\nhost errors. "
              },
              {
                "k": "strong",
                "text": "Follow-up resolved"
              },
              {
                "k": "text",
                "text": ": "
              },
              {
                "k": "code",
                "text": "TrapKind"
              },
              {
                "k": "text",
                "text": " is\n"
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ", mirroring wasmtime's "
              },
              {
                "k": "code",
                "text": "TrapCode"
              },
              {
                "k": "text",
                "text": " taxonomy at the\nnika layer — engine-specific variants land without a major AND\nwithout a direct wasmtime dep in L0.5."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`PluginCallContext { trust, input_trust, cancel, fuel_budget,\nwall_timeout_ms }`"
              },
              {
                "k": "text",
                "text": " — the per-call DTO replacing the bare-cancel\nargument. "
              },
              {
                "k": "strong",
                "text": "Follow-up resolved"
              },
              {
                "k": "text",
                "text": ": the seed's "
              },
              {
                "k": "code",
                "text": "caller_trust"
              },
              {
                "k": "text",
                "text": " /\n"
              },
              {
                "k": "code",
                "text": "plugin_trust"
              },
              {
                "k": "text",
                "text": " pair became "
              },
              {
                "k": "code",
                "text": "input_trust"
              },
              {
                "k": "text",
                "text": " (propagated from the verb\nthat constructed the call) and "
              },
              {
                "k": "code",
                "text": "trust"
              },
              {
                "k": "text",
                "text": " (granted to the plugin's\nOUTPUT — lower than "
              },
              {
                "k": "code",
                "text": "input_trust"
              },
              {
                "k": "text",
                "text": " by host policy: plugin output is\nalways untrusted data regardless of what it was given, the ADR-030\nsticky-taint model applied at the plugin boundary). Both budget\nknobs ride the context ("
              },
              {
                "k": "code",
                "text": "None"
              },
              {
                "k": "text",
                "text": " = unmetered), so the dual-timeout\npolicy (fuel + wall) is per-call, not global."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Single timeout (wall-clock only)"
              },
              {
                "k": "text",
                "text": " — rejected: wall time cannot\ndistinguish a slow host from a hot guest loop; fuel is deterministic\nper-op and survives host load. Both ship."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Closed `TrapKind` enum"
              },
              {
                "k": "text",
                "text": " — rejected (the stub's open question):\nwasmtime/wasmer taxonomies evolve; a closed enum turns every new trap\nclass into a major."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Trust pair as caller/plugin"
              },
              {
                "k": "text",
                "text": " (the seed's sketch) — rejected at\nhardening: what gates downstream consumption is the trust of the\nOUTPUT, not the identity of the caller; input/output naming states\nthe dataflow directly."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Empirical validation (what makes this \"merged code\")",
          "id": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-kernel-plugin/src/wasm.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "OutOfFuel"
              },
              {
                "k": "text",
                "text": " (line ~146), "
              },
              {
                "k": "code",
                "text": "Trap"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "TrapKind"
              },
              {
                "k": "text",
                "text": " (lines ~162-192, with "
              },
              {
                "k": "code",
                "text": "Display"
              },
              {
                "k": "text",
                "text": "), "
              },
              {
                "k": "code",
                "text": "PluginCallContext"
              },
              {
                "k": "text",
                "text": "\n(line ~217) with the untrusted-default constructor."
              }
            ],
            [
              {
                "k": "text",
                "text": "The reservations are exercised by the plugin trait's mock host in\n"
              },
              {
                "k": "code",
                "text": "nika-kernel-mock"
              },
              {
                "k": "text",
                "text": " (the ADR-020 test seam)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "See also",
          "id": "see-also"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-020 — WASM plugin boundary + Sandbox (amended by this ADR)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-028 — Forward-compat reservation policy (this ADR is an instance)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-030 — sticky-taint trust model (applied here at the plugin edge)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 — "
              },
              {
                "k": "code",
                "text": "TrustLevel"
              },
              {
                "k": "text",
                "text": " lattice used by "
              },
              {
                "k": "code",
                "text": "PluginCallContext"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "FCI-035 addendum — Wave 4A/4B reservation catalog."
              }
            ],
            [
              {
                "k": "text",
                "text": "NIKA-700..749 — WASM plugin error code range (reserved v0.80)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-033",
      "file": "adr-033-l0-foundational-types.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 1485,
      "sha256": "cb3eebfa81ae865563dbafd1a218dc703e28cd371febe526643db1f22b559327",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-033: L0 foundational types: Cost, UUIDv7, TrustLevel, TokenUsage evolution",
          "id": "adr-033-l0-foundational-types-cost-uuidv7-trustlevel-tokenusage-evolution"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "L0 today contains exactly two crates: "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " (error codes,\nNIKA-XXX ranges) and "
            },
            {
              "k": "code",
              "text": "nika-catalog"
            },
            {
              "k": "text",
              "text": " (provider/model catalog data).\nNeither holds the foundational "
            },
            {
              "k": "em",
              "text": "value"
            },
            {
              "k": "text",
              "text": " types — Cost, IDs, TrustLevel —\nthat L1 effect crates and L2 verb crates will need."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel currently leaks several types that should live one layer\nbelow it."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "TokenUsage"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/provider.rs:247-256"
            },
            {
              "k": "text",
              "text": ") has 4 fields:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[non_exhaustive]\npub struct TokenUsage {\n    pub input_tokens: u64,\n    pub output_tokens: u64,\n    pub cache_read_tokens: Option<u64>,\n    pub cache_write_tokens: Option<u64>,\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This is missing several axes that real providers already bill for:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "reasoning_tokens"
              },
              {
                "k": "text",
                "text": " — Anthropic extended thinking, OpenAI o3."
              }
            ],
            [
              {
                "k": "code",
                "text": "thinking_tokens"
              },
              {
                "k": "text",
                "text": " — separate from reasoning; visible thinking trace."
              }
            ],
            [
              {
                "k": "code",
                "text": "cache_creation_tokens"
              },
              {
                "k": "text",
                "text": " — Anthropic prompt caching write side."
              }
            ],
            [
              {
                "k": "code",
                "text": "image_tokens_in"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "image_tokens_out"
              },
              {
                "k": "text",
                "text": " — vision input + image output."
              }
            ],
            [
              {
                "k": "code",
                "text": "audio_tokens_in"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "audio_tokens_out"
              },
              {
                "k": "text",
                "text": " — Gemini Live, GPT-4o audio."
              }
            ],
            [
              {
                "k": "code",
                "text": "video_tokens"
              },
              {
                "k": "text",
                "text": " — Gemini 1.5 Pro video."
              }
            ],
            [
              {
                "k": "code",
                "text": "tool_use_tokens"
              },
              {
                "k": "text",
                "text": " — some providers bill tool definitions separately."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "MemoryId"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/memory.rs:21-22"
            },
            {
              "k": "text",
              "text": ") is "
            },
            {
              "k": "code",
              "text": "MemoryId(u128)"
            },
            {
              "k": "text",
              "text": "\nwith "
            },
            {
              "k": "code",
              "text": "Display"
            },
            {
              "k": "text",
              "text": " as "
            },
            {
              "k": "code",
              "text": "mem-{hex32}"
            },
            {
              "k": "text",
              "text": ". The "
            },
            {
              "k": "code",
              "text": "u128"
            },
            {
              "k": "text",
              "text": " is opaque random; not\ntime-sortable. Memory queries that want chronological ordering need\na separate timestamp."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "TaskId"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/types.rs"
            },
            {
              "k": "text",
              "text": ") is "
            },
            {
              "k": "code",
              "text": "TaskId(String)"
            },
            {
              "k": "text",
              "text": " —\na bare string wrapper, no validation, no time ordering, no parse\nguarantee."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Cost"
            },
            {
              "k": "text",
              "text": " is currently "
            },
            {
              "k": "code",
              "text": "cost_usd: Option<f64>"
            },
            {
              "k": "text",
              "text": " on "
            },
            {
              "k": "code",
              "text": "InferResponse"
            },
            {
              "k": "text",
              "text": "\n("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/provider.rs:307"
            },
            {
              "k": "text",
              "text": "). Floating-point accumulation\nacross thousands of API calls drifts; we cannot ship billing on f64."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "TrustLevel"
            },
            {
              "k": "text",
              "text": " is referenced by Shield error codes\n(NIKA-380..389: "
            },
            {
              "k": "code",
              "text": "nika-error/src/codes.rs"
            },
            {
              "k": "text",
              "text": ") but currently passed as\nhardcoded strings in workflow engine code. No enum exists in any\nadmitted crate."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "L1 effect crates and L2 verb crates need a shared vocabulary for these\nvalues. Putting them in "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " (L0.5) creates an upward\ndependency from L0 catalog → L0.5 kernel just to share an "
            },
            {
              "k": "code",
              "text": "IdGenerator"
            },
            {
              "k": "text",
              "text": "\ntarget type. Putting them in L0 "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " is the only stratum that\nall higher layers already depend on."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Add 23 foundational value types to `nika-error` (L0). All higher\nlayers re-export or import directly. `nika-error` keeps its dependency\nbudget: std + serde + thiserror + miette only."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The 23 types group into 9 modules:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Module · Types · Why"
              }
            ],
            [
              {
                "k": "text",
                "text": "`cost` · `Cost(i128)` · nano-USD; arithmetic; Display `$X.XXXXXX`; no float drift"
              }
            ],
            [
              {
                "k": "text",
                "text": "`id` · 9 newtyped UUIDv7 IDs: `RunId`, `EventId`, `TraceId`, `SpanId`, `CorrelationId`, `WorkflowId`, `TenantId`, `ProviderId`, `ModelId` · type-safe ID separation; chronological ordering"
              }
            ],
            [
              {
                "k": "text",
                "text": "`trust` · `TrustLevel(u8)` lattice + `meet` / `join` / `is_at_least` · Shield gating becomes a compile-time invariant"
              }
            ],
            [
              {
                "k": "text",
                "text": "`retry` · `RetryConfig`, `ErrorCategory` · shared vocabulary for ADR-019's stratified retry"
              }
            ],
            [
              {
                "k": "text",
                "text": "`budget` · `BudgetDirective` enum · per-task budget enforcement (cost, tokens, time)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`schema` · `EventSchemaVersion`, `TraceFormatVersion` · wire-compat versioning for events + traces"
              }
            ],
            [
              {
                "k": "text",
                "text": "`hash` · `Blake3Hash`, `ContentDigest`, `BlobRef` · content-addressed storage primitive"
              }
            ],
            [
              {
                "k": "text",
                "text": "`baggage` · W3C Baggage (max 64 entries, 8 KB cap) · OTel propagation across spans"
              }
            ],
            [
              {
                "k": "text",
                "text": "`resource` · OTel `Resource { service_name, attrs }` · OTel resource attributes, span enrichment"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Migration of existing kernel types"
            },
            {
              "k": "text",
              "text": " (lands in S1-C):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "MemoryId(u128)"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "MemoryId(Uuid)"
              },
              {
                "k": "text",
                "text": " UUIDv7-backed. "
              },
              {
                "k": "code",
                "text": "Display"
              },
              {
                "k": "text",
                "text": " becomes\n"
              },
              {
                "k": "code",
                "text": "mem-{uuid}"
              },
              {
                "k": "text",
                "text": ". Existing serde format moves to UUIDv7 string. "
              },
              {
                "k": "strong",
                "text": "Pre-S1-C\ncallers will need to be re-codegened"
              },
              {
                "k": "text",
                "text": " (mock IDs in tests included)."
              }
            ],
            [
              {
                "k": "code",
                "text": "TaskId(String)"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "TaskId(Uuid)"
              },
              {
                "k": "text",
                "text": " UUIDv7-backed. Validation on parse."
              }
            ],
            [
              {
                "k": "code",
                "text": "InferResponse.cost_usd: Option<f64>"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "cost: Option<Cost>"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "TokenUsage"
              },
              {
                "k": "text",
                "text": " 4-field → 18-field expansion (reasoning, thinking,\ncache_creation, image*, audio*, video, tool_use, ...). All new fields\n"
              },
              {
                "k": "code",
                "text": "Option<u64>"
              },
              {
                "k": "text",
                "text": " so producers opt in. "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " makes the\nexpansion non-breaking."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Cost arithmetic"
            },
            {
              "k": "text",
              "text": " (concrete shape):"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[non_exhaustive]\npub struct Cost(i128); // nano-USD: 1 USD = 1_000_000_000\n\nimpl Cost {\n    pub const ZERO: Cost;\n    pub fn from_usd(usd: f64) -> Cost; // saturating\n    pub fn from_nano_usd(n: i128) -> Cost;\n    pub fn as_nano_usd(&self) -> i128;\n    pub fn as_usd(&self) -> f64; // lossy display\n}\n\nimpl Add for Cost { ... }\nimpl Sub for Cost { ... }\nimpl Mul<u64> for Cost { ... }\nimpl Display for Cost { /* \"$X.XXXXXX\" */ }"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "i128 gives a range of ±1.7e29 nano-USD = ±170 quintillion USD. Enough\nfor any conceivable aggregation horizon."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "TrustLevel lattice"
            },
            {
              "k": "text",
              "text": " (concrete shape):"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[non_exhaustive]\npub struct TrustLevel(u8); // Untrusted=0, LocalTrusted=128, Trusted=255\n\nimpl TrustLevel {\n    pub const UNTRUSTED: Self;\n    pub const LOCAL_TRUSTED: Self;\n    pub const TRUSTED: Self;\n    pub fn meet(self, other: Self) -> Self; // min\n    pub fn join(self, other: Self) -> Self; // max\n    pub fn is_at_least(self, threshold: Self) -> bool;\n}\n\nimpl Display for TrustLevel { ... }\nimpl FromStr for TrustLevel { ... }\nimpl Serialize / Deserialize for TrustLevel { ... }"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Lattice ops let Shield gate \"this tool requires "
            },
            {
              "k": "code",
              "text": "Trusted"
            },
            {
              "k": "text",
              "text": " data\" with a\ncompile-time-checkable contract."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "UUIDv7"
            },
            {
              "k": "text",
              "text": " is chosen over UUIDv4 because:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "Time-sortable: natural chronological ordering for memory queries,\nevent logs, task lookup."
              }
            ],
            [
              {
                "k": "text",
                "text": "Random suffix preserves uniqueness at high creation rates."
              }
            ],
            [
              {
                "k": "text",
                "text": "Wire-compatible with "
              },
              {
                "k": "code",
                "text": "uuid::Uuid"
              },
              {
                "k": "text",
                "text": " ecosystem."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " adds "
            },
            {
              "k": "code",
              "text": "uuid = { version = \"1\", features = [\"v7\", \"serde\"] }"
            },
            {
              "k": "text",
              "text": "\nto its dep list (S1-C lands the dep)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Single L0 vocabulary across all higher layers — no more \"what's a\nTrustLevel\" ambiguity."
              }
            ],
            [
              {
                "k": "text",
                "text": "Cost arithmetic moves from f64 (drift) to i128 (exact) — billing\nbecomes auditable."
              }
            ],
            [
              {
                "k": "text",
                "text": "UUIDv7 IDs give natural chronological ordering for memory queries\nand event logs without a parallel timestamp field."
              }
            ],
            [
              {
                "k": "text",
                "text": "TokenUsage expansion (4 → 18 fields) covers every billable axis\ncurrent providers expose. New axes land additively under\n"
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "TrustLevel becomes a typed value with lattice ops; Shield can express\n\"tool X requires "
              },
              {
                "k": "code",
                "text": "at_least(LocalTrusted)"
              },
              {
                "k": "text",
                "text": "\" as code, not docs."
              }
            ],
            [
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": " give ADR-019's three retry layers a\nshared vocabulary."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "23 new types is a bigger admit than typical for L0. Mitigated by\ntopological ordering in S1-C (hash → schema → id → baggage → trust →\nretry → budget → cost → resource)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Migrating "
              },
              {
                "k": "code",
                "text": "MemoryId"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "TaskId"
              },
              {
                "k": "text",
                "text": " to UUIDv7 changes their wire\nformat (existing snapshots in tests must be refreshed). Pre-emption:\nno production data exists yet (Diamond is orphan-branch, no users)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " gains a "
              },
              {
                "k": "code",
                "text": "uuid"
              },
              {
                "k": "text",
                "text": " dependency. Acceptable — "
              },
              {
                "k": "code",
                "text": "uuid"
              },
              {
                "k": "text",
                "text": " is\nstd-only with "
              },
              {
                "k": "code",
                "text": "serde"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "v7"
              },
              {
                "k": "text",
                "text": " features; no transitive runtime."
              }
            ],
            [
              {
                "k": "text",
                "text": "TokenUsage 18-field struct is wider; serialization size grows.\nMitigated: all new fields are "
              },
              {
                "k": "code",
                "text": "Option<u64>"
              },
              {
                "k": "text",
                "text": " and producers omit them\nby default."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " already crosses 184 tests (largest crate by test\ncount); adding ~70-90 more keeps it under reasonable test budget."
              }
            ],
            [
              {
                "k": "text",
                "text": "Some types (RetryConfig, BudgetDirective) live in "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": "\neven though \"error\" is not literally what they describe. This is\nconsistent with "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " being the de-facto \"L0 foundation\"\ncrate. We may rename it to "
              },
              {
                "k": "code",
                "text": "nika-foundation"
              },
              {
                "k": "text",
                "text": " at v0.82+ if friction\nappears."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:247-256"
              },
              {
                "k": "text",
                "text": " — current 4-field\nTokenUsage."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:307"
              },
              {
                "k": "text",
                "text": " — current\n"
              },
              {
                "k": "code",
                "text": "cost_usd: Option<f64>"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs:21-22"
              },
              {
                "k": "text",
                "text": " — current "
              },
              {
                "k": "code",
                "text": "MemoryId(u128)"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/types.rs"
              },
              {
                "k": "text",
                "text": " — current "
              },
              {
                "k": "code",
                "text": "TaskId(String)"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/codes.rs"
              },
              {
                "k": "text",
                "text": " — Shield NIKA-380..389 (TrustLevel\nconsumers)."
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — Pattern 2\n("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": "), INV-019 (per-crate "
              },
              {
                "k": "code",
                "text": "new()"
              },
              {
                "k": "text",
                "text": " ctor), INV-025\n(verb error enums "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-019 — names "
              },
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": " as the shared\nretry vocabulary that lands here."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-034 — L0.5 traits expansion that depends on these L0 types\n(IdGenerator returns "
              },
              {
                "k": "code",
                "text": "Uuid"
              },
              {
                "k": "text",
                "text": ", BillingSink takes "
              },
              {
                "k": "code",
                "text": "Cost"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- Keep `cost_usd` as `f64`",
          "id": "alt-a----keep-cost_usd-as-f64"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Floats are simpler.\"\nRejected: f64 accumulation across thousands of provider calls drifts\nin the 7th decimal. Auditable billing is not negotiable. i128\nnano-USD is exact and trivially comparable."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- UUIDv4 (random only)",
          "id": "alt-b----uuidv4-random-only"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Standard, well-supported.\"\nRejected: not time-sortable. Memory queries that want\n\"recent first\" need a separate timestamp; UUIDv7 collapses both into\none column. UUIDv7 is also already an RFC (9562, 2024-05)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- Types stay in `nika-kernel`",
          "id": "alt-c----types-stay-in-nika-kernel"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Don't add a new home.\"\nRejected: L1 crates (nika-http, nika-fs) need "
            },
            {
              "k": "code",
              "text": "Cost"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "TrustLevel"
            },
            {
              "k": "text",
              "text": "\nbut must not depend on L0.5 kernel (per ADR-006 layering and\n"
            },
            {
              "k": "code",
              "text": "scripts/ci/check-layering.sh"
            },
            {
              "k": "text",
              "text": "). L0 "
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " is the only\ncommon ancestor."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- Separate `nika-types` or `nika-foundation` crate",
          "id": "alt-d----separate-nika-types-or-nika-foundation-crate"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Logically distinct from errors.\"\nRejected: would proliferate L0 crates without functional gain.\n"
            },
            {
              "k": "code",
              "text": "nika-error"
            },
            {
              "k": "text",
              "text": " already has zero deps beyond std + serde + thiserror;\nadding types keeps the dep graph flat. The name is suboptimal but\nnot load-bearing — we may rename later."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt E -- Decimal floats (e.g., `decimal_rs::Decimal`)",
          "id": "alt-e----decimal-floats-eg-decimal_rsdecimal"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Exact arithmetic without going to integer.\"\nRejected: brings a non-trivial third-party dep into L0 (the strictest\nlayer). i128 gives the same exactness with zero deps."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-005 — error hierarchy. ADR-033 lives in the same crate\n("
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": ") but adds value types alongside the existing error\ncodes."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants. Pattern 2\n("
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": ") is applied to every new struct/enum."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-019 — retry + timeout ownership. ADR-019 named\n"
              },
              {
                "k": "code",
                "text": "RetryConfig"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ErrorCategory"
              },
              {
                "k": "text",
                "text": " as the shared vocabulary; ADR-033\nis where they actually live."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-034 — L0.5 kernel traits expansion. ADR-034's traits\n(IdGenerator, BillingSink, ...) consume ADR-033's types."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups (S1-C and beyond):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Migrate "
              },
              {
                "k": "code",
                "text": "MemoryId(u128)"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "MemoryId(Uuid)"
              },
              {
                "k": "text",
                "text": " UUIDv7-backed (S1-C)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Migrate "
              },
              {
                "k": "code",
                "text": "TaskId(String)"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "TaskId(Uuid)"
              },
              {
                "k": "text",
                "text": " UUIDv7-backed (S1-C)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Replace "
              },
              {
                "k": "code",
                "text": "InferResponse.cost_usd: Option<f64>"
              },
              {
                "k": "text",
                "text": " with\n"
              },
              {
                "k": "code",
                "text": "cost: Option<Cost>"
              },
              {
                "k": "text",
                "text": " (S1-C)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Wire "
              },
              {
                "k": "code",
                "text": "TrustLevel"
              },
              {
                "k": "text",
                "text": " into Shield gating when verb-* crates ship."
              }
            ],
            [
              {
                "k": "text",
                "text": "Consider renaming "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "nika-foundation"
              },
              {
                "k": "text",
                "text": " at v0.82+ if\nthe \"errors crate that holds value types\" framing causes friction."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-034",
      "file": "adr-034-l05-kernel-traits-expansion.md",
      "sections": [
        {
          "title": "Boundary statement (read first)",
          "anchor": "boundary-statement-read-first"
        },
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 1950,
      "sha256": "18405e0546b9492aea906208a25845739e318565f792e95d4c8c1a10572dc71a",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-034: L0.5 kernel traits expansion: 6 new traits scoped to v0.81+ additions",
          "id": "adr-034-l05-kernel-traits-expansion-6-new-traits-scoped-to-v081-additions"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Boundary statement (read first)",
          "id": "boundary-statement-read-first"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "This ADR does NOT supersede ADR-006."
            },
            {
              "k": "text",
              "text": " It is scoped to the "
            },
            {
              "k": "strong",
              "text": "6 NEW v0.81+\ntraits"
            },
            {
              "k": "text",
              "text": " listed below ("
            },
            {
              "k": "code",
              "text": "IdGenerator"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "SecretResolver"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "MetricsExporter"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "TracerProvider"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "BillingSink"
            },
            {
              "k": "text",
              "text": ") plus the planned\n"
            },
            {
              "k": "code",
              "text": "MemoryUpdate"
            },
            {
              "k": "text",
              "text": " sub-trait. ADR-006 remains authoritative for the "
            },
            {
              "k": "strong",
              "text": "v0.80\ntraits"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "Clock"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Fs*"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Http*"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Shell*"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "BlobStore"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "Provider*"
            },
            {
              "k": "text",
              "text": ",\n"
            },
            {
              "k": "code",
              "text": "Memory*"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ToolExecutor"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "ContextCompressor"
            },
            {
              "k": "text",
              "text": ")."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Scope · Authoritative ADR"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.80 traits: Clock, Fs*, Http*, Shell*, BlobStore, Provider*, Memory*, ToolExecutor, ContextCompressor · **ADR-006**"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.81 stub traits: WasmPluginHost, Sandbox, ObservabilitySink · **ADR-020** (WASM/Sandbox), **(unified ObservabilitySink lives in S1-B; split planned at v0.100, see below)**"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.81 cancellation primitive: CancelCtx (struct, not a trait) · **ADR-016**"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.81+ NEW traits: IdGenerator, SecretResolver, MetricsExporter, TracerProvider, EventSink, BillingSink, MemoryUpdate · **ADR-034 (this)**"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-034 frontmatter lists "
            },
            {
              "k": "code",
              "text": "amends: [\"ADR-006\"]"
            },
            {
              "k": "text",
              "text": " because the L0.5 trait\ninventory grows; ADR-006's trait list itself is not changed."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The kernel today ("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/src/lib.rs:48-66"
            },
            {
              "k": "text",
              "text": ") declares 13\nmodules and ~28 public traits. ADR-006 froze that v0.80 list. New\nsessions (Phase D Session 4 — HTTP API, DataSource, MCP lifecycle;\nPhase E2 — TOML pricing) need additional injection points that v0.80\ndid not anticipate."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Existing patterns to mirror (verified):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "All async kernel traits use "
              },
              {
                "k": "code",
                "text": "#[trait_variant::make(XxxDyn: Send)]"
              },
              {
                "k": "text",
                "text": "\nfor object-safe "
              },
              {
                "k": "code",
                "text": "Dyn"
              },
              {
                "k": "text",
                "text": " companions (e.g.,\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/clock.rs:27"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/context.rs:48"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:444"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs:268"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "All public types are "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " per ADR-007."
              }
            ],
            [
              {
                "k": "text",
                "text": "All public structs have a "
              },
              {
                "k": "code",
                "text": "pub fn new()"
              },
              {
                "k": "text",
                "text": " constructor per INV-019."
              }
            ],
            [
              {
                "k": "text",
                "text": "All async traits require "
              },
              {
                "k": "code",
                "text": "Send + Sync"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Mocks live in "
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/"
              },
              {
                "k": "text",
                "text": ". Pattern:\n"
              },
              {
                "k": "code",
                "text": "#[derive(Clone, Debug, Default)]"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "pub fn new() -> Self"
              },
              {
                "k": "text",
                "text": ",\ntrait impl with no-op or deterministic behavior, inline tests for\nSend+Sync + behavior. See "
              },
              {
                "k": "code",
                "text": "NullMemoryStore"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/memory.rs:14-41"
              },
              {
                "k": "text",
                "text": "),\n"
              },
              {
                "k": "code",
                "text": "NullContextCompressor"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/compressor.rs"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-014 sealing policy applies per-trait. The v0.81 expansion needs an\nexplicit sealing decision for each new trait."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "INV-024 (\"EventKind emission from exactly 1 site per verb path\") makes\nthe case for a typed "
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": ": events are scattered across stdout\nwrites today; one trait makes the single emission site machine-checkable."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Add 6 new L0.5 traits to `nika-kernel` at v0.81 (S1-C)."
            },
            {
              "k": "text",
              "text": " All follow\nthe existing patterns. Each has a paired mock in "
            },
            {
              "k": "code",
              "text": "nika-kernel-mock"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "1. `IdGenerator` (sync, sealed = NO / **open**)",
          "id": "1-idgenerator-sync-sealed--no--open"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "pub trait IdGenerator: Send + Sync {\n    fn next_id(&self) -> Uuid; // UUIDv7 in production, sequential in tests\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Why:"
            },
            {
              "k": "text",
              "text": " ID generation today uses "
            },
            {
              "k": "code",
              "text": "Uuid::now_v7()"
            },
            {
              "k": "text",
              "text": " scattered through\nthe codebase. Tests that compare against goldens cannot use exact\nmatching. "
            },
            {
              "k": "code",
              "text": "IdGenerator"
            },
            {
              "k": "text",
              "text": " lets tests inject a "
            },
            {
              "k": "code",
              "text": "SequentialIdGenerator"
            },
            {
              "k": "text",
              "text": "\nwith deterministic output. "
            },
            {
              "k": "strong",
              "text": "Sync"
            },
            {
              "k": "text",
              "text": " because ID gen is never I/O."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Open"
            },
            {
              "k": "text",
              "text": " so users can plug in a custom monotonic generator (e.g.,\nSnowflake-style for cross-process ordering)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Mock: "
            },
            {
              "k": "code",
              "text": "SequentialIdGenerator { next: AtomicU64 }"
            },
            {
              "k": "text",
              "text": " increments a counter,\nformats as a deterministic UUID."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "2. `SecretResolver` (async, sealed = YES)",
          "id": "2-secretresolver-async-sealed--yes"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[trait_variant::make(SecretResolverDyn: Send)]\npub trait SecretResolver: Send + Sync + sealed::Sealed {\n    async fn resolve(&self, key: &str) -> Result<Secret, SecretError>;\n}\n\npub struct Secret(zeroize::Zeroizing<String>); // zeros on drop\npub struct SecretRef(String);                  // public reference; safe to log"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Why:"
            },
            {
              "k": "text",
              "text": " Workflow YAML uses "
            },
            {
              "k": "code",
              "text": "${env.API_KEY}"
            },
            {
              "k": "text",
              "text": " references. Today the engine\ncalls "
            },
            {
              "k": "code",
              "text": "std::env::var"
            },
            {
              "k": "text",
              "text": " directly — no abstraction means no vault backend,\nno test in-memory store. "
            },
            {
              "k": "code",
              "text": "SecretResolver"
            },
            {
              "k": "text",
              "text": " is async because vault\nbackends (AWS, GCP, HashiCorp) are network-bound."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Sealed"
            },
            {
              "k": "text",
              "text": " because secret leakage via custom impls is a Shield\nexfiltration risk — this is a security boundary; community impls go\nthrough pck protocol with audit."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "Secret"
            },
            {
              "k": "text",
              "text": " wraps "
            },
            {
              "k": "code",
              "text": "zeroize::Zeroizing<String>"
            },
            {
              "k": "text",
              "text": " so credentials zero on\ndrop. "
            },
            {
              "k": "code",
              "text": "SecretRef"
            },
            {
              "k": "text",
              "text": " is a separate type that carries only the "
            },
            {
              "k": "em",
              "text": "reference"
            },
            {
              "k": "text",
              "text": "\n(safe to log)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Mock: "
            },
            {
              "k": "code",
              "text": "NullSecretResolver"
            },
            {
              "k": "text",
              "text": " returns "
            },
            {
              "k": "code",
              "text": "SecretError::NotFound"
            },
            {
              "k": "text",
              "text": " for everything.\nA "
            },
            {
              "k": "code",
              "text": "MapSecretResolver { entries: BTreeMap<String, String> }"
            },
            {
              "k": "text",
              "text": " mock for\ndeterministic tests."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "3. `MetricsExporter` (async, sealed = NO / **open**)",
          "id": "3-metricsexporter-async-sealed--no--open"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[trait_variant::make(MetricsExporterDyn: Send)]\npub trait MetricsExporter: Send + Sync {\n    async fn counter(&self, name: &str, value: u64, labels: &[(String, String)]);\n    async fn gauge(&self, name: &str, value: f64, labels: &[(String, String)]);\n    async fn histogram(&self, name: &str, value: f64, labels: &[(String, String)]);\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Why:"
            },
            {
              "k": "text",
              "text": " Splits from the unified "
            },
            {
              "k": "code",
              "text": "ObservabilitySink"
            },
            {
              "k": "text",
              "text": " (S1-B,\n"
            },
            {
              "k": "code",
              "text": "crates/nika-kernel/src/observability.rs"
            },
            {
              "k": "text",
              "text": "). v0.95 keeps the unified\nsink for development; v0.100 introduces dedicated metrics + tracing\ntraits because backends (Prometheus vs OTel vs StatsD) want different\nshapes."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Open"
            },
            {
              "k": "text",
              "text": " so users can plug in alternative metrics backends."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Mock: "
            },
            {
              "k": "code",
              "text": "NullMetricsExporter"
            },
            {
              "k": "text",
              "text": " (no-op), "
            },
            {
              "k": "code",
              "text": "RecordingMetricsExporter"
            },
            {
              "k": "text",
              "text": " (captures\ncalls in "
            },
            {
              "k": "code",
              "text": "Vec"
            },
            {
              "k": "text",
              "text": " for assertions)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "4. `TracerProvider` (async, sealed = NO / **open**)",
          "id": "4-tracerprovider-async-sealed--no--open"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[trait_variant::make(TracerProviderDyn: Send)]\npub trait TracerProvider: Send + Sync {\n    async fn start_span(&self, name: &str, parent: Option<SpanId>) -> SpanId;\n    async fn end_span(&self, span: SpanId);\n    fn inject(&self, span: SpanId, carrier: &mut dyn TraceCarrier);\n    fn extract(&self, carrier: &dyn TraceCarrier) -> Option<SpanId>;\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Why:"
            },
            {
              "k": "text",
              "text": " Splits from "
            },
            {
              "k": "code",
              "text": "ObservabilitySink"
            },
            {
              "k": "text",
              "text": ". OTel TracerProvider shape.\nInject/extract enables W3C trace propagation across HTTP boundaries."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Open"
            },
            {
              "k": "text",
              "text": " so users can ship to alternative trace backends (Jaeger,\nTempo, Honeycomb)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Mock: "
            },
            {
              "k": "code",
              "text": "NullTracerProvider"
            },
            {
              "k": "text",
              "text": " (no-op, returns synthetic SpanIds)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "5. `EventSink` (async, sealed = YES)",
          "id": "5-eventsink-async-sealed--yes"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[trait_variant::make(EventSinkDyn: Send)]\npub trait EventSink: Send + Sync + sealed::Sealed {\n    async fn emit(&self, event: WorkflowEvent) -> Result<(), EventSinkError>;\n    async fn flush(&self) -> Result<(), EventSinkError>;\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Why:"
            },
            {
              "k": "text",
              "text": " INV-024 mandates \"EventKind emission from exactly 1 site per\nverb path\". "
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": " is that single site. Today events are written\nto stdout/file via direct calls — "
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": " lets tests assert on\nemitted events without parsing stdout."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Sealed"
            },
            {
              "k": "text",
              "text": " because workflow event taxonomy is engine-controlled; users\nshould not invent new variants. New event sinks (OTLP, Kafka) ship via\npck protocol."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Mock: "
            },
            {
              "k": "code",
              "text": "NullEventSink"
            },
            {
              "k": "text",
              "text": " (drops events), "
            },
            {
              "k": "code",
              "text": "RecordingEventSink"
            },
            {
              "k": "text",
              "text": " (captures\nevents in a "
            },
            {
              "k": "code",
              "text": "parking_lot::Mutex<Vec<WorkflowEvent>>"
            },
            {
              "k": "text",
              "text": " for assertions)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "6. `BillingSink` (async, sealed = YES)",
          "id": "6-billingsink-async-sealed--yes"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "#[trait_variant::make(BillingSinkDyn: Send)]\npub trait BillingSink: Send + Sync + sealed::Sealed {\n    async fn record_cost(\n        &self,\n        task_id: TaskId,\n        cost: Cost,\n        provider_id: ProviderId,\n        model_id: ModelId,\n    ) -> Result<(), BillingSinkError>;\n\n    async fn flush(&self) -> Result<(), BillingSinkError>;\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Why:"
            },
            {
              "k": "text",
              "text": " Cost tracking has fundamentally different reliability\nrequirements from telemetry — billing must not be lossy. Separate\ntrait so backends can implement write-ahead logging, exactly-once\nsemantics, or batch-flush durability without entangling with the\nmetrics path."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Sealed"
            },
            {
              "k": "text",
              "text": " because billing is a contract surface (we are accountable\nfor cost reporting accuracy); custom impls would risk audit gaps."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Mock: "
            },
            {
              "k": "code",
              "text": "NullBillingSink"
            },
            {
              "k": "text",
              "text": " (no-op for tests that don't care),\n"
            },
            {
              "k": "code",
              "text": "RecordingBillingSink"
            },
            {
              "k": "text",
              "text": " (captures + sums for assertions)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "7. `MemoryUpdate` sub-trait + `MemoryStore` evolution (deferred)",
          "id": "7-memoryupdate-sub-trait--memorystore-evolution-deferred"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The existing "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": " blanket\n("
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/memory.rs:289-290"
            },
            {
              "k": "text",
              "text": ") is\n"
            },
            {
              "k": "code",
              "text": "MemoryRemember + MemoryRecall + MemoryForget"
            },
            {
              "k": "text",
              "text": ". Cortex (v0.95) needs\nan "
            },
            {
              "k": "code",
              "text": "update()"
            },
            {
              "k": "text",
              "text": " operation (patch a memory frame in place). Plan:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "pub trait MemoryUpdate: Send + Sync {\n    async fn update(&self, id: MemoryId, patch: MemoryFramePatch)\n        -> Result<(), MemoryError>;\n}\n\n// Default blanket impl: returns Err(MemoryError::Unavailable)\nimpl<T: ?Sized + Send + Sync> MemoryUpdate for T {\n    default async fn update(&self, _: MemoryId, _: MemoryFramePatch)\n        -> Result<(), MemoryError>\n    {\n        Err(MemoryError::Unavailable { reason: \"update not implemented\".into() })\n    }\n}\n\n// Augmented blanket:\npub trait MemoryStore:\n    MemoryRemember + MemoryRecall + MemoryForget + MemoryUpdate {}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Deferred to v0.95 Cortex."
            },
            {
              "k": "text",
              "text": " Specialization-style default impls are\nnot stable; we will land this at v0.95 with concrete pattern\n(probably an extension trait, not specialization). The decision\n"
            },
            {
              "k": "em",
              "text": "to add it"
            },
            {
              "k": "text",
              "text": " is locked here so consumers know the shape."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Sealing pattern (lands in S1-C)",
          "id": "sealing-pattern-lands-in-s1-c"
        },
        {
          "k": "code",
          "lang": "rust",
          "text": "// crates/nika-kernel-core/src/sealed.rs\nmod sealed {\n    pub trait Sealed {}\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "S1-C applies "
            },
            {
              "k": "code",
              "text": ": sealed::Sealed"
            },
            {
              "k": "text",
              "text": " supertrait to: "
            },
            {
              "k": "code",
              "text": "Provider"
            },
            {
              "k": "text",
              "text": " (existing),\n"
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": " (new), "
            },
            {
              "k": "code",
              "text": "BillingSink"
            },
            {
              "k": "text",
              "text": " (new), "
            },
            {
              "k": "code",
              "text": "SecretResolver"
            },
            {
              "k": "text",
              "text": " (new). Other\ntraits stay open."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "INV-024 (\"single emission site per verb path\") becomes\nmachine-checkable — only "
              },
              {
                "k": "code",
                "text": "EventSink::emit"
              },
              {
                "k": "text",
                "text": " writes events; everything\nelse is a syntax error."
              }
            ],
            [
              {
                "k": "text",
                "text": "Tests can inject deterministic "
              },
              {
                "k": "code",
                "text": "IdGenerator"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "SequentialClock"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "MapSecretResolver"
              },
              {
                "k": "text",
                "text": " — golden tests use exact comparison."
              }
            ],
            [
              {
                "k": "text",
                "text": "Billing path is structurally separated from telemetry — no risk of\nsampling losing cost data."
              }
            ],
            [
              {
                "k": "text",
                "text": "Sealing on "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EventSink"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "BillingSink"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "SecretResolver"
              },
              {
                "k": "text",
                "text": "\ncloses four sensitive contract surfaces; "
              },
              {
                "k": "code",
                "text": "MetricsExporter"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "TracerProvider"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "IdGenerator"
              },
              {
                "k": "text",
                "text": " stay open for community backends."
              }
            ],
            [
              {
                "k": "text",
                "text": "Mock crate keeps 1:1 trait coverage — no \"trait without mock\" gaps."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "6 new traits + 6 new mocks = ~600 LOC added to L0.5. Mitigated\nbecause most are small (mocks are 30-50 LOC each, traits are\n10-30 LOC each)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Sealing pattern adds a "
              },
              {
                "k": "code",
                "text": "sealed.rs"
              },
              {
                "k": "text",
                "text": " module; new contributors need to\nunderstand why they cannot impl "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": " (answer: pck protocol).\nDocumented in module-level comment."
              }
            ],
            [
              {
                "k": "code",
                "text": "Secret"
              },
              {
                "k": "text",
                "text": " requires "
              },
              {
                "k": "code",
                "text": "zeroize"
              },
              {
                "k": "text",
                "text": " dep on "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": ". Lightweight, but\none more dep. Acceptable — "
              },
              {
                "k": "code",
                "text": "zeroize"
              },
              {
                "k": "text",
                "text": " has zero transitive deps."
              }
            ],
            [
              {
                "k": "code",
                "text": "MemoryUpdate"
              },
              {
                "k": "text",
                "text": " deferred to v0.95 means today's "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": "\ncontract is "
              },
              {
                "k": "strong",
                "text": "not"
              },
              {
                "k": "text",
                "text": " complete. Acceptable — "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " is itself\na kernel hook for v0.95, not exposed to users yet."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ObservabilitySink (S1-B) stays as the v0.95 unified path.\n"
              },
              {
                "k": "code",
                "text": "MetricsExporter"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "TracerProvider"
              },
              {
                "k": "text",
                "text": " are the v0.100 split. Both\ncoexist; users opt into the split when v0.100 ships."
              }
            ],
            [
              {
                "k": "text",
                "text": "Sync vs async choice per trait follows the existing kernel rule\n(sync where I/O-free, async where backends are network-bound)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel/src/lib.rs:48-66"
              },
              {
                "k": "text",
                "text": " — current module list (S1-B\nstate: 17 modules)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/clock.rs:27"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "trait_variant::make"
              },
              {
                "k": "text",
                "text": " pattern."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/context.rs:48"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "trait_variant::make"
              },
              {
                "k": "text",
                "text": "\npattern."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/provider.rs:444-454"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": " trait\nfamily (will gain "
              },
              {
                "k": "code",
                "text": ": sealed::Sealed"
              },
              {
                "k": "text",
                "text": " in S1-C)."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs:289-290"
              },
              {
                "k": "text",
                "text": " — current "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": "\nblanket (will gain "
              },
              {
                "k": "code",
                "text": "MemoryUpdate"
              },
              {
                "k": "text",
                "text": " at v0.95)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Telemetry traits: unified ObservabilitySink stub (S1-B, kernel src file\nlater deleted) was dropped per Q12 rev.3 (2026-04-16) before any v0.100\nsplit; replacement lives in-tree at\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/infra/metrics.rs"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "MetricsExporter"
              },
              {
                "k": "text",
                "text": "),\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/infra/trace.rs"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "TracerProvider"
              },
              {
                "k": "text",
                "text": "),\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/infra/audit.rs"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "AuditSink"
              },
              {
                "k": "text",
                "text": "),\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/infra/event_sink.rs"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "EventSink"
              },
              {
                "k": "text",
                "text": "), and\n"
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/infra/billing.rs"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "BillingSink"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-mock/src/lib.rs"
              },
              {
                "k": "text",
                "text": " — mock module list (mirrors\nevery kernel trait)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-006 — v0.80 trait set, "
              },
              {
                "k": "strong",
                "text": "not superseded"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-014 — sealing policy applied per-trait above."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 — L0 types consumed by these traits ("
              },
              {
                "k": "code",
                "text": "Cost"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "Uuid"
              },
              {
                "k": "text",
                "text": "-backed\nIDs)."
              }
            ],
            [
              {
                "k": "text",
                "text": "INV-024 — \"single emission site per verb path\" (motivates "
              },
              {
                "k": "code",
                "text": "EventSink"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "INV-019 — "
              },
              {
                "k": "code",
                "text": "pub fn new()"
              },
              {
                "k": "text",
                "text": " constructor on every public struct."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A -- No `IdGenerator`, use `Uuid::now_v7()` directly",
          "id": "alt-a----no-idgenerator-use-uuidnow_v7-directly"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Don't over-abstract.\"\nRejected: every golden-file test that includes IDs would need regex\nmatching instead of exact comparison. Determinism is worth the trait."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B -- Single `TelemetrySink` trait covering metrics, traces, billing",
          "id": "alt-b----single-telemetrysink-trait-covering-metrics-traces-billing"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Unify the observability surface.\"\nRejected: billing has different reliability contracts (durable, no\nsampling). Traces and metrics tolerate sampling and best-effort\ndelivery. One trait would either over-promise on traces or\nunder-promise on billing. Three traits = three contracts, each met."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C -- `EventSink` as part of `ObservabilitySink`",
          "id": "alt-c----eventsink-as-part-of-observabilitysink"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Events are observability.\"\nRejected: "
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": " operates at the workflow level (task started,\ntask completed, tool invoked). "
            },
            {
              "k": "code",
              "text": "ObservabilitySink"
            },
            {
              "k": "text",
              "text": " operates at the\ninfrastructure level (spans, metrics). Different abstraction\nlayers; combining them blurs the contract."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt D -- `MemoryUpdate` as separate trait, not added to `MemoryStore`",
          "id": "alt-d----memoryupdate-as-separate-trait-not-added-to-memorystore"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Don't change the blanket.\"\nConsidered. Rejected: every "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": " consumer would need a\nsecond trait bound ("
            },
            {
              "k": "code",
              "text": "T: MemoryStore + MemoryUpdate"
            },
            {
              "k": "text",
              "text": "). Adding it to the\nblanket with a default "
            },
            {
              "k": "code",
              "text": "Err(Unavailable)"
            },
            {
              "k": "text",
              "text": " impl is backward-compatible\n(existing "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": " impls continue to compile, get the default\nbehavior). We accept the small specialization complexity at v0.95."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt E -- Open all 6 traits (no sealing)",
          "id": "alt-e----open-all-6-traits-no-sealing"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "\"Trust the community.\"\nRejected: "
            },
            {
              "k": "code",
              "text": "Provider"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "EventSink"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "BillingSink"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "SecretResolver"
            },
            {
              "k": "text",
              "text": "\neach carry security or contract risk. Sealing forces structured pck\nplugin protocol with audit; open traits would let any crate impl\nthem with no review."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-006 — kernel ISP trait design. ADR-034 "
              },
              {
                "k": "strong",
                "text": "amends"
              },
              {
                "k": "text",
                "text": " ADR-006 by\nadding 6 new traits to the L0.5 inventory; ADR-006's trait list is\nunchanged."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-007 — forward-compat invariants. Pattern 1 (kernel traits\nupfront) is the rationale for landing these at v0.81."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-014 — sealed kernel traits. Per-trait sealing decisions made\nhere."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-016 — Cancellation. Future "
              },
              {
                "k": "code",
                "text": "Cancellable"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "CancelPropagator"
              },
              {
                "k": "text",
                "text": "\nv0.95 traits will live alongside these."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-020 — WASM plugin boundary. ObservabilitySink (S1-B) is the\nunified predecessor that splits into "
              },
              {
                "k": "code",
                "text": "MetricsExporter"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "TracerProvider"
              },
              {
                "k": "text",
                "text": " at v0.100."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-033 — L0 foundational types. "
              },
              {
                "k": "code",
                "text": "IdGenerator"
              },
              {
                "k": "text",
                "text": " returns "
              },
              {
                "k": "code",
                "text": "Uuid"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "BillingSink"
              },
              {
                "k": "text",
                "text": " takes "
              },
              {
                "k": "code",
                "text": "Cost"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "TaskId"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ProviderId"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ModelId"
              },
              {
                "k": "text",
                "text": ",\nall from "
              },
              {
                "k": "code",
                "text": "nika-error"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Follow-ups:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "S1-C lands the 6 traits + 6 mocks + "
              },
              {
                "k": "code",
                "text": "sealed.rs"
              },
              {
                "k": "text",
                "text": " + applies sealing\nto "
              },
              {
                "k": "code",
                "text": "Provider"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "EventSink"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "BillingSink"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "SecretResolver"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.95 Cortex lands "
              },
              {
                "k": "code",
                "text": "MemoryUpdate"
              },
              {
                "k": "text",
                "text": " sub-trait + augments "
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": "\nblanket."
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.100 splits "
              },
              {
                "k": "code",
                "text": "ObservabilitySink"
              },
              {
                "k": "text",
                "text": " into "
              },
              {
                "k": "code",
                "text": "MetricsExporter"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "TracerProvider"
              },
              {
                "k": "text",
                "text": " (both already defined here)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Implement "
              },
              {
                "k": "code",
                "text": "nika-secret-vault"
              },
              {
                "k": "text",
                "text": " (L1) when production secrets land."
              }
            ],
            [
              {
                "k": "text",
                "text": "Implement "
              },
              {
                "k": "code",
                "text": "nika-observability-otel"
              },
              {
                "k": "text",
                "text": " (L1) when OTel export is wired\nend-to-end."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-035",
      "file": "adr-035-telemetry-seams.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Empirical validation (what makes this \"merged code\")",
          "anchor": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "title": "See also",
          "anchor": "see-also"
        }
      ],
      "words": 401,
      "sha256": "13d3a17d06f8e49ba17043f38554d8d9c65108958f6a57337fb191c08d494f1d",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-035: Telemetry seams: SpanGuard parent_span_id + SpanRef (OTel-ready)",
          "id": "adr-035-telemetry-seams-spanguard-parent_span_id--spanref-otel-ready"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Status flipped "
            },
            {
              "k": "code",
              "text": "proposed → accepted"
            },
            {
              "k": "text",
              "text": " 2026-07-12 under the merged-code\nrule (the #474 ADR ruling): both reservations ship in\n"
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-core/src/infra/trace.rs"
            },
            {
              "k": "text",
              "text": " (the kernel's core member\n— the ADR predates the kernel descent; "
            },
            {
              "k": "code",
              "text": "nika-kernel"
            },
            {
              "k": "text",
              "text": " re-exports)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Wave 4B #1 (commit "
            },
            {
              "k": "code",
              "text": "861f09bc9"
            },
            {
              "k": "text",
              "text": ", 2026-04-17) extended the kernel\ntelemetry types with two additive reservations so span identity can\ntravel — across await points, channels, spawned tasks — without a\nthread-local stack (which the tokio runtime breaks) and without\ncloning a live guard."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Both shapes ship in "
            },
            {
              "k": "code",
              "text": "nika-kernel-core/src/infra/trace.rs"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "`SpanGuard.parent_span_id: Option<SpanId>`"
              },
              {
                "k": "text",
                "text": " — the explicit\nparent link. Nested spans stitch into a tree by data, not by\nambient state: the module doc states the law (« parent_span_id\nthreads parent→child links »), and the field is "
              },
              {
                "k": "code",
                "text": "Option"
              },
              {
                "k": "text",
                "text": " because a\nroot span honestly has no parent — this is the one place an absent\nvalue IS the semantics, not a policy hole."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`SpanRef { trace_id, span_id }`"
              },
              {
                "k": "text",
                "text": " — the lightweight copyable\nhandle (with "
              },
              {
                "k": "code",
                "text": "::new()"
              },
              {
                "k": "text",
                "text": ", FCI #19) for propagating span identity\nacross boundaries where a "
              },
              {
                "k": "code",
                "text": "SpanGuard"
              },
              {
                "k": "text",
                "text": " cannot go: channel payloads,\nspawn captures, cross-process IPC. The shape deliberately mirrors\nOTel's "
              },
              {
                "k": "code",
                "text": "SpanContext"
              },
              {
                "k": "text",
                "text": " so a future bridge is a field-for-field map."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Both are "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " — OTel's "
            },
            {
              "k": "code",
              "text": "TraceFlags"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "TraceState"
            },
            {
              "k": "text",
              "text": " can\njoin "
            },
            {
              "k": "code",
              "text": "SpanRef"
            },
            {
              "k": "text",
              "text": " later without a major."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Thread-local span stack"
              },
              {
                "k": "text",
                "text": " — rejected: tokio task migration breaks\nambient parenting; the explicit field survives every executor."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Clone the `SpanGuard` across boundaries"
              },
              {
                "k": "text",
                "text": " — rejected: a guard's\nDrop is its END event; two owners means double-close or leaked\nspans. A "
              },
              {
                "k": "code",
                "text": "Copy"
              },
              {
                "k": "text",
                "text": "-able ref carries identity without lifecycle."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Adopt the `opentelemetry` crate types directly in L0.5"
              },
              {
                "k": "text",
                "text": " —\nrejected: the kernel stays zero-heavy-deps; mirroring the SHAPE\nkeeps the bridge zero-copy while the dependency stays out."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Empirical validation (what makes this \"merged code\")",
          "id": "empirical-validation-what-makes-this-merged-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-kernel-core/src/infra/trace.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "SpanRef"
              },
              {
                "k": "text",
                "text": " (line ~21, with\nconstructor), "
              },
              {
                "k": "code",
                "text": "parent_span_id: Option<SpanId>"
              },
              {
                "k": "text",
                "text": " on "
              },
              {
                "k": "code",
                "text": "SpanGuard"
              },
              {
                "k": "text",
                "text": "\n(line ~49), module-doc law at the top."
              }
            ],
            [
              {
                "k": "text",
                "text": "The OTel direction the reservation aimed at materialized\nprojection-side meanwhile: "
              },
              {
                "k": "code",
                "text": "nika trace export"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "nika-dap/src/otel.rs"
              },
              {
                "k": "text",
                "text": ") projects recorded journals to OTLP/JSON —\nconsuming the same span-identity vocabulary these types reserved."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "See also",
          "id": "see-also"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-028 — Forward-compat reservation policy (this ADR is an instance)."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-034 — the tracer trait family that emits these shapes."
              }
            ],
            [
              {
                "k": "text",
                "text": "FCI-035 addendum — Wave 4A/4B reservation catalog."
              }
            ],
            [
              {
                "k": "text",
                "text": "NIKA-800..819 — Observability / telemetry error code range (reserved\nv0.80)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika trace export"
              },
              {
                "k": "text",
                "text": " — the shipped OTLP projection (journal-side)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-037",
      "file": "adr-037-bottom-up-diamond-progression.md",
      "sections": [
        {
          "title": "Status",
          "anchor": "status"
        },
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Cascade plan (post-acceptance)",
          "anchor": "cascade-plan-post-acceptance"
        },
        {
          "title": "See also",
          "anchor": "see-also"
        }
      ],
      "words": 1360,
      "sha256": "dbf820ae2e05c497a79571812f735de258ac27d78612e0665476e3208b8d4724",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-037: Bottom-up diamond progression",
          "id": "adr-037-bottom-up-diamond-progression"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "⚠️ AMENDED 2026-06-20 (D-2026-06-20-N1) · the VERSION-policy layer of this ADR is superseded."
            },
            {
              "k": "text",
              "text": "\nThe "
            },
            {
              "k": "strong",
              "text": "core decision is INTACT"
            },
            {
              "k": "text",
              "text": " · bottom-up layer progression · no\nfeature-milestone defer · every feature built as its layer is reached. What\nchanges · the \"forever-v0.x · no v1.0 target ever\" framing (ADR-002) is RETIRED\n→ real semver toward a "
            },
            {
              "k": "strong",
              "text": "1.0"
            },
            {
              "k": "text",
              "text": " launch. The "
            },
            {
              "k": "code",
              "text": "v0.8X.Y"
            },
            {
              "k": "text",
              "text": " layer-tag scheme below is a\nhistorical record · the engine is now "
            },
            {
              "k": "strong",
              "text": "0.90.0"
            },
            {
              "k": "text",
              "text": " (RC-grade) → "
            },
            {
              "k": "code",
              "text": "1.0.0-rc.N"
            },
            {
              "k": "text",
              "text": " →\n"
            },
            {
              "k": "strong",
              "text": "1.0.0"
            },
            {
              "k": "text",
              "text": " launch → 1.x minors add the remaining crates additively toward the\n42-crate target → "
            },
            {
              "k": "strong",
              "text": "2.0"
            },
            {
              "k": "text",
              "text": " = the Connectome era. The bottom-up "
            },
            {
              "k": "em",
              "text": "ordering"
            },
            {
              "k": "text",
              "text": " still\ngoverns which crate admits next; only the public version number now CLIMBS to\nsignal maturity instead of sitting at 0.x forever. Original body preserved per\ncross-source-validation §2.7 (zero silent rewrite)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Status",
          "id": "status"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Accepted"
            },
            {
              "k": "text",
              "text": " 2026-04-17 night post-brainstorm (Q-plan 1a/2a/3b/4b/5-both/6a). Amends ADR-028. Cascade: ROADMAP restructure, FCI-007 re-eval, crate count target revised 40-42 → 50-90 (cap 100 unchanged)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika Diamond shipped 6 admitted L0/L0.5 crates + 1 WIP ("
            },
            {
              "k": "code",
              "text": "nika-schema"
            },
            {
              "k": "text",
              "text": ") by 2026-04-17. Throughout the first 6 admissions, the ROADMAP carried arbitrary version gates:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "v0.81"
              },
              {
                "k": "text",
                "text": " — Foundation"
              }
            ],
            [
              {
                "k": "code",
                "text": "v0.90"
              },
              {
                "k": "text",
                "text": " — Ship (pck + providers)"
              }
            ],
            [
              {
                "k": "code",
                "text": "v0.95"
              },
              {
                "k": "text",
                "text": " — Cortex (9 memory satellites)"
              }
            ],
            [
              {
                "k": "code",
                "text": "v0.100"
              },
              {
                "k": "text",
                "text": " — Plugins (WASM host + sandbox)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "These gates encoded a "
            },
            {
              "k": "strong",
              "text": "feature-milestone scheduling"
            },
            {
              "k": "text",
              "text": " model: ship core, then Cortex later, then plugins later. In practice this produced:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Stubs"
              },
              {
                "k": "text",
                "text": " that pretend to implement reserved traits ("
              },
              {
                "k": "code",
                "text": "MemoryStore"
              },
              {
                "k": "text",
                "text": " in "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " with no satellite impl in sight until v0.95)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Version-labelled drift"
              },
              {
                "k": "text",
                "text": " — docs + specs referenced \"target design — arriving at v0.95\" as if it were a commitment."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Shadow zones"
              },
              {
                "k": "text",
                "text": " — the gap between \"reserved\" and \"implemented\" accumulated residue (invariants drift, type changes needed later)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Fake ambition"
              },
              {
                "k": "text",
                "text": " — any mention of \"we'll ship X at vN.M\" is a promise we cannot keep with forever-v0.x."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "User said 2026-04-17 night:"
            }
          ]
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "text",
              "text": "\"c'est une mauvaise chose de le faire en différé. on a pas la bonne logique v0.95 v0.100. je veux vraiment qu'on fasse toutes les features au fur et à mesure en partant du bas du diamant et on remonte.\""
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Translation: "
            },
            {
              "k": "strong",
              "text": "defer = bad logic. Build every feature as its layer is reached, from the bottom of the diamond upward."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Abandon feature-milestone scheduling. Adopt "
            },
            {
              "k": "strong",
              "text": "bottom-up layer progression"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "code",
          "lang": "text",
          "text": "L0    (~9 crates)     foundation types, pure, sync\n  ↓\nL0.5  (~2 crates)     kernel traits (sealed, ISP, async OK in defs)\n  ↓\nL1    (~20+ crates)   effect impls — fs, http, process, git, memory-*, provider-*\n  ↓\nL2    (~10+ crates)   verbs + domain services + memory orchestrator + pck\n  ↓\nL3    (~5 crates)     runtime + shield + wasm-host + sandbox\n  ↓\nL4    (~7 crates)     interfaces — cli, lsp, daemon, mcp, sdk, serve\n  ↓\nL5    (1 crate)       the `nika` binary"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Every crate admits atomically with 12 gates. Every admission = 1 commit = 1 "
            },
            {
              "k": "code",
              "text": "v0.80.N"
            },
            {
              "k": "text",
              "text": " tag increment."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "What changes",
          "id": "what-changes"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "ROADMAP.md restructure"
              },
              {
                "k": "text",
                "text": " — replace "
              },
              {
                "k": "code",
                "text": "v0.81/v0.90/v0.95/v0.100"
              },
              {
                "k": "text",
                "text": " section headers with layer-progression sections. No calendar targets. No \"this ships at vN.M\" language."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-028 re-scope"
              },
              {
                "k": "text",
                "text": " — distinguish:"
              },
              {
                "k": "text",
                "text": "- **API type reservation** (non_exhaustive ratchet, reserved fields in InferRequest/InferResponse, kernel trait surface) — KEEP. Legitimate forward-compat work.\n- **Feature scheduling** (we'll ship Cortex at v0.95) — REMOVE. Bad diamond."
              }
            ],
            [
              {
                "k": "strong",
                "text": "FCI-007 `unstable-cortex` re-evaluate"
              },
              {
                "k": "text",
                "text": " — if Cortex ships layer-by-layer without defer, we don't need a feature flag gating it. Either drop or repurpose as per-crate experimental opt-in."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Version tagging — Q-plan 3b LOCKED"
              },
              {
                "k": "text",
                "text": ": "
              },
              {
                "k": "code",
                "text": "v0.8X.Y"
              },
              {
                "k": "text",
                "text": " where X climbs per layer-phase completion, Y increments per admission within a phase."
              },
              {
                "k": "text",
                "text": "- `v0.80.N` → current baseline (L0 partial, L0.5 complete)\n- `v0.81` → L0 complete (9 crates admitted)\n- `v0.82` → L1a primitives complete (fs, process, http-client, keys-env)\n- `v0.83` → L1b deps-étage-1 (git, keys-keychain, catalog-sync)\n- `v0.84` → L1c providers (rig + openai-compat + mock)\n- `v0.85` → L1d pck backend (registry + store)\n- `v0.86` → L1e memory foundation (embed, store-fjall, autodesc, hnsw, bm25, rrf)\n- `v0.87` → L1f memory advanced (temporal, fsrs, rdfs-reasoner, graph-algos)\n- `v0.88` → L1g reserved satellites (as demand emerges: causal, episodic, working, meta, procedural, spatial)\n- `v0.89` → L2 complete (verbs, orchestrators, builtins)\n- `v0.90` → L3 complete (runtime, shield, wasm-host, sandbox). **NOT a \"ship\" tag** per Q-plan 6a.\n- `v0.91` → L4 complete (cli, daemon, serve, mcp, lsp, sdk)\n- `v0.92` → L5 binary first full-stack integration\n- `v0.93+` → polish, optimization, quality iterations\n- ~~Forever-v0.x — no v1.0 target ever.~~ (amended D-2026-06-20-N1 · real semver toward a 1.0 launch · this layer-tag scheme is now a historical record · see top banner)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Total crate count target"
              },
              {
                "k": "text",
                "text": " — Q-plan 1a LOCKED: 40-42 → "
              },
              {
                "k": "strong",
                "text": "50-90 crates"
              },
              {
                "k": "text",
                "text": " (cap 100 unchanged). Growth driven by 14-crate memory subsystem, 4 storage backends, ~30 provider crates, own "
              },
              {
                "k": "code",
                "text": "nika-embed"
              },
              {
                "k": "text",
                "text": ", WASM host + 3 sandbox crates."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Dedicated deep-dive sessions reserved"
              },
              {
                "k": "text",
                "text": " — Q-plan 5 LOCKED both:"
              },
              {
                "k": "text",
                "text": "- **Session nika-memory** — most-ambitious B1-B5 validated 2026-04-17 night. Design 14 crates (8 core + 6 reserved). Runs FIRST (stabilizes kernel memory traits before L1 memory admission).\n- **Session nika-registry** — backend + publishing policy. Runs after memory."
              }
            ],
            [
              {
                "k": "strong",
                "text": "\"v0.90 ship target\" DROPPED"
              },
              {
                "k": "text",
                "text": " — Q-plan 6a. Versions 0.9X are just layer tags, no marketing ceremony. (Amended D-2026-06-20-N1 · the "
              },
              {
                "k": "strong",
                "text": "1.0.0"
              },
              {
                "k": "text",
                "text": " launch is now the marketing moment · 0.9X stays RC-grade pre-launch · per top banner.)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "What stays the same",
          "id": "what-stays-the-same"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "12-gate admission model (unchanged)."
              }
            ],
            [
              {
                "k": "text",
                "text": "28+ invariants (all still binding)."
              }
            ],
            [
              {
                "k": "text",
                "text": "7 shadow zones (still pre-launch quality gates)."
              }
            ],
            [
              {
                "k": "code",
                "text": "publish = false"
              },
              {
                "k": "text",
                "text": " on foundation crates (ADR-022 unchanged)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The release model (ADR-002) — amended D-2026-06-20-N1 to real semver toward a 1.0 launch (was \"forever-v0.x · no v1.0 target\")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Ordering within a layer",
          "id": "ordering-within-a-layer"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Dependencies-first topological order. If two crates have no dep relation, tiebreaker:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "User-value-first"
              },
              {
                "k": "text",
                "text": " (which unlocks a demo or blocks downstream first)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "LOC-smallest-first"
              },
              {
                "k": "text",
                "text": " (easier wins early build momentum)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Effect-axis-smallest-first"
              },
              {
                "k": "text",
                "text": " (fewer capability axes = lower review burden)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Example L1 candidates in topological order:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-fs"
              },
              {
                "k": "text",
                "text": " (L1, reads-fs rw-fs, no deps on other L1)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-process"
              },
              {
                "k": "text",
                "text": " (L1, exec-shell, no deps)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-http-client"
              },
              {
                "k": "text",
                "text": " (L1, net-egress, no deps)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-git"
              },
              {
                "k": "text",
                "text": " (L1, depends on "
              },
              {
                "k": "code",
                "text": "nika-fs"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "nika-process"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-memory-*"
              },
              {
                "k": "text",
                "text": " (L1, each independent, 8 core + reserved)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-<provider>-*"
              },
              {
                "k": "text",
                "text": " (L1, depend on "
              },
              {
                "k": "code",
                "text": "nika-http-client"
              },
              {
                "k": "text",
                "text": ")"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Bottom-up integrity — no fake feature-milestones, no version-labelled residue (the anti-defer discipline survives the D-2026-06-20-N1 real-semver amendment; only the version labels changed, not the build order)."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Every admitted crate is REAL code, not stub. No \"v0.95 target design\" banners in docs."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Linear ROADMAP — users see \"what's admitted, what's coming up in this layer, what's above\"."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Tagging discipline — "
              },
              {
                "k": "code",
                "text": "v0.80.N"
              },
              {
                "k": "text",
                "text": " monotonic, no backtracking, no retroactive milestone rewrites."
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Harder to communicate \"when does Cortex ship\" — answer is \"when we reach L1/L2 memory satellites, and never before then\"."
              }
            ],
            [
              {
                "k": "text",
                "text": "❌ Existing docs (ROADMAP sections, spec drafts, adr-036 MSRV body) that reference "
              },
              {
                "k": "code",
                "text": "v0.90"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "v0.95"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "v0.100"
              },
              {
                "k": "text",
                "text": " need cascade edits."
              }
            ],
            [
              {
                "k": "text",
                "text": "⚠️ Some speculative scheduling (nika-sdk crates.io publishing, MSRV cadence) currently pinned to v0.90 — adr-036 must be reworded to say \"at whichever tag crosses L4 interface completion\" rather than \"v0.90\"."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Keep version-milestone gates but clarify they're goals not deadlines"
              },
              {
                "k": "text",
                "text": " — fails because docs still communicate false scheduling."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Hybrid: layer-progression as primary, version labels as secondary guidance"
              },
              {
                "k": "text",
                "text": " — fails because version labels re-encode the deferred-scheduling mindset."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Skip ADR-028 revision, just restructure ROADMAP"
              },
              {
                "k": "text",
                "text": " — fails because ADR-028 still uses \"reservation for v0.95 Cortex\" language that contradicts this ADR."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Cascade plan (post-acceptance)",
          "id": "cascade-plan-post-acceptance"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "Restructure ROADMAP.md (1 commit)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Re-scope ADR-028 (1 commit, Under Revision → Amended)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Re-evaluate FCI-007 (1 commit in "
              },
              {
                "k": "code",
                "text": "forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Update CLAUDE.md narrative (1 commit)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Reserve dedicated sessions for nika-memory + nika-registry (memory files, no commits)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Rework adr-036 MSRV body to remove v0.90 references (1 commit)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Constellation reconciliation report: Note Option A/B/C oxigraph rename deferred to nika-memory session (1 commit)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "See also",
          "id": "see-also"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-001 — Diamond orphan branch."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-002 — release model (amended D-2026-06-20-N1 · real semver toward 1.0 · was \"forever-v0.x\")."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-003 — 12-gate admission."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-004 — Context-window-sized crates."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-028 — Forward-compat reservation policy (amended by this ADR)."
              }
            ],
            [
              {
                "k": "code",
                "text": "philosophy_bottom_up_diamond_2026_04_17.md"
              },
              {
                "k": "text",
                "text": " in memory — full brainstorm record."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-038",
      "file": "adr-038-nika-bm25-admission.md",
      "sections": [
        {
          "title": "Status",
          "anchor": "status"
        },
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "12-Gate admission readiness (Wave 3 evidence anchors)",
          "anchor": "12-gate-admission-readiness-wave-3-evidence-anchors"
        },
        {
          "title": "Consumer signal",
          "anchor": "consumer-signal"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 1805,
      "sha256": "68dd8cf361fd1a207cd2b5dd8ff252a8534c553d3b5851393746f5a19ae876cc",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-038: Admit nika-bm25 as the first L1 memory satellite",
          "id": "adr-038-admit-nika-bm25-as-the-first-l1-memory-satellite"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Status",
          "id": "status"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Proposed"
            },
            {
              "k": "text",
              "text": " 2026-05-12 · pre-admission gate-readiness pass per Diamond Phase 1 entry plan §2 W2.4. Status flips to "
            },
            {
              "k": "strong",
              "text": "Accepted"
            },
            {
              "k": "text",
              "text": " when the W3 12-gate ceremony lands the crate in "
            },
            {
              "k": "code",
              "text": "crates/nika-bm25/"
            },
            {
              "k": "text",
              "text": " and registers it in the workspace "
            },
            {
              "k": "code",
              "text": "[workspace] members"
            },
            {
              "k": "text",
              "text": " array."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The Diamond memory subsystem architecture (locked separately in private studio doctrine and surfaced publicly via "
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/memory.rs:1-413"
            },
            {
              "k": "text",
              "text": ") ships as 1 L2 orchestrator ("
            },
            {
              "k": "code",
              "text": "nika-memory"
            },
            {
              "k": "text",
              "text": ") + 8 L1 satellites — "
            },
            {
              "k": "code",
              "text": "nika-hnsw"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-bm25"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-rrf"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-fsrs"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-graph-algos"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-rdfs-reasoner"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-temporal"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-autodesc"
            },
            {
              "k": "text",
              "text": " ★. Each satellite is independently admissible via the 12-gate ceremony (ADR-003) and publishable standalone on crates.io as a SuperNovae contribution to the Rust RDF/ML ecosystem."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Diamond Phase 1 entry plan §3 (W3 wave) "
            },
            {
              "k": "code",
              "text": "nika-bm25"
            },
            {
              "k": "text",
              "text": " is the "
            },
            {
              "k": "strong",
              "text": "simplest L1 satellite to admit first"
            },
            {
              "k": "text",
              "text": " because :"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "BM25 has "
              },
              {
                "k": "strong",
                "text": "zero deps beyond a hand-rolled scorer"
              },
              {
                "k": "text",
                "text": " (no Oxigraph integration yet · no embedding-model dep)"
              }
            ],
            [
              {
                "k": "text",
                "text": "The algorithm is a battle-tested "
              },
              {
                "k": "strong",
                "text": "IR standard for 15 years"
              },
              {
                "k": "text",
                "text": " (Robertson · Walker · SIGIR 1994 · plus the variant catalog in Manning · Raghavan · Schütze "
              },
              {
                "k": "em",
                "text": "Introduction to Information Retrieval"
              },
              {
                "k": "text",
                "text": " 2008 ch.11)"
              }
            ],
            [
              {
                "k": "text",
                "text": "It is testable in isolation against toy corpora · no "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " plumbing needed yet"
              }
            ],
            [
              {
                "k": "text",
                "text": "It proves the satellite-admission flow before tackling the harder satellites ("
              },
              {
                "k": "code",
                "text": "nika-hnsw"
              },
              {
                "k": "text",
                "text": " needs an empirical embedding distribution · "
              },
              {
                "k": "code",
                "text": "nika-autodesc"
              },
              {
                "k": "text",
                "text": " is the moat differentiator · both should land later)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Phase 1 entry plan §2 the kernel-side memory trait surface ("
            },
            {
              "k": "code",
              "text": "MemoryRemember"
            },
            {
              "k": "text",
              "text": " / "
            },
            {
              "k": "code",
              "text": "MemoryRecall"
            },
            {
              "k": "text",
              "text": " / "
            },
            {
              "k": "code",
              "text": "MemoryForget"
            },
            {
              "k": "text",
              "text": " / "
            },
            {
              "k": "code",
              "text": "MemoryStore"
            },
            {
              "k": "text",
              "text": " blanket / "
            },
            {
              "k": "code",
              "text": "MemoryLifecycle"
            },
            {
              "k": "text",
              "text": " standalone) is already shipped at "
            },
            {
              "k": "code",
              "text": "crates/nika-kernel-ai/src/memory.rs"
            },
            {
              "k": "text",
              "text": " (607 LOC · "
            },
            {
              "k": "code",
              "text": "trait_variant::make"
            },
            {
              "k": "text",
              "text": " pattern per FCI-001 Decision A1). The kernel-side "
            },
            {
              "k": "code",
              "text": "MemoryError"
            },
            {
              "k": "text",
              "text": " enum maps to NIKA_601..604 per Diamond W2.1 + W2.2. "
            },
            {
              "k": "code",
              "text": "nika-bm25"
            },
            {
              "k": "text",
              "text": " will :"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Implement "
              },
              {
                "k": "code",
                "text": "MemoryRecall"
              },
              {
                "k": "text",
                "text": " (lexical-scoring branch · alongside semantic HNSW)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Reserve its own NIKA-620..629 sub-range within the Memory 600-649 category (per Phase 1 entry plan §2 architect Q2 sub-allocation lock)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Admit "
            },
            {
              "k": "code",
              "text": "nika-bm25"
            },
            {
              "k": "text",
              "text": " to the Diamond workspace at Wave 3 as the first L1 memory satellite. Implementation = "
            },
            {
              "k": "strong",
              "text": "CRAFT from scratch"
            },
            {
              "k": "text",
              "text": " (ADR-001) hand-rolled · NOT a wrapper around "
            },
            {
              "k": "code",
              "text": "tantivy"
            },
            {
              "k": "text",
              "text": " (which would violate ADR-001 + introduce ~500K LOC of search-engine infrastructure we do not need at L1)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Target shape ·"
            }
          ]
        },
        {
          "k": "code",
          "lang": "text",
          "text": "crates/nika-bm25/\n├── Cargo.toml          name=nika-bm25 · version=0.0.1 · license=AGPL-3.0-or-later\n├── src/\n│   ├── lib.rs          public API surface · MemoryRecall impl\n│   ├── scorer.rs       Bm25 struct · k1/b/avgdl params · score() formula\n│   ├── index.rs        inverted-index builder · tokenize + postings + IDF\n│   └── query.rs        query parsing + multi-term scoring\n└── tests/\n    └── manning_fixtures.rs  1:1 fidelity against published BM25 textbook examples"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Default parameter values · "
            },
            {
              "k": "code",
              "text": "k1=1.2"
            },
            {
              "k": "text",
              "text": " (term saturation per Robertson) · "
            },
            {
              "k": "code",
              "text": "b=0.75"
            },
            {
              "k": "text",
              "text": " (length normalization · industry default). Configurable via "
            },
            {
              "k": "code",
              "text": "Bm25::with_params()"
            },
            {
              "k": "text",
              "text": " builder."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Reserve "
            },
            {
              "k": "strong",
              "text": "NIKA-620..629"
            },
            {
              "k": "text",
              "text": " sub-range for "
            },
            {
              "k": "code",
              "text": "nika-bm25"
            },
            {
              "k": "text",
              "text": " error codes (Phase 1 entry plan §2 architect Q2 allocation · within the Memory 600-649 category locked Diamond W2.1)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Proves the L1 satellite admission flow (lower-bound complexity reference for the 7 remaining satellites)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Adds a real-world "
              },
              {
                "k": "code",
                "text": "MemoryRecall"
              },
              {
                "k": "text",
                "text": " implementer the kernel mock can be tested against (parity with the abstract trait)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Lexical scoring complements future semantic HNSW (the orchestrator fuses both via RRF · nika-rrf admits at W4)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Publishable on crates.io as "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " (sovereign IR primitive · AGPL · zero vendor lock · per Rule 3 vendor-neutral default)"
              }
            ],
            [
              {
                "k": "text",
                "text": "~600 LOC well under the 15k crate cap and 1500-LOC file cap (ADR-023 + nika-invariants §\"Max LOC per file\")"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Adds 1 admitted crate to the workspace count (was 7 admitted · target 40-42 · acceptable)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Hand-rolled BM25 means we own the math forever (mitigation · IR textbook fixtures freeze the parity surface · "
              },
              {
                "k": "code",
                "text": "cargo mutants"
              },
              {
                "k": "text",
                "text": " keeps the impl honest)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Lexical-only · cannot do semantic similarity until "
              },
              {
                "k": "code",
                "text": "nika-hnsw"
              },
              {
                "k": "text",
                "text": " ships (intentional · v0.1 scope keeps the surface small)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "NIKA-620..629 sub-range becomes RESERVED in the Memory category (no concrete codes yet · landed when nika-bm25 errors crystallize during impl)"
              }
            ],
            [
              {
                "k": "text",
                "text": "The crate ships zero "
              },
              {
                "k": "code",
                "text": "nika-kernel"
              },
              {
                "k": "text",
                "text": " plumbing at v0.1 (just the algorithm) · plumbing waves later when the orchestrator consumes it"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "12-Gate admission readiness (Wave 3 evidence anchors)",
          "id": "12-gate-admission-readiness-wave-3-evidence-anchors"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per ADR-003 the admission ceremony runs all 12 gates. Pre-flight status before W3 ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Gate · Check · Pre-flight verdict · Where it lands"
              }
            ],
            [
              {
                "k": "text",
                "text": "**1** · SPEC · `docs/crate-specs/nika-bm25.md` exists · ⏸ pending W3 · New file at W3 ; must cite this ADR + the Diamond Phase 1 entry plan §3"
              }
            ],
            [
              {
                "k": "text",
                "text": "**2** · LOC cap · ≤15k · ✅ trivially (target ~600 LOC) · `wc -l crates/nika-bm25/src/*.rs`"
              }
            ],
            [
              {
                "k": "text",
                "text": "**3** · Single-file cap · ≤1500 LOC · ✅ trivially (scorer ~200 · index ~250 · query ~150) · Per ADR-023 file modularity discipline"
              }
            ],
            [
              {
                "k": "text",
                "text": "**4** · Function cap · ≤100 LOC · ✅ design splits per-term scoring + corpus stats · Per nika-invariants §\"Max LOC per fn\""
              }
            ],
            [
              {
                "k": "text",
                "text": "**5** · Zero `.unwrap()` in `src/` · ✅ enforced via `workspace.lints.clippy unwrap_used = \"deny\"` · Per dx/.claude/rules/security.md §\"Propagate errors\""
              }
            ],
            [
              {
                "k": "text",
                "text": "**6** · Layer registry (L0/0.5/1/2/3/4/5) · ✅ L1 satellite layer · `[workspace.metadata.diamond.layers]` row added in W3 admission commit"
              }
            ],
            [
              {
                "k": "text",
                "text": "**7** · Forward-compat invariants (8 patterns) · ✅ FCI-001 `trait_variant::make` (impls `MemoryRecall`) · FCI-002 `#[non_exhaustive]` on public error enum · FCI-003 versioned BM25 index format · Per `docs/architecture/forward-compat-invariants.md`"
              }
            ],
            [
              {
                "k": "text",
                "text": "**8** · Test coverage (TDD · RED→GREEN) · ⏸ pending W3 · Insta snapshots + proptest fuzzing + 1:1 textbook fidelity tests"
              }
            ],
            [
              {
                "k": "text",
                "text": "**9** · Clippy `-D warnings` · ✅ enforced via workspace lints · `cargo clippy --workspace --all-targets -- -D warnings`"
              }
            ],
            [
              {
                "k": "text",
                "text": "**10** · `cargo fmt --check` · ✅ enforced via lefthook pre-commit · Pattern 1 atomic stage+commit"
              }
            ],
            [
              {
                "k": "text",
                "text": "**11** · Documentation (`#![warn(missing_docs)]`) · ⏸ pending W3 · Crate-level `//!` doc + per-item `///` doc"
              }
            ],
            [
              {
                "k": "text",
                "text": "**12** · License + AGPL header on every file · ✅ enforced via hygiene vector · `SPDX-License-Identifier: AGPL-3.0-or-later`"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Plus the mandatory pattern gates per "
            },
            {
              "k": "code",
              "text": "nika/engine/.claude/CLAUDE.md"
            },
            {
              "k": "text",
              "text": " ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "MUTATION ≥90%"
              },
              {
                "k": "text",
                "text": " via "
              },
              {
                "k": "code",
                "text": "cargo mutants -p nika-bm25"
              }
            ],
            [
              {
                "k": "strong",
                "text": "PROPERTY"
              },
              {
                "k": "text",
                "text": " testing via "
              },
              {
                "k": "code",
                "text": "proptest"
              },
              {
                "k": "text",
                "text": " (TF-IDF monotonicity · score positivity · empty-corpus invariants)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "CANARY E2E"
              },
              {
                "k": "text",
                "text": " ↔ a "
              },
              {
                "k": "code",
                "text": "tests/canary-bm25.nika.yaml"
              },
              {
                "k": "text",
                "text": " workflow (or documented exemption if the scorer is too pure for an end-to-end canary)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "PARITY LEGACY"
              },
              {
                "k": "text",
                "text": " ↔ N/A (brouillon branch v0.79 has no BM25 implementation · CRAFT-from-scratch · no legacy to mirror)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "REVIEW SWARM"
              },
              {
                "k": "text",
                "text": " (3 agents · spn-nika:code-reviewer + spn-rust:rust-pro + feature-dev:code-reviewer)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consumer signal",
          "id": "consumer-signal"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per "
            },
            {
              "k": "code",
              "text": "dx/.claude/rules/steal-pattern.md"
            },
            {
              "k": "text",
              "text": " §\"Step 0 — Consumer-signal gate\" (Wave 10 of the DS federation discipline · adopted as a general anti-speculative-port pattern), Tier B/C steal-pattern ports require ≥1 consumer signal before status flips Proposed → Accepted. ADR-038 is an "
            },
            {
              "k": "strong",
              "text": "internal Diamond admission"
            },
            {
              "k": "text",
              "text": ", not a Tier B/C steal-pattern port, but the same anti-speculative discipline applies ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Locked decision"
              },
              {
                "k": "text",
                "text": " · the Diamond memory architecture (private studio doctrine + public trait surface at "
              },
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs"
              },
              {
                "k": "text",
                "text": ") lists "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " as one of the 8 mandatory L1 memory satellites · ✅ satisfies « locked decision » in §Step 0"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Scheduled launch"
              },
              {
                "k": "text",
                "text": " · Phase 1 entry plan §3 schedules "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " as W3 admission with ~7h focused effort · ✅ satisfies « scheduled launch »"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Consumer ticket"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "nika-memory"
              },
              {
                "k": "text",
                "text": " L2 orchestrator (deferred to W11+) requires both lexical and semantic recall paths · BM25 IS the lexical half · the orchestrator is the consumer-of-record once it ships"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-ai/src/memory.rs:218-223"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "MemoryRecall"
              },
              {
                "k": "text",
                "text": " trait definition that "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " will implement"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-error/src/codes/registry.rs:140-186"
              },
              {
                "k": "text",
                "text": " — NIKA_600 placeholder + NIKA_601..604 active + NIKA-620..629 reserved for this crate"
              }
            ],
            [
              {
                "k": "text",
                "text": "W3 admission plan · private SuperNovae DX surface (not engine-bound)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Commit anchor pre-W3 · "
              },
              {
                "k": "code",
                "text": "git log --oneline -1"
              },
              {
                "k": "text",
                "text": " on "
              },
              {
                "k": "code",
                "text": "main"
              },
              {
                "k": "text",
                "text": " at admission time will land in the W3 closeout commit body"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — Wrap `tantivy` (or `meilisearch-sdk`)",
          "id": "alt-a--wrap-tantivy-or-meilisearch-sdk"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Skip the hand-roll · adopt a published Rust search engine. Rejected because :"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Violates ADR-001 (CRAFT not extraction) · we own the math"
              }
            ],
            [
              {
                "k": "code",
                "text": "tantivy"
              },
              {
                "k": "text",
                "text": " is ~500K LOC across the workspace · we want a ~600 LOC primitive"
              }
            ],
            [
              {
                "k": "text",
                "text": "The orchestrator consumer needs "
              },
              {
                "k": "strong",
                "text": "just BM25 scoring"
              },
              {
                "k": "text",
                "text": " (not full-text-search infra · query parsers · faceting · etc.)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Wrapper-of-wrapper guard (steal-pattern.md §\"Wrapper-of-wrapper guard\") · always port the leaf"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — Defer `nika-bm25` until `nika-hnsw` admits",
          "id": "alt-b--defer-nika-bm25-until-nika-hnsw-admits"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Implement HNSW first then BM25. Rejected because :"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "HNSW requires an empirical embedding distribution (100k+ real BGE-M3 vectors) we do not have yet"
              }
            ],
            [
              {
                "k": "text",
                "text": "BM25 has zero dependencies on the embedding model · can land independently"
              }
            ],
            [
              {
                "k": "text",
                "text": "W5 of the Phase 1 entry plan explicitly DEFERS "
              },
              {
                "k": "code",
                "text": "nika-hnsw"
              },
              {
                "k": "text",
                "text": " to a trigger-gated wave (« when ≥100k real vectors exist »)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — Admit a placeholder `nika-bm25-stub` first then real impl later",
          "id": "alt-c--admit-a-placeholder-nika-bm25-stub-first-then-real-impl-later"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Stub the trait surface · land real algorithm later. Rejected because :"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Stubs are an anti-pattern per ADR-037 §Context (« Stubs that pretend to implement reserved traits accumulate residue »)"
              }
            ],
            [
              {
                "k": "text",
                "text": "The 12-gate ceremony explicitly forbids stub admission (gate 5 "
              },
              {
                "k": "code",
                "text": "cargo mutants ≥90%"
              },
              {
                "k": "text",
                "text": " cannot pass on a "
              },
              {
                "k": "code",
                "text": "todo!()"
              },
              {
                "k": "text",
                "text": " impl)"
              }
            ],
            [
              {
                "k": "text",
                "text": "CRAFT-from-scratch in 7h focused beats stub-then-rewrite in calendar time"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "ADR-001"
              },
              {
                "k": "text",
                "text": " — Orphan branch · CRAFT not extraction (rejects Alt A wrapper approach)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-003"
              },
              {
                "k": "text",
                "text": " — 12-gate admission ceremony (governs the W3 admission close)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-016"
              },
              {
                "k": "text",
                "text": " — Cancel-safety discipline (lib functions must be cancel-safe per "
              },
              {
                "k": "code",
                "text": "MemoryRecall::recall()"
              },
              {
                "k": "text",
                "text": " contract)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-023"
              },
              {
                "k": "text",
                "text": " — File modularity discipline (≤1500 LOC/file enforced)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-037"
              },
              {
                "k": "text",
                "text": " — Bottom-up Diamond progression (no feature-milestone defer · admit when ready)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/crate-layer-registry.md"
              },
              {
                "k": "text",
                "text": " — L1 row for "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " lands in Diamond W2.5"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — FCI-001/002/003 patterns this crate respects"
              }
            ],
            [
              {
                "k": "code",
                "text": "dx/.claude/rules/no-legacy-no-back-compat.md"
              },
              {
                "k": "text",
                "text": " — NUKE-LEGACY · BM25 ships as canonical lexical scorer · zero alias / shim / dual-format support"
              }
            ],
            [
              {
                "k": "code",
                "text": "dx/.claude/rules/naming-memory-subsystem.md"
              },
              {
                "k": "text",
                "text": " — Diamond ⊥ Memory orthogonality · "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " is a L1 crate organized SELON Diamond methodology"
              }
            ],
            [
              {
                "k": "text",
                "text": "Robertson & Walker 1994 · « Some simple effective approximations to the 2-Poisson model for probabilistic weighted retrieval »"
              }
            ],
            [
              {
                "k": "text",
                "text": "Manning · Raghavan · Schütze 2008 · "
              },
              {
                "k": "em",
                "text": "Introduction to Information Retrieval"
              },
              {
                "k": "text",
                "text": " ch.11 (fixture source for parity tests)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Promotion gate"
            },
            {
              "k": "text",
              "text": " · ADR-038 stays "
            },
            {
              "k": "code",
              "text": "proposed"
            },
            {
              "k": "text",
              "text": " until ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-bm25/"
              },
              {
                "k": "text",
                "text": " scaffolded with the 4-file shape"
              }
            ],
            [
              {
                "k": "text",
                "text": "12 gates ✅ per the readiness table above (with W3-pending gates filled in)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Workspace "
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "[workspace] members"
              },
              {
                "k": "text",
                "text": " array includes "
              },
              {
                "k": "code",
                "text": "crates/nika-bm25"
              }
            ],
            [
              {
                "k": "code",
                "text": "[workspace.metadata.diamond.layers] nika-bm25 = \"L1\""
              },
              {
                "k": "text",
                "text": " added (Diamond W2.5)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/crate-layer-registry.md"
              },
              {
                "k": "text",
                "text": " L1 section has the "
              },
              {
                "k": "code",
                "text": "nika-bm25"
              },
              {
                "k": "text",
                "text": " row (Diamond W2.5)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Single atomic admission commit · message format "
              },
              {
                "k": "code",
                "text": "feat(nika-bm25): admit to workspace — all 12 gates passed"
              },
              {
                "k": "text",
                "text": " (per nika/engine commit-granularity.md)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "On promotion · status flips to "
            },
            {
              "k": "code",
              "text": "accepted"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "enables: []"
            },
            {
              "k": "text",
              "text": " will list nika-rrf and nika-hnsw admission ADRs once they land."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Out-of-scope for ADR-038"
            },
            {
              "k": "text",
              "text": " (deferred to future ADRs) ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "BM25F (per-field weighting) — nice-to-have at v0.2 if a consumer asks"
              }
            ],
            [
              {
                "k": "text",
                "text": "BM25+ (lower-bound saturation) — same as BM25F · deferred"
              }
            ],
            [
              {
                "k": "text",
                "text": "Custom tokenizers (CJK · IDF caches · etc.) — orchestrator-driven · post v0.95 Cortex"
              }
            ],
            [
              {
                "k": "text",
                "text": "Persistent index serialization format — FCI-003 versioning bake-in at W3 · concrete schema in a follow-up ADR"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-082",
      "file": "adr-082-envelope-nika-v1-single-version-marker.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Related",
          "anchor": "related"
        }
      ],
      "words": 404,
      "sha256": "9087ab2620ff8f78e05a237a286ad2e75a5cf5ccadec9d862d3d0f259bfc8657",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-082: Workflow envelope — single version marker `nika: v1`",
          "id": "adr-082-workflow-envelope--single-version-marker-nika-v1"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The public "
            },
            {
              "k": "strong",
              "text": "`nika-spec`"
            },
            {
              "k": "text",
              "text": " (Apache-2.0 · created 2026-05-22 · 5 pillars\nlocked D-2026-05-22-N1..N8) defines the canonical workflow envelope as a\n"
            },
            {
              "k": "strong",
              "text": "single version marker"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "code",
          "lang": "yaml",
          "text": "nika: v1                 # required · language name (key) + contract version (value)\nworkflow: my-workflow-id # required · kebab-case · unique within file\ntasks:\n  <task-id>: ..."
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This "
            },
            {
              "k": "strong",
              "text": "supersedes"
            },
            {
              "k": "text",
              "text": " the Kubernetes-style envelope adopted in "
            },
            {
              "k": "strong",
              "text": "ADR-021"
            },
            {
              "k": "text",
              "text": "\n("
            },
            {
              "k": "code",
              "text": "apiVersion: nika.sh/v1"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "kind"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "metadata"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "spec"
            },
            {
              "k": "text",
              "text": "). The spec\nexplicitly rejects the two-field "
            },
            {
              "k": "code",
              "text": "apiVersion:"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "schema:"
            },
            {
              "k": "text",
              "text": " ceremony\n(cf "
            },
            {
              "k": "code",
              "text": "nika-spec spec/01-envelope.md"
            },
            {
              "k": "text",
              "text": " · \"Why one field, not apiVersion +\nschema\") on the grounds that modern specs converge on a single version\nmarker — OpenAPI writes "
            },
            {
              "k": "code",
              "text": "openapi: 3.1.0"
            },
            {
              "k": "text",
              "text": ", Docker Compose dropped its\n"
            },
            {
              "k": "code",
              "text": "version:"
            },
            {
              "k": "text",
              "text": " field entirely."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Two contradictory legacy forms were also still scattered across engine\ndocs and surfaced by a verification swarm 2026-05-25:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "apiVersion: nika.sh/v1"
              },
              {
                "k": "text",
                "text": " (the ADR-021 K8s form)"
              }
            ],
            [
              {
                "k": "code",
                "text": "schema: \"nika/workflow@0.12\""
              },
              {
                "k": "text",
                "text": " (the pre-Diamond brouillon form)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A phantom citation "
            },
            {
              "k": "code",
              "text": "ADR-044"
            },
            {
              "k": "text",
              "text": " (\"adr-044-schema-envelope.md\") was referenced\nin several docs but the file never existed."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Adopt `nika: v1` as the single canonical envelope version marker for\nevery Nika user-facing workflow YAML."
            },
            {
              "k": "text",
              "text": " The contract version is "
            },
            {
              "k": "code",
              "text": "v1"
            },
            {
              "k": "text",
              "text": ",\nlocked forever — a "
            },
            {
              "k": "code",
              "text": "v2"
            },
            {
              "k": "text",
              "text": " would require a breaking LANGUAGE contract change\n· effectively never. This LANGUAGE-envelope axis is "
            },
            {
              "k": "strong",
              "text": "orthogonal to the\nengine version"
            },
            {
              "k": "text",
              "text": ": the engine ships real semver toward a 1.0 launch (ADR-002\n· amended D-2026-06-20-N1), but "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": " stays frozen across every engine\nmajor (it is the real \"v1\" signal adopters always saw)."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The "
              },
              {
                "k": "strong",
                "text": "`kind` discriminator"
              },
              {
                "k": "text",
                "text": " + multi-document rationale from ADR-021\n"
              },
              {
                "k": "strong",
                "text": "survives"
              },
              {
                "k": "text",
                "text": " (9 doc kinds: Workflow · Skill · Agent · Provider · Mcp ·\nEval · Recipe · Shield · Lints). Only the "
              },
              {
                "k": "strong",
                "text": "version-field shape"
              },
              {
                "k": "text",
                "text": "\nchanged: "
              },
              {
                "k": "code",
                "text": "apiVersion: nika.sh/v1"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "The engine's internal canonical URI stays "
              },
              {
                "k": "code",
                "text": "https://nika.sh/spec/v1"
              },
              {
                "k": "text",
                "text": " for\nRDF / conformance tooling — but the "
              },
              {
                "k": "strong",
                "text": "author never types a URL"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "The serde discriminator becomes "
              },
              {
                "k": "code",
                "text": "#[serde(tag = \"nika\")]"
              },
              {
                "k": "text",
                "text": " with variant\n"
              },
              {
                "k": "code",
                "text": "#[serde(rename = \"v1\")]"
              },
              {
                "k": "text",
                "text": " (was "
              },
              {
                "k": "code",
                "text": "tag = \"schema\""
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "nika/workflow@0.12"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-schema"
              },
              {
                "k": "text",
                "text": " parser rewrite (Phase D) targets "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": ", not the\n"
              },
              {
                "k": "code",
                "text": "apiVersion:"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "schema:"
              },
              {
                "k": "text",
                "text": " forms."
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-021 marked "
              },
              {
                "k": "code",
                "text": "superseded"
              },
              {
                "k": "text",
                "text": "; its "
              },
              {
                "k": "code",
                "text": "kind"
              },
              {
                "k": "text",
                "text": "/multi-doc analysis stays valid\nreference material."
              }
            ],
            [
              {
                "k": "text",
                "text": "The phantom "
              },
              {
                "k": "code",
                "text": "ADR-044"
              },
              {
                "k": "text",
                "text": " references are removed; this ADR is the canonical\nengine-side anchor for the envelope decision (mirrors the public spec)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Public canon source of truth: "
              },
              {
                "k": "code",
                "text": "nika-spec spec/01-envelope.md"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "docs/adr/adr-021-yaml-envelope-convention.md"
              },
              {
                "k": "text",
                "text": " (superseded · K8s form)"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-spec"
              },
              {
                "k": "text",
                "text": " spec/01-envelope.md — the public "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " contract"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/forward-compat-invariants.md"
              },
              {
                "k": "text",
                "text": " — FCI-009 envelope versioning"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-083",
      "file": "adr-083-l1-computer-use-cross-platform-doctrine.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "References",
          "anchor": "references"
        },
        {
          "title": "Update log",
          "anchor": "update-log"
        }
      ],
      "words": 1021,
      "sha256": "c3081f4348a59f1a6fe0111eca6ec8fe9229f323256232430fa90e09463d82a5",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-083 · L1 computer-use effect crates · cross-platform doctrine",
          "id": "adr-083--l1-computer-use-effect-crates--cross-platform-doctrine"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The Phase 2 M2 computer-use L1 effect crates ("
            },
            {
              "k": "code",
              "text": "nika-screen"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-ocr"
            },
            {
              "k": "text",
              "text": " ·\n"
            },
            {
              "k": "code",
              "text": "nika-a11y"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-input"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-browser"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika-vision-local"
            },
            {
              "k": "text",
              "text": ") back the\nfuture "
            },
            {
              "k": "code",
              "text": "invoke:"
            },
            {
              "k": "text",
              "text": "-tool surface that lets a workflow "
            },
            {
              "k": "em",
              "text": "see + act on"
            },
            {
              "k": "text",
              "text": " a desktop."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "An empirical audit (2026-05-26) of the three already-admitted crates surfaced\nan "
            },
            {
              "k": "strong",
              "text": "inconsistent platform posture"
            },
            {
              "k": "text",
              "text": " that was never an explicit decision:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crate · Backend · Platform reality"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-screen` · `xcap` 0.9 · **cross-platform** (macOS / Linux / Windows)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-ocr` · `ocrs` + `rten` · **cross-platform** (pure-Rust, no target gate)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-a11y` · `accessibility` 0.2 · **macOS-only** (`[target.'cfg(target_os=\"macos\")']`)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-screen"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "nika-ocr"
            },
            {
              "k": "text",
              "text": " are cross-platform-by-default; "
            },
            {
              "k": "code",
              "text": "nika-a11y"
            },
            {
              "k": "text",
              "text": " shipped\nmacOS-only — "
            },
            {
              "k": "strong",
              "text": "forced"
            },
            {
              "k": "text",
              "text": ", because no single cross-platform pure-Rust "
            },
            {
              "k": "em",
              "text": "accessibility-\nquery"
            },
            {
              "k": "text",
              "text": " crate exists ("
            },
            {
              "k": "code",
              "text": "atspi"
            },
            {
              "k": "text",
              "text": " = Linux AT-SPI2 · "
            },
            {
              "k": "code",
              "text": "uiautomation"
            },
            {
              "k": "text",
              "text": " = Windows UIA are\nper-OS). The macOS-only posture was a "
            },
            {
              "k": "em",
              "text": "necessity"
            },
            {
              "k": "text",
              "text": ", not a chosen strategy — but it\nwas never written down as such, so the next crate ("
            },
            {
              "k": "code",
              "text": "nika-input"
            },
            {
              "k": "text",
              "text": ", M2.4) nearly\ninherited \"macOS-first\" as if it were the pattern. It is not: "
            },
            {
              "k": "code",
              "text": "nika-input"
            },
            {
              "k": "text",
              "text": " has a\nmature cross-platform option ("
            },
            {
              "k": "code",
              "text": "enigo"
            },
            {
              "k": "text",
              "text": ")."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator directive (2026-05-26, verbatim): "
            },
            {
              "k": "em",
              "text": "« je veux cette philosophie de cross\nplateform et macos, linux prio sous unix et aussi windows et tout les systeme …\non peut revoir et refactor a11y si besoin »"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "L1 computer-use effect crates are CROSS-PLATFORM."
            },
            {
              "k": "text",
              "text": " Priority order ·\n"
            },
            {
              "k": "strong",
              "text": "macOS + Linux (Unix family) first"
            },
            {
              "k": "text",
              "text": ", then "
            },
            {
              "k": "strong",
              "text": "Windows"
            },
            {
              "k": "text",
              "text": ", then all systems."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Backend-selection rule (the doctrine):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "Prefer "
              },
              {
                "k": "strong",
                "text": "one mature, permissively-licensed cross-platform Rust crate"
              },
              {
                "k": "text",
                "text": " that\ncovers macOS + Linux (+ Windows) behind the L0.5 trait. The guards (ADR-081)\nstay pure + at "
              },
              {
                "k": "em",
                "text": "our"
              },
              {
                "k": "text",
                "text": " layer, so a third-party backend never touches them."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Only when no cross-platform crate exists"
              },
              {
                "k": "text",
                "text": ", fall back to "
              },
              {
                "k": "strong",
                "text": "per-OS backends\nbehind the same L0.5 trait"
              },
              {
                "k": "text",
                "text": " — and even then, macOS-only is a "
              },
              {
                "k": "em",
                "text": "temporary"
              },
              {
                "k": "text",
                "text": "\nstate: the Linux (then Windows) backends are a "
              },
              {
                "k": "strong",
                "text": "planned refactor"
              },
              {
                "k": "text",
                "text": ", not a\npermanent gate. Prioritize the "
              },
              {
                "k": "strong",
                "text": "macOS + Linux"
              },
              {
                "k": "text",
                "text": " backends first."
              }
            ],
            [
              {
                "k": "text",
                "text": "The cross-platform DTOs + the mandatory guards always ship "
              },
              {
                "k": "strong",
                "text": "all-OS, headless"
              },
              {
                "k": "text",
                "text": "\n(a denied/unavailable OS returns "
              },
              {
                "k": "code",
                "text": "BackendUnavailable"
              },
              {
                "k": "text",
                "text": ", never a compile gap in\nthe shared logic)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Per-crate disposition",
          "id": "per-crate-disposition"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crate · Disposition under this doctrine"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-screen` · ✅ cross-platform already (`xcap`) · no action"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-ocr` · ✅ cross-platform already (`ocrs`/`rten`) · no action"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-a11y` · 🔧 **REFACTOR** · keep `accessibility` (macOS) · **add `atspi`** (Linux AT-SPI2 · the crate-spec §4 already vetted it · Apache/MIT) · `uiautomation` (Windows) later. Trait + `AxNode` DTO + Guard 3 are already backend-agnostic → additive per-OS backend behind `#[cfg]`. Amends the crate-spec's \"RESOLVED macOS-first\" disposition."
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-input` · 🆕 **cross-platform from day 1** via `enigo` (macOS + Linux + Windows · MIT). Resolves the M2.4 backend DECISION POINT. Guards 1+2 are pure + at our layer · `enigo` only performs the final OS event post. (No `enigo` is rejected in favour of macOS-only CGEvent — that would re-introduce the inconsistency this ADR removes.)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-browser` (M2.5) · cross-platform backend mandatory (the chosen automation lib must cover macOS + Linux)."
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-vision-local` (M2.6) · cross-platform by construction (local model inference · pure compute · like `nika-ocr`)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per-crate exact crate + symbol surface is "
            },
            {
              "k": "strong",
              "text": "primary-source verified at Gate 3"
            },
            {
              "k": "text",
              "text": "\n("
            },
            {
              "k": "code",
              "text": ".crate"
            },
            {
              "k": "text",
              "text": " tarball extraction · the rigor that confirmed "
            },
            {
              "k": "code",
              "text": "accessibility"
            },
            {
              "k": "text",
              "text": " for\n"
            },
            {
              "k": "code",
              "text": "nika-a11y"
            },
            {
              "k": "text",
              "text": " · no phantom symbols written before verification)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "One coherent platform posture across the computer-use cohort · no accidental\nmacOS lock-in."
              }
            ],
            [
              {
                "k": "text",
                "text": "macOS + Linux ship together (the operator's stated priority · Unix-family first)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The guards stay backend-agnostic · adding an OS backend never touches security\nlogic."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative (acknowledged · mitigated)",
          "id": "negative-acknowledged--mitigated"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-a11y"
              },
              {
                "k": "text",
                "text": " gains a Linux "
              },
              {
                "k": "code",
                "text": "atspi"
              },
              {
                "k": "text",
                "text": " backend (D-Bus/zbus) → a refactor + its own\nGate 3 primary-source verification + tri-platform consideration. Mitigated ·\nadditive behind the existing trait · the DTO + Guard 3 already all-OS."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-input"
              },
              {
                "k": "text",
                "text": " takes a dependency on "
              },
              {
                "k": "code",
                "text": "enigo"
              },
              {
                "k": "text",
                "text": " (a backend we don't fully own) vs\nhand-written per-OS FFI. Mitigated · "
              },
              {
                "k": "code",
                "text": "enigo"
              },
              {
                "k": "text",
                "text": " is mature + permissive · the\nguards + consent type-state are "
              },
              {
                "k": "em",
                "text": "ours"
              },
              {
                "k": "text",
                "text": " (the dep only posts the final event)."
              }
            ],
            [
              {
                "k": "text",
                "text": "CI eventually wants macOS + Linux runners for the per-OS backends. Deferred ·\nheadless logic + guards are CI-covered all-OS today; real-OS smoke stays\n"
              },
              {
                "k": "code",
                "text": "#[ignore]"
              },
              {
                "k": "text",
                "text": " per the M2 precedent."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Sequencing note (NOT this ADR's call · gate-review)",
          "id": "sequencing-note-not-this-adrs-call--gate-review"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The computer-use cohort is "
            },
            {
              "k": "strong",
              "text": "not"
            },
            {
              "k": "text",
              "text": " in the v0.81 first-publish scope (v0.81 =\nbinary engine + language · the 4 verbs + DAG + providers + MCP server). This ADR\ngoverns "
            },
            {
              "k": "strong",
              "text": "how"
            },
            {
              "k": "text",
              "text": " computer-use is built (cross-platform); "
            },
            {
              "k": "strong",
              "text": "when"
            },
            {
              "k": "text",
              "text": " (relative to the\nv0.81 spine: providers → verbs → L3 runtime) is an operator gate-review decision\ntracked separately (private roadmap)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "macOS-first, port later (rejected)"
              },
              {
                "k": "text",
                "text": " · the prior implicit posture. Rejected\nper operator directive — cross-platform is the "
              },
              {
                "k": "em",
                "text": "philosophy"
              },
              {
                "k": "text",
                "text": ", day-1 where a\ncrate exists. macOS-only survives "
              },
              {
                "k": "em",
                "text": "only"
              },
              {
                "k": "text",
                "text": " where no cross-platform crate exists\n(a11y), as a temporary refactor target."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`enigo` rejected for \"control\" (rejected)"
              },
              {
                "k": "text",
                "text": " · the M2.4 spec's first draft\nleaned macOS-CGEvent for sovereignty/control. Re-examined · the guards are pure"
              },
              {
                "k": "text",
                "text": "+ at our layer, so `enigo` does not weaken them · it only removes hand-written\nper-OS FFI. Cross-platform day-1 > marginal FFI ownership."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "docs/crate-specs/nika-input.md"
              },
              {
                "k": "text",
                "text": " §4 (backend DECISION POINT · resolved here)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/crate-specs/nika-a11y.md"
              },
              {
                "k": "text",
                "text": " §4 (macOS-first disposition · amended here)"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/adr/adr-081-l1-effect-crate-guard-contract.md"
              },
              {
                "k": "text",
                "text": " (guard contract · backend-agnostic)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-kernel-core/src/io/input.rs"
              },
              {
                "k": "text",
                "text": " (the trait names macOS CGEvent · Windows SendInput · Linux uinput — superseded for "
              },
              {
                "k": "code",
                "text": "nika-input"
              },
              {
                "k": "text",
                "text": " by the cross-platform "
              },
              {
                "k": "code",
                "text": "enigo"
              },
              {
                "k": "text",
                "text": " choice; the per-OS APIs remain the fallback model the trait doc describes)"
              }
            ],
            [
              {
                "k": "text",
                "text": "v0.81 first-publish scope (binary engine + language · the 4 verbs + DAG + providers + MCP server) · sequencing context for the §Sequencing note (private roadmap · not cited from this public repo)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Update log",
          "id": "update-log"
        },
        {
          "k": "code",
          "lang": "text",
          "text": "2026-05-26  ACCEPTED — initial · operator directive locks the cross-platform\n              doctrine for the L1 computer-use cohort (macOS + Linux prio ·\n              Windows + all). Resolves nika-input backend = enigo (cross-platform\n              day-1). Flags nika-a11y for a Linux atspi backend refactor. screen +\n              ocr already cross-platform (no action). Guards stay backend-agnostic.\n              Sequencing (now vs v0.81 spine) is a separate operator gate-review."
        }
      ]
    },
    {
      "id": "ADR-084",
      "file": "adr-084-nika-catalog-spec-26-reconciliation.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Per-spec-category mapping (the canonical 26)",
          "anchor": "per-spec-category-mapping-the-canonical-26"
        },
        {
          "title": "Trust-category mapping (per `nika-schema::trust.rs`)",
          "anchor": "trust-category-mapping-per-nika-schematrustrs"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Gate verification",
          "anchor": "gate-verification"
        },
        {
          "title": "Companion artefacts",
          "anchor": "companion-artefacts"
        },
        {
          "title": "References",
          "anchor": "references"
        }
      ],
      "words": 1245,
      "sha256": "eb99b99cedaa4ad3a2b4115181b566c38a0918590a6e4612c11ac6c3b75df30d",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-084 · nika-catalog reconciliation to spec 26",
          "id": "adr-084--nika-catalog-reconciliation-to-spec-26"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika/spec/stdlib/builtins-v0.1.md"
            },
            {
              "k": "text",
              "text": " (the canonical workflow language stdlib\nper ADR-082 single-version-marker envelope "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": ") ships "
            },
            {
              "k": "strong",
              "text": "26 builtins\nacross 5 categories"
            },
            {
              "k": "text",
              "text": " as of D-2026-05-22-N6 stdlib-collapse + 2026-05-27\n"
            },
            {
              "k": "code",
              "text": "nika:json_merge"
            },
            {
              "k": "text",
              "text": " cut. The engine's "
            },
            {
              "k": "code",
              "text": "nika-catalog::ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " carried\n"
            },
            {
              "k": "strong",
              "text": "63 entries across 10 categories"
            },
            {
              "k": "text",
              "text": " as a fossil of pre-D-N6 era (engine\ninventory grew during Sprint 2 + media planning before the spec\nminimalism doctrine locked)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A monorepo-side §2.6 Signal 7 verify-the-auditor catch (2026-05-27 ·\n"
            },
            {
              "k": "code",
              "text": "studio/architecture/research/2026-05-27-g1-builtins-enum-codegen-design.md"
            },
            {
              "k": "text",
              "text": "\n§10 BLOCKER) surfaced the drift empirically · engine first-5 names were\n"
            },
            {
              "k": "code",
              "text": "aggregate · assert · chart · chunk · compare"
            },
            {
              "k": "text",
              "text": " vs spec first-5\n"
            },
            {
              "k": "code",
              "text": "sleep · log · emit · assert · prompt"
            },
            {
              "k": "text",
              "text": ". The drift gates the future\nschemars-adoption G1 codegen (builtin-enum closed set in\n"
            },
            {
              "k": "code",
              "text": "workflow.schema.json"
            },
            {
              "k": "text",
              "text": " for LSP-perfect completion · proposed-blocked\nuntil catalog reconciliation lands)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This ADR locks the catalog reconciliation as the structural close."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-catalog::ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "nika-catalog::types::builtin::BuiltinCategory"
            },
            {
              "k": "text",
              "text": "\nreconcile to spec source-of-truth "
            },
            {
              "k": "code",
              "text": "nika/spec/stdlib/builtins-v0.1.md"
            },
            {
              "k": "text",
              "text": " ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "`ALL_BUILTINS`"
              },
              {
                "k": "text",
                "text": " · 63 → 26 entries (46 legacy removed · 9 spec-only\nadded · 17 keeps recategorised to the simplified 5-category enum)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`BuiltinCategory`"
              },
              {
                "k": "text",
                "text": " · 10 → 5 variants ·"
              },
              {
                "k": "text",
                "text": "- KEEP · `Core` · `File` · `Data` · `Introspection`\n- ADD · `Network` (for `fetch` + `notify` · spec category)\n- REMOVE · `DataSprint2` · `CostRecords` · `Agent` · `MediaAlwaysOn` ·\n  `MediaCore` · `MediaOptIn` (6 variants · the 46 legacy entries that\n  used them are all cut · zero workspace consumer matches on these\n  variants per `grep -rn \"BuiltinCategory::<v>\" crates/`)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`nika-schema::trust.rs`"
              },
              {
                "k": "text",
                "text": " · 67 hardcoded "
              },
              {
                "k": "code",
                "text": "\"nika:<name>\""
              },
              {
                "k": "text",
                "text": " strings\nreduced to 26 across 3 trust categories (PROPAGATING 7 · PURE 14 ·\nEXTERNAL 5 · the legacy REFERENCE category consolidated into\nPROPAGATING since they were semantically equivalent per the existing\ntrust.rs comment · REFERENCE re-instates when media DEFERRED builtins\nland per LOCK-031)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Doc-comments"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "63 tools across 10 categories"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "26 tools across 5 categories"
              },
              {
                "k": "text",
                "text": " (line 6 of "
              },
              {
                "k": "code",
                "text": "types/builtin.rs"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Per-spec-category mapping (the canonical 26)",
          "id": "per-spec-category-mapping-the-canonical-26"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Category · N · Builtins"
              }
            ],
            [
              {
                "k": "text",
                "text": "Core · 7 · `sleep` · `log` · `emit` · `assert` · `prompt` · `done` · `wait_until`"
              }
            ],
            [
              {
                "k": "text",
                "text": "File · 5 · `read` · `write` · `edit` · `glob` · `grep`"
              }
            ],
            [
              {
                "k": "text",
                "text": "Data · 8 · `jq` · `json_diff` · `validate` · `json_merge_patch` · `csv_to_json` · `uuid` · `date` · `hash`"
              }
            ],
            [
              {
                "k": "text",
                "text": "Network · 2 · `fetch` · `notify`"
              }
            ],
            [
              {
                "k": "text",
                "text": "Introspection · 4 · `cost` · `records` · `dag_info` · `threads`"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Total · 26. Alphabetically sorted in "
            },
            {
              "k": "code",
              "text": "ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " (binary-search\ninvariant preserved · unit-test "
            },
            {
              "k": "code",
              "text": "sorted_order"
            },
            {
              "k": "text",
              "text": " GREEN)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Trust-category mapping (per `nika-schema::trust.rs`)",
          "id": "trust-category-mapping-per-nika-schematrustrs"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Trust class · N · Builtins · Output trust"
              }
            ],
            [
              {
                "k": "text",
                "text": "PROPAGATING · 7 · `csv_to_json` · `glob` · `grep` · `jq` · `json_diff` · `json_merge_patch` · `read` · input"
              }
            ],
            [
              {
                "k": "text",
                "text": "PURE · 14 · `assert` · `cost` · `dag_info` · `date` · `done` · `emit` · `hash` · `log` · `records` · `sleep` · `threads` · `uuid` · `validate` · `wait_until` · Trusted"
              }
            ],
            [
              {
                "k": "text",
                "text": "EXTERNAL · 5 · `edit` · `fetch` · `notify` · `prompt` · `write` · Untrusted"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Total · 26. Conservative classification · file "
            },
            {
              "k": "code",
              "text": "edit"
            },
            {
              "k": "text",
              "text": " is EXTERNAL (writes\nto disk · observable side-effect) · file "
            },
            {
              "k": "code",
              "text": "read"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "glob"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "grep"
            },
            {
              "k": "text",
              "text": " are\nPROPAGATING (input-trust passes through · no observable side-effect).\n"
            },
            {
              "k": "code",
              "text": "prompt"
            },
            {
              "k": "text",
              "text": " is EXTERNAL because human/API input is by definition Untrusted\nfloor."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Spec ↔ engine parity"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "nika-catalog"
              },
              {
                "k": "text",
                "text": " now mirrors "
              },
              {
                "k": "code",
                "text": "nika/spec/stdlib"
              },
              {
                "k": "text",
                "text": "\nexactly · the catch-class « engine carries phantoms vs spec » closes\nstructurally · future schemars-adoption G1 codegen (ADR-085 reserved)\nunblocks · "
              },
              {
                "k": "code",
                "text": "workflow.schema.json"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "builtin_name"
              },
              {
                "k": "text",
                "text": " can ratchet from\npermissive pattern to closed enum derived from "
              },
              {
                "k": "code",
                "text": "ALL_BUILTINS"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Trust surface lean"
              },
              {
                "k": "text",
                "text": " · 67 → 26 hardcoded refs in "
              },
              {
                "k": "code",
                "text": "trust.rs"
              },
              {
                "k": "text",
                "text": " · -61%\nsurface area · easier to audit + verify the trust-floor invariant."
              }
            ],
            [
              {
                "k": "strong",
                "text": "SemVer-clean enum simplification"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "BuiltinCategory"
              },
              {
                "k": "text",
                "text": " removes 6\nvariants but "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": " is on the enum AND zero workspace\nconsumers match on the removed variants · the breaking change is\ntheoretical ("
              },
              {
                "k": "code",
                "text": "cargo public-api"
              },
              {
                "k": "text",
                "text": " will flag but no actual call-site\nbreaks)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Forward-compat"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "Network"
              },
              {
                "k": "text",
                "text": " variant added per "
              },
              {
                "k": "code",
                "text": "#[non_exhaustive]"
              },
              {
                "k": "text",
                "text": "\nratchet · safely consumable when downstream wants per-category trust\ndefaults. REFERENCE class consolidation into PROPAGATING is reversible\nif media builtins re-emerge (current LOCK-031 DEFERRED)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Public API surface change"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "BuiltinCategory"
              },
              {
                "k": "text",
                "text": " enum membership\nreshuffles · "
              },
              {
                "k": "code",
                "text": "cargo public-api"
              },
              {
                "k": "text",
                "text": " diff will show removed variants.\nAcceptable per ADR-002 (amended D-2026-06-20-N1 · real semver toward 1.0) ·\nthe workspace is pre-1.0 · breaking MINOR ratchets are allowed pre-launch."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Trust.rs REFERENCE category gone"
              },
              {
                "k": "text",
                "text": " · the documentation-only split\nbetween PROPAGATING and REFERENCE (both passed input trust) is now\ncollapsed · reintroduces when media DEFERRED builtins land. Cost ·\n~0 (semantically equivalent today · just a code-organization signal)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Tests · +2 net new"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "legacy_builtins_unknown_post_d_n6"
              },
              {
                "k": "text",
                "text": " (trust)"
              },
              {
                "k": "text",
                "text": "+ `category_totals_match_spec_26` (trust) · plus `builtin_count`\n+ `category_counts` + `is_known_builtin_works` updated for the new\ntotals. 1170 baseline → 1172 tests GREEN."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "A · Keep 63 catalog · LSP codegen filters to spec 26 at build time",
          "id": "a--keep-63-catalog--lsp-codegen-filters-to-spec-26-at-build-time"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · the source-of-truth divergence stays · spec ↔ engine parity\nis never structural · drift recurs at each spec update · two truth\nsources is the anti-pattern this ADR closes."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "B · Defer to L2 `nika-builtin` admission (the MEMORY-pointer framing)",
          "id": "b--defer-to-l2-nika-builtin-admission-the-memory-pointer-framing"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED post user-explicit override (« ship reconciliation now »\nAskUserQuestion 2026-05-27). The L2-future framing remained valid as\nlong as no consumer needed the spec-26 set today · the schemars-adoption\nG1 plan + the canon coherence work surfaced an immediate consumer signal\nthat overrode the LOCK-031 spirit · the user redirected accordingly.\nDocumented per "
            },
            {
              "k": "code",
              "text": "dx/.claude/rules/socratic-research-discipline.md"
            },
            {
              "k": "text",
              "text": " §5c\n(Option D · don't pick least bad)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "C · Mass-rename engine to keep 63 set + mark legacy as deprecated",
          "id": "c--mass-rename-engine-to-keep-63-set--mark-legacy-as-deprecated"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · violates "
            },
            {
              "k": "code",
              "text": "dx/.claude/rules/no-legacy-no-back-compat.md"
            },
            {
              "k": "text",
              "text": "\nNUKE-LEGACY doctrine + Class 1 single-canonical-set discipline ·\ndeprecated-alias drift accumulates · git history is the archive."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate verification",
          "id": "gate-verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This is a refactor of admitted-crate data + types (not a new crate\nadmission) · the ADR-003 12-gate ceremony reduces to applicable gates ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Gate 2 TDD"
              },
              {
                "k": "text",
                "text": " · ✓ existing tests updated to spec 26 · 2 new tests\nadded · 1172 lib tests GREEN."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Gate 3 IMPL"
              },
              {
                "k": "text",
                "text": " · ✓ "
              },
              {
                "k": "code",
                "text": "cargo check --workspace"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Gate 4 CLIPPY"
              },
              {
                "k": "text",
                "text": " · ✓ "
              },
              {
                "k": "code",
                "text": "cargo clippy --workspace --all-targets -- -D warnings"
              },
              {
                "k": "text",
                "text": " 0 warnings (post 8 doc_markdown backtick fixes)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Gate 8 DOCS"
              },
              {
                "k": "text",
                "text": " · ✓ "
              },
              {
                "k": "code",
                "text": "cargo doc --no-deps -p nika-catalog -p nika-schema"
              },
              {
                "k": "text",
                "text": "\n0 warnings."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Gate 12 ATOMIC"
              },
              {
                "k": "text",
                "text": " · ✓ single commit · co-authored Nika 🦋."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo fmt --check"
              },
              {
                "k": "text",
                "text": " ✓ GREEN."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo deny check"
              },
              {
                "k": "text",
                "text": " ✓ advisories/bans/licenses/sources ok."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gates 1 / 5 / 6 / 7 / 9 / 10 / 11 · N/A for data refactor (no new\ncrate · no new logic surface beyond data shape)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Companion artefacts",
          "id": "companion-artefacts"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-catalog/src/data/builtins.rs"
              },
              {
                "k": "text",
                "text": " · 63 → 26 entries · 5-category\nreorganization · tests updated."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog/src/types/builtin.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "BuiltinCategory"
              },
              {
                "k": "text",
                "text": " 10 → 5\nvariants · doc-comment refreshed."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog/src/lib.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "all_builtins_non_empty"
              },
              {
                "k": "text",
                "text": " test 63 → 26."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/trust.rs"
              },
              {
                "k": "text",
                "text": " · 67 → 26 hardcoded refs · 4 → 3 trust\ncategory lists · 2 new tests."
              }
            ],
            [
              {
                "k": "code",
                "text": "studio/architecture/research/2026-05-27-g1-builtins-enum-codegen-design.md"
              },
              {
                "k": "text",
                "text": "\n· §10 BLOCKER inventory + reconciliation scope (the design + audit\ntrail · monorepo-side)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " · the canonical source-of-truth\n(unchanged · this ADR mirrors it into the engine)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " · spec canonical 26 + cuts"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-082 · envelope "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " single-version-marker"
              }
            ],
            [
              {
                "k": "text",
                "text": "D-2026-05-22-N6 · stdlib-collapse 42 → 27 (monorepo decision)"
              }
            ],
            [
              {
                "k": "text",
                "text": "2026-05-27 "
              },
              {
                "k": "code",
                "text": "nika:json_merge"
              },
              {
                "k": "text",
                "text": " cut · "
              },
              {
                "k": "code",
                "text": "jaq"
              },
              {
                "k": "text",
                "text": " source-verified"
              }
            ],
            [
              {
                "k": "code",
                "text": "dx/.claude/rules/no-legacy-no-back-compat.md"
              },
              {
                "k": "text",
                "text": " · NUKE-LEGACY discipline"
              }
            ],
            [
              {
                "k": "code",
                "text": "dx/.claude/rules/cross-source-validation.md"
              },
              {
                "k": "text",
                "text": " §2.6 Signal 7 verify-the-auditor"
              }
            ],
            [
              {
                "k": "code",
                "text": "dx/.claude/rules/round-1-ship-pattern.md"
              },
              {
                "k": "text",
                "text": " §6 LOCK-031 trigger-gated"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-085",
      "file": "adr-085-spec-schema-oneof-bridge-to-schemars-codegen.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Why interim hand-derived (NOT engine-codegen output today)",
          "anchor": "why-interim-hand-derived-not-engine-codegen-output-today"
        },
        {
          "title": "Future ratchet (trigger-gated)",
          "anchor": "future-ratchet-trigger-gated"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Gate verification",
          "anchor": "gate-verification"
        },
        {
          "title": "Companion artefacts",
          "anchor": "companion-artefacts"
        },
        {
          "title": "References",
          "anchor": "references"
        }
      ],
      "words": 1334,
      "sha256": "f46379d845321966d43379ec9f7870d05b52dac33b94f8e11259f5e3c958dba7",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-085 · spec workflow.schema.json oneOf · bridge to schemars codegen",
          "id": "adr-085--spec-workflowschemajson-oneof--bridge-to-schemars-codegen"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika/spec/schemas/workflow.schema.json"
            },
            {
              "k": "text",
              "text": " ships the structural contract\nfor "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": " workflow documents · consumed by JSON-Schema-aware tools\n(IDE plugins · yaml-language-server · CI validators · 3rd-party\nauthoring tooling)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Pre-this-ADR · the "
            },
            {
              "k": "code",
              "text": "invoke.tool"
            },
            {
              "k": "text",
              "text": " field was a permissive regex pattern ·"
            }
          ]
        },
        {
          "k": "code",
          "lang": "json",
          "text": "\"tool\": {\n  \"type\": \"string\",\n  \"pattern\": \"^(nika|mcp):[a-z][a-z0-9_]*(/[a-z][a-z0-9_]*)*$\"\n}"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This permits ANY "
            },
            {
              "k": "code",
              "text": "nika:<name>"
            },
            {
              "k": "text",
              "text": " string. yaml-language-server (the canonical\nLSP backbone for VS Code · IntelliJ · Neovim) "
            },
            {
              "k": "strong",
              "text": "cannot autocomplete from\nregex patterns"
            },
            {
              "k": "text",
              "text": " · only from closed "
            },
            {
              "k": "code",
              "text": "enum"
            },
            {
              "k": "text",
              "text": " arrays per JSON-Schema 2020-12\nspec. The cookbook author writes "
            },
            {
              "k": "code",
              "text": "tool: nika:"
            },
            {
              "k": "text",
              "text": " and gets ZERO completion\nsuggestions · friction the new-author surface absorbs disproportionately."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The G1 LSP-readiness research (per "
            },
            {
              "k": "code",
              "text": "nika-lsp-readiness-design-v0.1"
            },
            {
              "k": "text",
              "text": " ·\nD-2026-05-27-N2) established the central theorem · « one obvious way »\n(D-2026-05-22-N6 stdlib-collapse 42→26) IS « LSP-perfect » (closed\nfinite enumerable set) · they're the same property at different layers.\nThe catalog reconciliation (ADR-084) closed the engine-side · this ADR\ncloses the spec-side."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika/spec/schemas/workflow.schema.json"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "invoke.tool"
            },
            {
              "k": "text",
              "text": " field splits from\nthe single regex pattern into a "
            },
            {
              "k": "code",
              "text": "oneOf"
            },
            {
              "k": "text",
              "text": " ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "nika branch"
              },
              {
                "k": "text",
                "text": " · CLOSED enum of 26 canonical builtins (sorted\nalphabetically from "
              },
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "nika:assert"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "nika:cost"
              },
              {
                "k": "text",
                "text": " · ... · "
              },
              {
                "k": "code",
                "text": "nika:write"
              },
              {
                "k": "text",
                "text": "). Enables\nyaml-language-server LSP-perfect autocomplete for any cookbook author."
              }
            ],
            [
              {
                "k": "strong",
                "text": "mcp branch"
              },
              {
                "k": "text",
                "text": " · KEEPS the open regex pattern ("
              },
              {
                "k": "code",
                "text": "^mcp:[a-z][a-z0-9_]* (/[a-z][a-z0-9_]*)*$"
              },
              {
                "k": "text",
                "text": ") · MCP tool surface is external + plugin-dynamic ·\nclosed-enum is structurally impossible (each MCP server publishes its\nown dynamic catalog at runtime)."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Engine companion · "
            },
            {
              "k": "code",
              "text": "nika-schema::codegen::nika_builtin_tool_enum_schema()"
            },
            {
              "k": "text",
              "text": "\nemits the same enum "
            },
            {
              "k": "strong",
              "text": "derived from"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "nika_catalog::ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " at\ncall time · the no-drift gate (spec hand-derived → engine catalog\ncanonical) is enforced by "
            },
            {
              "k": "code",
              "text": "nika-schema/src/codegen.rs::tests:: enum_matches_catalog_one_to_one"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Why interim hand-derived (NOT engine-codegen output today)",
          "id": "why-interim-hand-derived-not-engine-codegen-output-today"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per LOCK-031 trigger-gate discipline (no infra behind a locked gate)"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "scope-shrink (canon NOW · code DEFER per consumer signal) ·"
              }
            ]
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "LSP value is operator-immediate"
              },
              {
                "k": "text",
                "text": " · the hand-derived oneOf gives\nyaml-language-server consumers 100% of the LSP-completion value\nTODAY · zero engine-ship dependency · zero schemars adoption ·\nzero "
              },
              {
                "k": "code",
                "text": "nika-schema-codegen"
              },
              {
                "k": "text",
                "text": " xtask required."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Schemars adoption requires nika-schema parser maturation"
              },
              {
                "k": "text",
                "text": " · the\nfull G1 ratchet ("
              },
              {
                "k": "code",
                "text": "WorkflowDocument"
              },
              {
                "k": "text",
                "text": " Rust type with "
              },
              {
                "k": "code",
                "text": "#[derive(JsonSchema)]"
              },
              {
                "k": "text",
                "text": "+ `#[schemars(schema_with = ...)]` on the `tool` field) requires\nthe WIP nika-schema parser scaffolding to reach a complete\n`WorkflowDocument` shape · today nika-schema ships parser + raw + types\nmodules but the canonical document-level type is still maturing."
              }
            ],
            [
              {
                "k": "strong",
                "text": "The bridge is the right shape"
              },
              {
                "k": "text",
                "text": " · per the schema file header comment\n(« INTERIM hand-derived schema · prose spec is the single source of\ntruth · regenerate + diff against engine "
              },
              {
                "k": "code",
                "text": "nika-schema"
              },
              {
                "k": "text",
                "text": " at GA ») · the\ndecision was always to hand-derive then codegen · this ADR locks the\ntransition mechanic."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Future ratchet (trigger-gated)",
          "id": "future-ratchet-trigger-gated"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The full schemars-adoption G1 codegen (the « next ADR-086 » slot) ships\nwhen ALL of these trigger gates clear ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "nika-schema parser maturity"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "WorkflowDocument"
              },
              {
                "k": "text",
                "text": " is the canonical\ndocument-level Rust type · all sub-types ("
              },
              {
                "k": "code",
                "text": "Task"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "Verb"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "Invoke"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "Agent"
              },
              {
                "k": "text",
                "text": " · etc.) admitted into the public surface."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Cookbook `$schema` modeline adoption"
              },
              {
                "k": "text",
                "text": " · ≥1 example in\n"
              },
              {
                "k": "code",
                "text": "nika/spec/examples/"
              },
              {
                "k": "text",
                "text": " carries "
              },
              {
                "k": "code",
                "text": "# yaml-language-server: $schema=..."
              },
              {
                "k": "text",
                "text": "\npointing to "
              },
              {
                "k": "code",
                "text": "workflow.schema.json"
              },
              {
                "k": "text",
                "text": " · proving the LSP-consumer path\nis live."
              }
            ],
            [
              {
                "k": "strong",
                "text": "LSP UX feedback signal"
              },
              {
                "k": "text",
                "text": " · ≥1 explicit operator-side incident OR\nfeature request citing « completion missed builtin X » OR\n« workflow schema autocomplete is the killer feature »."
              }
            ],
            [
              {
                "k": "strong",
                "text": "schemars 1.0 in workspace.dependencies"
              },
              {
                "k": "text",
                "text": " · today absent · added\nonly when the full "
              },
              {
                "k": "code",
                "text": "WorkflowDocument"
              },
              {
                "k": "text",
                "text": " derive is shippable."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When all 4 clear · the future ADR-086 (or ADR-085 extension) authors ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-schema-codegen/"
              },
              {
                "k": "text",
                "text": " xtask binary (or "
              },
              {
                "k": "code",
                "text": "cargo run --bin nika-schema-codegen"
              },
              {
                "k": "text",
                "text": ") that emits "
              },
              {
                "k": "code",
                "text": "workflow.schema.json"
              },
              {
                "k": "text",
                "text": " from\n"
              },
              {
                "k": "code",
                "text": "schema_for!(WorkflowDocument)"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema::codegen::nika_builtin_tool_enum_schema"
              },
              {
                "k": "text",
                "text": " becomes the\n"
              },
              {
                "k": "code",
                "text": "schema_with"
              },
              {
                "k": "text",
                "text": " adapter consumed by schemars during the derive."
              }
            ],
            [
              {
                "k": "text",
                "text": "The hand-derived "
              },
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " becomes a\nCHECK-IN CODEGEN OUTPUT (regenerable + diff-able · per Diamond Rule 7\nhallucination forbidden + Self-Evolving Olympus PILLAR 1 projection-\nby-default)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Hygiene vector "
              },
              {
                "k": "code",
                "text": "check-spec-schema-codegen-parity"
              },
              {
                "k": "text",
                "text": " (P1/fail) verifies\nspec/schemas/workflow.schema.json matches the engine codegen output\nat commit time · structural no-drift gate."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "LSP value lands now"
              },
              {
                "k": "text",
                "text": " · yaml-language-server autocompletes the 26\nbuiltins · zero engine dependency · operator wins immediately."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No-drift property structural at engine layer"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "nika_builtin_tool_enum_schema()"
              },
              {
                "k": "text",
                "text": " reads "
              },
              {
                "k": "code",
                "text": "ALL_BUILTINS"
              },
              {
                "k": "text",
                "text": " at call time ·\ncatalog mutations propagate automatically · the 8 conformance tests\nin "
              },
              {
                "k": "code",
                "text": "codegen.rs"
              },
              {
                "k": "text",
                "text": " enforce parity (26 entries · alphabetically sorted ·\nprefixed "
              },
              {
                "k": "code",
                "text": "nika:"
              },
              {
                "k": "text",
                "text": " · matches catalog · includes canonical anchors ·\nexcludes legacy post-D-N6)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Bridge is publicly documented"
              },
              {
                "k": "text",
                "text": " · the schema file header + this ADR"
              },
              {
                "k": "text",
                "text": "+ `nika-schema::codegen` module doc all cross-reference the future\nratchet · no surprise transition when ADR-086 ships."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Symbol seam established"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "pub fn nika_builtin_tool_enum_schema"
              },
              {
                "k": "text",
                "text": "\nis the seam future schemars adoption plugs into ("
              },
              {
                "k": "code",
                "text": "schema_with"
              },
              {
                "k": "text",
                "text": "\nattribute target) · the migration is mechanical when triggered."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Spec ↔ engine sync is manual today"
              },
              {
                "k": "text",
                "text": " · if "
              },
              {
                "k": "code",
                "text": "ALL_BUILTINS"
              },
              {
                "k": "text",
                "text": " mutates\n(e.g. spec adds 27th builtin via a future D-lock) · the spec schema's\nhand-derived enum must be regenerated manually · the engine no-drift\ntest catches the catalog side but NOT the schema-file side until the\nfuture hygiene vector ships."
              },
              {
                "k": "text",
                "text": "- Mitigation · spec changes to builtins are extremely rare (governed\n  by D-locks per `nika/spec/stdlib/builtins-v0.1.md` § Promotion\n  criteria · the canon is « one obvious way » per D-N6).\n- Mitigation · `nika-schema::codegen::nika_builtin_tool_enum_schema`\n  PRINTS the canonical JSON-Schema fragment a maintainer can paste\n  into the spec schema (zero invention · just copy)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Schemars dep deferred"
              },
              {
                "k": "text",
                "text": " · the full G1 codegen ratchet ships only\nwhen nika-schema parser matures + LSP UX signal · this is intentional\nscope-shrink, not weakness."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "L0 layer constraint preserved"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "codegen.rs"
              },
              {
                "k": "text",
                "text": " is pure-Rust ·\nzero I/O · zero async · "
              },
              {
                "k": "code",
                "text": "serde_json"
              },
              {
                "k": "text",
                "text": " already in nika-schema deps ·\nno new crate · no L0 violation."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Tests · +8 net new"
              },
              {
                "k": "text",
                "text": " in "
              },
              {
                "k": "code",
                "text": "nika-schema/src/codegen.rs::tests"
              },
              {
                "k": "text",
                "text": " ·\nenum_has_26_entries · enum_alphabetically_sorted · enum_all_prefixed_with_nika ·\nenum_matches_catalog_one_to_one · schema_is_string_type · schema_has_description ·\nenum_includes_canonical_anchors · enum_excludes_legacy_post_d_n6."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "A · Generate workflow.schema.json from schemars NOW",
          "id": "a--generate-workflowschemajson-from-schemars-now"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · requires "
            },
            {
              "k": "code",
              "text": "WorkflowDocument"
            },
            {
              "k": "text",
              "text": " Rust type maturation (nika-schema\nWIP) · scope creep + premature surface lock · LSP value lands without\nthis · scope-shrink discipline applies."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "B · Keep regex pattern · accept zero LSP completion",
          "id": "b--keep-regex-pattern--accept-zero-lsp-completion"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · the LSP-perfection theorem (D-2026-05-27-N2) established that\n« one obvious way » + closed-enum-schema = SAME PROPERTY · this ADR\nmaterializes the property at the spec surface."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "C · Generate the schema fragment in a build.rs at engine build time",
          "id": "c--generate-the-schema-fragment-in-a-buildrs-at-engine-build-time"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · engine build artifacts don't propagate to the spec submodule ·\nthe spec is a separate distribution · build.rs would be invisible to\nexternal schema consumers. The future codegen xtask (ADR-086) writes\nthe file at the spec submodule path · explicit + checked-in."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate verification",
          "id": "gate-verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This is a refactor of admitted-crate data + types (not a new crate\nadmission) · the ADR-003 12-gate ceremony reduces to applicable gates ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Gate 2 TDD · 8 new conformance tests in "
              },
              {
                "k": "code",
                "text": "codegen.rs"
              },
              {
                "k": "text",
                "text": " · the no-drift\ngate ("
              },
              {
                "k": "code",
                "text": "enum_matches_catalog_one_to_one"
              },
              {
                "k": "text",
                "text": ") is the canonical seam."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 3 IMPL · "
              },
              {
                "k": "code",
                "text": "cargo check --workspace"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 4 CLIPPY · "
              },
              {
                "k": "code",
                "text": "cargo clippy --workspace --all-targets -- -D warnings"
              },
              {
                "k": "text",
                "text": " 0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 8 DOCS · "
              },
              {
                "k": "code",
                "text": "cargo doc --no-deps -p nika-schema"
              },
              {
                "k": "text",
                "text": " 0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 12 ATOMIC · single engine commit."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo fmt --check"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo deny check"
              },
              {
                "k": "text",
                "text": " advisories+bans+licenses+sources ok."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gates 1 / 5 / 6 / 7 / 9 / 10 / 11 · N/A for data refactor."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Companion artefacts",
          "id": "companion-artefacts"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-schema/src/codegen.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "pub fn nika_builtin_tool_enum_schema"
              },
              {
                "k": "text",
                "text": "+ 8 conformance tests · the engine-side no-drift gate."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/lib.rs"
              },
              {
                "k": "text",
                "text": " · module registration + re-export."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " (separate submodule commit\n· invoke.tool oneOf · 26-builtin closed enum)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal G1 design + closure artefacts in private monorepo (cited by\nD-id only · audit trail preserved at the source-of-truth surface)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-084 · nika-catalog reconciliation to spec 26 (prerequisite)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-082 · envelope "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " single-version-marker"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " · the canonical 26 source-of-truth"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · D-2026-05-27-N2 LSP-perfection theorem (private blueprint)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · LOCK-031 trigger-gate discipline (private doctrine)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · scope-shrink discipline (canon NOW · code DEFER)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · cross-source-validation §2.6 Signal 7 (verify-the-auditor)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-086",
      "file": "adr-086-builtin-rams-sweep-convert-universal-multi-format.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Reference implementation (L2 nika-builtin admission · future)",
          "anchor": "reference-implementation-l2-nika-builtin-admission--future"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Gate verification",
          "anchor": "gate-verification"
        },
        {
          "title": "Future Rams ratchets (trigger-gated · ADR-087+ reserved)",
          "anchor": "future-rams-ratchets-trigger-gated--adr-087-reserved"
        },
        {
          "title": "Companion artefacts",
          "anchor": "companion-artefacts"
        },
        {
          "title": "References",
          "anchor": "references"
        }
      ],
      "words": 1642,
      "sha256": "23d34439c882b0d9ed3b1ccb68076ec10a0551328af08f7c79cc39374da955e7",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-086 · builtin Rams sweep · nika:csv_to_json → nika:convert",
          "id": "adr-086--builtin-rams-sweep--nikacsv_to_json--nikaconvert"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator-led audit 2026-05-27 of the post-D-2026-05-22-N6 26-builtin\nset surfaced 4 high-confidence Rams « less but better » consolidation\ncandidates · the operator named "
            },
            {
              "k": "code",
              "text": "nika:csv_to_json"
            },
            {
              "k": "text",
              "text": " specifically as\nthe canonical anti-pattern (single-direction · single-format · vs\nthe "
            },
            {
              "k": "code",
              "text": "fetch+extract"
            },
            {
              "k": "text",
              "text": " super-powerful-with-mode-args canonical exemplar)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator verbatim · « faut vraiment réfléchir de manière plus\natomique · "
            },
            {
              "k": "code",
              "text": "nika:csv_to_json"
            },
            {
              "k": "text",
              "text": " faudrait plutôt un builtin super\npuissant qui gère à convertir n'importe quoi · le "
            },
            {
              "k": "code",
              "text": "fetch"
            },
            {
              "k": "text",
              "text": " il est\navancé avec extract · les autres ça devrait être pareil · faut pas\nhésiter à améliorer · réunir · Rams · et chercher les meilleurs\ncrates correspondant »."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The D-N6 stdlib-collapse (42 → 27) + 2026-05-27 "
            },
            {
              "k": "code",
              "text": "json_merge"
            },
            {
              "k": "text",
              "text": " cut\n(27 → 26) established the precedent · the canonical pattern is « one\nsuper-powerful builtin · multi-mode parameters » (jq subsumes ~13\ndata builtins · fetch subsumes 8 extract modes). This ADR continues\nthe sweep at the format-conversion layer."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika:csv_to_json"
            },
            {
              "k": "text",
              "text": " removed · "
            },
            {
              "k": "code",
              "text": "nika:convert"
            },
            {
              "k": "text",
              "text": " adopted as universal\nmulti-format converter."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`nika:convert` surface",
          "id": "nikaconvert-surface"
        },
        {
          "k": "code",
          "lang": "yaml",
          "text": "invoke:\n  tool: \"nika:convert\"\n  args:\n    input: \"${{ tasks.A.output }}\"   # data to convert\n    from: csv                         # REQUIRED · enum · json | yaml | toml | csv\n    to: json                          # REQUIRED · enum · json | yaml | toml | csv\n    has_header: true                  # OPTIONAL · CSV only · default true"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Formats v0.1 baseline",
          "id": "formats-v01-baseline"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "4 formats · "
            },
            {
              "k": "code",
              "text": "json"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "yaml"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "toml"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "csv"
            },
            {
              "k": "text",
              "text": " · 12 directions in scope\n(4×3 minus identity). Forward-compat · "
            },
            {
              "k": "code",
              "text": "xml"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "tsv"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "messagepack"
            },
            {
              "k": "text",
              "text": "\netc. ADD on consumer signal per "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " ratchet (additive\nMINOR per ADR-002 · amended D-2026-06-20-N1 · real semver toward 1.0)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine catalog mutation",
          "id": "engine-catalog-mutation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-catalog::ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " (line ~21 in "
            },
            {
              "k": "code",
              "text": "data/builtins.rs"
            },
            {
              "k": "text",
              "text": ") ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "DELETE entry · "
              },
              {
                "k": "code",
                "text": "Builtin { name: \"csv_to_json\", category: Data }"
              }
            ],
            [
              {
                "k": "text",
                "text": "INSERT entry (alphabetical · between "
              },
              {
                "k": "code",
                "text": "assert"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "cost"
              },
              {
                "k": "text",
                "text": ") ·\n"
              },
              {
                "k": "code",
                "text": "Builtin { name: \"convert\", category: Data }"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Sort invariant preserved · "
            },
            {
              "k": "code",
              "text": "convert"
            },
            {
              "k": "text",
              "text": " < "
            },
            {
              "k": "code",
              "text": "cost"
            },
            {
              "k": "text",
              "text": " alphabetically ("
            },
            {
              "k": "code",
              "text": "c-o-n"
            },
            {
              "k": "text",
              "text": "\n< "
            },
            {
              "k": "code",
              "text": "c-o-s"
            },
            {
              "k": "text",
              "text": "). The 6-existing tests ("
            },
            {
              "k": "code",
              "text": "builtin_count"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "sorted_order"
            },
            {
              "k": "text",
              "text": " ·\n"
            },
            {
              "k": "code",
              "text": "find_known_builtins"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "is_known_builtin_works"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "case_sensitive"
            },
            {
              "k": "text",
              "text": " ·\n"
            },
            {
              "k": "code",
              "text": "all_builtins_have_non_empty_names"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "category_counts"
            },
            {
              "k": "text",
              "text": ") stay GREEN ·\ntotal 26 unchanged · 8 Data category unchanged."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine trust.rs mutation (per ADR-084 §C precedent)",
          "id": "engine-trustrs-mutation-per-adr-084-c-precedent"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "TRUST_PROPAGATING_BUILTINS"
            },
            {
              "k": "text",
              "text": " list ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "DELETE "
              },
              {
                "k": "code",
                "text": "\"nika:csv_to_json\""
              }
            ],
            [
              {
                "k": "text",
                "text": "INSERT "
              },
              {
                "k": "code",
                "text": "\"nika:convert\""
              },
              {
                "k": "text",
                "text": " (alphabetical · first slot)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Trust class unchanged ("
            },
            {
              "k": "code",
              "text": "csv_to_json"
            },
            {
              "k": "text",
              "text": " was PROPAGATING · "
            },
            {
              "k": "code",
              "text": "convert"
            },
            {
              "k": "text",
              "text": " stays\nPROPAGATING · pure-data transform · output trust = input trust)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine codegen.rs mutation (ADR-085 no-drift gate)",
          "id": "engine-codegenrs-mutation-adr-085-no-drift-gate"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "enum_excludes_legacy_post_d_n6"
            },
            {
              "k": "text",
              "text": " regression test extends the legacy\nexclusion list with "
            },
            {
              "k": "code",
              "text": "nika:csv_to_json"
            },
            {
              "k": "text",
              "text": " · 5 → 6 legacy names guarded\n(the cut-set regression)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Spec mutation (companion · nika/spec submodule)",
          "id": "spec-mutation-companion--nikaspec-submodule"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Spec ratchet shipped same-arc per the operator-led Rams sweep ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " · §1 Data row + §3 Data section\nrename csv_to_json → convert with full new semantics (formats list ·\npattern rationale · reference implementation crates · alphabetical\nposition preserved in §3)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " · invoke.tool oneOf nika\nbranch enum · alphabetical re-sort (convert moves before cost) ·\ncount stays 26 · LSP-perfect autocomplete preserved."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Reference implementation (L2 nika-builtin admission · future)",
          "id": "reference-implementation-l2-nika-builtin-admission--future"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Primary-source verified candidate crates (crates.io API · 2026-05-27) ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crate · Version · Updated · Notes"
              }
            ],
            [
              {
                "k": "text",
                "text": "`serde-transcode` · 1.1.1 · 2021-07-10 · Canonical orchestrator · sfackler · 15M+ downloads · API-stable (COMPLETE not abandoned)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`serde_json` · 1.0 · (already nika dep) · JSON · zero-add"
              }
            ],
            [
              {
                "k": "text",
                "text": "`serde_yaml_bw` · 2.5.6 · 2026-05-02 · YAML · modern + maintained · 133k downloads · 2026-current"
              }
            ],
            [
              {
                "k": "text",
                "text": "`toml` · 1.1.2 · 2026-04-01 · TOML · spec 1.1.0 compliant · active"
              }
            ],
            [
              {
                "k": "text",
                "text": "`csv` · 1.4.0 · 2025-10-17 · CSV · quoting-aware · active"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Cohérent internal NUKE-LEGACY Class 2 (git is the archive · zero\ndeprecation alias) · zero-add prefer when an existing dep covers\nthe case · "
            },
            {
              "k": "code",
              "text": "serde_json"
            },
            {
              "k": "text",
              "text": " already in workspace. New deps gated on the\nL2 nika-builtin admission ceremony per Diamond Rule 2 12-gate."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Symmetry signal"
              },
              {
                "k": "text",
                "text": " · per « less but better » Rams principle · the\nfetch+extract pattern · jq subsumption · convert · all the same\n« one super-powerful builtin · multi-mode args » canonical shape\napplied at 3 distinct layers (HTTP · data transform · format\nconversion). The pattern propagates the more it's applied · easier\ncognitive surface for cookbook authors + LSP consumers."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Bidirectional coverage"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "nika:convert from:csv to:json"
              },
              {
                "k": "text",
                "text": " AND\n"
              },
              {
                "k": "code",
                "text": "nika:convert from:json to:csv"
              },
              {
                "k": "text",
                "text": " (the latter previously covered only\nvia "
              },
              {
                "k": "code",
                "text": "jq @csv"
              },
              {
                "k": "text",
                "text": ") · single canonical builtin · no « which direction\ngoes through which tool » mental load."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Forward extensibility"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "xml"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "tsv"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "messagepack"
              },
              {
                "k": "text",
                "text": " add via\nenum extension (forward-compat MINOR ratchet per ADR-002 · amended\nD-2026-06-20-N1 · zero new builtin slot consumed)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Trust class preserved"
              },
              {
                "k": "text",
                "text": " · csv_to_json was PROPAGATING · convert\nstays PROPAGATING · zero security regression."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No-drift gate enforced"
              },
              {
                "k": "text",
                "text": " · ADR-085 codegen.rs test\n"
              },
              {
                "k": "code",
                "text": "enum_matches_catalog_one_to_one"
              },
              {
                "k": "text",
                "text": " AUTO-detects the spec ↔ engine\nparity · the rename propagates automatically once ALL_BUILTINS\nmutates · "
              },
              {
                "k": "code",
                "text": "nika_builtin_tool_enum_schema()"
              },
              {
                "k": "text",
                "text": " returns the new 26-set\nderived at call time."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Public API breaking"
              },
              {
                "k": "text",
                "text": " · existing workflows using "
              },
              {
                "k": "code",
                "text": "tool: nika:csv_to_json"
              },
              {
                "k": "text",
                "text": "\nwill fail to parse against the spec-26 closed enum + against\n"
              },
              {
                "k": "code",
                "text": "is_known_builtin(\"csv_to_json\")"
              },
              {
                "k": "text",
                "text": " in engine. Acceptable on forever-\nv0.x per ADR-002 (NUKE-LEGACY · git is the archive · zero deprecation\nalias per internal no-legacy doctrine)."
              },
              {
                "k": "text",
                "text": "- Mitigation · the existing nika spec is pre-v1.0 GA · cookbook\n  authors haven't started migration · breakage cost ≈ 0 today.\n- Mitigation · the trust.rs test `legacy_builtins_unknown_post_d_n6`\n  explicitly catches any code path that still references\n  `\"nika:csv_to_json\"` · fails closed (uncategorized → Untrusted\n  output)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Tests · +1 net new legacy guard"
              },
              {
                "k": "text",
                "text": " in codegen.rs · 8 conformance\ntests stay GREEN (6 from baseline + 2 spec-26 gates from ADR-084) ·\nlegacy exclusion list 5 → 6."
              }
            ],
            [
              {
                "k": "strong",
                "text": "L0 layer constraint preserved"
              },
              {
                "k": "text",
                "text": " · the rename is pure data + pure\ntest mutation · zero I/O · zero async · zero L0 violation."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Backward graph projection (TTL)"
              },
              {
                "k": "text",
                "text": " · internal monorepo nika-\nlanguage graph carries 4 csv_to_json refs (instance · Builtin\nfamily member-list · "
              },
              {
                "k": "code",
                "text": "raises"
              },
              {
                "k": "text",
                "text": " arc · "
              },
              {
                "k": "code",
                "text": "invokedBy"
              },
              {
                "k": "text",
                "text": " arc) · these\nupdate SAME-ARC in the monorepo cascade (separate from this\nengine ADR)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "A · Keep `nika:csv_to_json` · add `nika:yaml_to_json` · `nika:toml_to_json` · etc.",
          "id": "a--keep-nikacsv_to_json--add-nikayaml_to_json--nikatoml_to_json--etc"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · N² builtin slot explosion · 4 formats × 3 target = 12\nbuiltins for what should be 1 · violates « less but better » Rams\nprinciple + the D-N6 anti-anti-pattern. Per existing spec comment\n« CSV parsing is not jq's job · the reverse direction is jq @csv » ·\nthe asymmetric treatment was exactly the friction the operator\naudit named."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "B · Keep `csv_to_json` only · skip the Rams sweep",
          "id": "b--keep-csv_to_json-only--skip-the-rams-sweep"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · operator-led directive (« FAIS · do it ») + the symmetry\nsignal (fetch+extract · jq · now convert · all the same « one\nsuper-powerful builtin · multi-mode args » pattern) · skipping leaves\nthe canonical anti-pattern in the spec."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "C · Adopt `dasel` library or similar all-in-one Go-origin tool",
          "id": "c--adopt-dasel-library-or-similar-all-in-one-go-origin-tool"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · "
            },
            {
              "k": "code",
              "text": "dasel"
            },
            {
              "k": "text",
              "text": " is a Go binary · embedding requires unsavory\nsubprocess shenanigans · violates L0 « zero I/O · zero async » +\nsovereignty Rule 1 + ADR-001 pure-Rust mandate. "
            },
            {
              "k": "code",
              "text": "serde-transcode"
            },
            {
              "k": "text",
              "text": " is\nthe canonical serde-ecosystem Rust solution · primary-source verified."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate verification",
          "id": "gate-verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per ADR-084 precedent (data refactor not crate admission · 12-gate\nreduces to applicable gates) ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Gate 2 TDD · trust.rs "
              },
              {
                "k": "code",
                "text": "legacy_builtins_unknown_post_d_n6"
              },
              {
                "k": "text",
                "text": " extended\n· codegen.rs "
              },
              {
                "k": "code",
                "text": "enum_excludes_legacy_post_d_n6"
              },
              {
                "k": "text",
                "text": " extended · existing\nbuiltins.rs "
              },
              {
                "k": "code",
                "text": "category_counts"
              },
              {
                "k": "text",
                "text": " (Data still 8) preserved · all GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 3 IMPL · "
              },
              {
                "k": "code",
                "text": "cargo check --workspace"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 4 CLIPPY · "
              },
              {
                "k": "code",
                "text": "cargo clippy --workspace --all-targets -- -D warnings"
              },
              {
                "k": "text",
                "text": " 0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 8 DOCS · "
              },
              {
                "k": "code",
                "text": "cargo doc --no-deps -p nika-catalog -p nika-schema"
              },
              {
                "k": "text",
                "text": "\n0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 12 ATOMIC · single engine commit + single monorepo bump."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo fmt --check"
              },
              {
                "k": "text",
                "text": " GREEN · "
              },
              {
                "k": "code",
                "text": "cargo deny check"
              },
              {
                "k": "text",
                "text": " ok."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gates 1 / 5 / 6 / 7 / 9 / 10 / 11 · N/A for data refactor."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Future Rams ratchets (trigger-gated · ADR-087+ reserved)",
          "id": "future-rams-ratchets-trigger-gated--adr-087-reserved"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "3 remaining consolidation candidates from the operator audit ·\ntrigger-gated per LOCK-031 spirit · don't pre-build infra ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "`nika:wait`"
              },
              {
                "k": "text",
                "text": " · collapse "
              },
              {
                "k": "code",
                "text": "sleep"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "wait_until"
              },
              {
                "k": "text",
                "text": " into one temporal\nprimitive with "
              },
              {
                "k": "code",
                "text": "duration:"
              },
              {
                "k": "text",
                "text": " OR "
              },
              {
                "k": "code",
                "text": "until:"
              },
              {
                "k": "text",
                "text": " mode. Net 26 → 25. Trigger ·\nD-lock + operator OUI/NON Bucket-C user-explicit-lock."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`nika:inspect`"
              },
              {
                "k": "text",
                "text": " · collapse 4 introspection builtins (cost ·\nrecords · dag_info · threads) into one with "
              },
              {
                "k": "code",
                "text": "view:"
              },
              {
                "k": "text",
                "text": " enum. Net 26 →"
              },
              {
                "k": "text",
                "text": "23. Trigger · D-lock + workflow.schema.json oneOf-vs-untyped design\ndecision (return-shape heterogeneity)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`nika:json_diff` jq-subsume feasibility"
              },
              {
                "k": "text",
                "text": " · jq CAN express\ndiff (recipe in audit doc §5) but loses the RFC-6902-canonical-Patch\noutput format. Trigger · cookbook adoption of the jq-diff recipe +\noperator preference signal."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When all 3 land · 26 → ~21 collapse · same "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": " envelope ·\nreal-semver-toward-1.0 discipline (ADR-002 · amended D-2026-06-20-N1)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Companion artefacts",
          "id": "companion-artefacts"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-catalog/src/data/builtins.rs"
              },
              {
                "k": "text",
                "text": " · ALL_BUILTINS rename (delete\ncsv_to_json · insert convert in alphabetical position)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/trust.rs"
              },
              {
                "k": "text",
                "text": " · TRUST_PROPAGATING_BUILTINS rename +\n"
              },
              {
                "k": "code",
                "text": "legacy_builtins_unknown_post_d_n6"
              },
              {
                "k": "text",
                "text": " test extended (+csv_to_json)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/codegen.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "enum_excludes_legacy_post_d_n6"
              },
              {
                "k": "text",
                "text": "\ntest extended (+csv_to_json · 5 → 6 legacy guarded)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " (separate submodule commit\n"
              },
              {
                "k": "code",
                "text": "2a14540"
              },
              {
                "k": "text",
                "text": ") · csv_to_json section → convert section with universal\nmulti-format semantics."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " (same submodule commit) ·\ninvoke.tool oneOf nika branch enum alphabetical re-sort."
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · Rams audit doc (private monorepo) · the 26-builtin\nbuiltin-by-builtin table + 4-collapse proposal + crate research +\nshipping plan."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-085 · spec workflow.schema.json oneOf bridge (prerequisite)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-084 · nika-catalog reconciliation to spec 26 (prerequisite)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-082 · envelope "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " single-version-marker"
              }
            ],
            [
              {
                "k": "text",
                "text": "D-2026-05-22-N6 · stdlib-collapse 42 → 27 (precedent · « one\nobvious way » + jq subsumption)"
              }
            ],
            [
              {
                "k": "text",
                "text": "2026-05-27 · "
              },
              {
                "k": "code",
                "text": "nika:json_merge"
              },
              {
                "k": "text",
                "text": " cut (precedent · razor-edge Rams\ndecision + jaq source-verified)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · "
              },
              {
                "k": "code",
                "text": "no-legacy-no-back-compat.md"
              },
              {
                "k": "text",
                "text": " Class 2 (git is the\narchive)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · LOCK-031 trigger-gated discipline (no infra behind\nlocked gate · future ratchet 1/2/3 deferred)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · "
              },
              {
                "k": "code",
                "text": "cross-source-validation.md"
              },
              {
                "k": "text",
                "text": " §2.6 Signal 7\n(verify-the-auditor · crate claims primary-source-verified via\ncrates.io API)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-087",
      "file": "adr-087-nika-wait-temporal-collapse.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Gate verification",
          "anchor": "gate-verification"
        },
        {
          "title": "Future Rams ratchets (trigger-gated)",
          "anchor": "future-rams-ratchets-trigger-gated"
        },
        {
          "title": "Companion artefacts",
          "anchor": "companion-artefacts"
        },
        {
          "title": "References",
          "anchor": "references"
        }
      ],
      "words": 1542,
      "sha256": "59c7a5ea94cabe07bb6620e042b61eae74b6539ba604838d5b216317fcc0038d",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-087 · nika:sleep + nika:wait_until → nika:wait",
          "id": "adr-087--nikasleep--nikawait_until--nikawait"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator-led Rams audit 2026-05-27 of the canonical builtin set\nidentified "
            },
            {
              "k": "code",
              "text": "nika:sleep"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "nika:wait_until"
            },
            {
              "k": "text",
              "text": " as two temporal-control\nprimitives with overlapping semantics ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika:sleep"
              },
              {
                "k": "text",
                "text": " · pause for a "
              },
              {
                "k": "strong",
                "text": "relative"
              },
              {
                "k": "text",
                "text": " duration (Go-duration string ·\n"
              },
              {
                "k": "code",
                "text": "\"5s\""
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "\"30m\""
              },
              {
                "k": "text",
                "text": " · ...). Trust PURE."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika:wait_until"
              },
              {
                "k": "text",
                "text": " · pause until an "
              },
              {
                "k": "strong",
                "text": "absolute"
              },
              {
                "k": "text",
                "text": " ISO 8601 timestamp.\nTrust PURE."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator verbatim · « "
            },
            {
              "k": "code",
              "text": "nika:wait_until"
            },
            {
              "k": "text",
              "text": " ça pourra pas être le sleep\naussi ? ». The answer is structural · BOTH are « temporal pause »\noperations · same trust class · same semantic family · the split was\nhistorical (separate names · separate args) not architectural (the\noperation is the same · just relative vs absolute mode)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per « less but better » Rams principle + the canonical « one super-\npowerful builtin · multi-mode args » pattern established by ADR-086\n(convert) + fetch+extract + jq subsumption · ADR-087 unifies the two\ntemporal builtins into a single "
            },
            {
              "k": "code",
              "text": "nika:wait"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika:sleep"
            },
            {
              "k": "text",
              "text": " and "
            },
            {
              "k": "code",
              "text": "nika:wait_until"
            },
            {
              "k": "text",
              "text": " removed · "
            },
            {
              "k": "code",
              "text": "nika:wait"
            },
            {
              "k": "text",
              "text": " adopted as\nunified temporal-control builtin."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`nika:wait` surface",
          "id": "nikawait-surface"
        },
        {
          "k": "code",
          "lang": "yaml",
          "text": "invoke:\n  tool: \"nika:wait\"\n  args:\n    duration: \"5s\"                   # RELATIVE · Go-duration string (\"500ms\"/\"30s\"/\"5m\"/\"1h30m\")\n    # OR (mutually exclusive · exactly-one-of) ·\n    until: \"2026-05-23T09:00:00Z\"    # ABSOLUTE · ISO 8601 timestamp · MAY be CEL expression\n    timeout: \"1h\"                    # OPTIONAL · cap for absolute wait (until: only)"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Mode semantics",
          "id": "mode-semantics"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "duration:"
              },
              {
                "k": "text",
                "text": " mode · relative pause (sleep equivalent). Engine parses\nGo-duration string (\"500ms\" · \"30s\" · \"5m\" · \"1h30m\") · validates non-\nnegative · returns no value (side-effect timer)."
              }
            ],
            [
              {
                "k": "code",
                "text": "until:"
              },
              {
                "k": "text",
                "text": " mode · absolute pause (wait_until equivalent). Engine parses\nISO 8601 timestamp (with timezone · or assumes UTC if naive) · MAY be a\nCEL "
              },
              {
                "k": "code",
                "text": "${{ }}"
              },
              {
                "k": "text",
                "text": " expression · "
              },
              {
                "k": "code",
                "text": "timeout:"
              },
              {
                "k": "text",
                "text": " caps the wait duration."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Exactly-one-of validation",
          "id": "exactly-one-of-validation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Parse-time check · the builtin args MUST set exactly one of "
            },
            {
              "k": "code",
              "text": "duration:"
            },
            {
              "k": "text",
              "text": "\nor "
            },
            {
              "k": "code",
              "text": "until:"
            },
            {
              "k": "text",
              "text": ". Both set OR neither set = "
            },
            {
              "k": "code",
              "text": "NIKA-BUILTIN-WAIT-003"
            },
            {
              "k": "text",
              "text": " error."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Error codes",
          "id": "error-codes"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "NIKA-BUILTIN-WAIT-001"
              },
              {
                "k": "text",
                "text": " · absolute timeout exceeded (only when "
              },
              {
                "k": "code",
                "text": "until:"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "NIKA-BUILTIN-WAIT-002"
              },
              {
                "k": "text",
                "text": " · timestamp in past (only when "
              },
              {
                "k": "code",
                "text": "until:"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "NIKA-BUILTIN-WAIT-003"
              },
              {
                "k": "text",
                "text": " · NEW · neither "
              },
              {
                "k": "code",
                "text": "duration:"
              },
              {
                "k": "text",
                "text": " nor "
              },
              {
                "k": "code",
                "text": "until:"
              },
              {
                "k": "text",
                "text": " set OR\nboth set (exactly-one-of violation)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine catalog mutation",
          "id": "engine-catalog-mutation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-catalog::ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "DELETE 2 entries · "
              },
              {
                "k": "code",
                "text": "Builtin { name: \"sleep\", category: Core }"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "Builtin { name: \"wait_until\", category: Core }"
              }
            ],
            [
              {
                "k": "text",
                "text": "INSERT 1 entry (alphabetical · between "
              },
              {
                "k": "code",
                "text": "validate"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "write"
              },
              {
                "k": "text",
                "text": ") ·\n"
              },
              {
                "k": "code",
                "text": "Builtin { name: \"wait\", category: Core }"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Sort invariant preserved · "
            },
            {
              "k": "code",
              "text": "wait"
            },
            {
              "k": "text",
              "text": " < "
            },
            {
              "k": "code",
              "text": "write"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "a"
            },
            {
              "k": "text",
              "text": " < "
            },
            {
              "k": "code",
              "text": "r"
            },
            {
              "k": "text",
              "text": ") · "
            },
            {
              "k": "code",
              "text": "wait"
            },
            {
              "k": "text",
              "text": " >\n"
            },
            {
              "k": "code",
              "text": "validate"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "v"
            },
            {
              "k": "text",
              "text": " < "
            },
            {
              "k": "code",
              "text": "w"
            },
            {
              "k": "text",
              "text": "). Total · 26 → "
            },
            {
              "k": "strong",
              "text": "25"
            },
            {
              "k": "text",
              "text": ". Core category · 7 → "
            },
            {
              "k": "strong",
              "text": "6"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine trust.rs mutation",
          "id": "engine-trustrs-mutation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "TRUST_PURE_BUILTINS"
            },
            {
              "k": "text",
              "text": " list ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "DELETE "
              },
              {
                "k": "code",
                "text": "\"nika:sleep\""
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "\"nika:wait_until\""
              }
            ],
            [
              {
                "k": "text",
                "text": "INSERT "
              },
              {
                "k": "code",
                "text": "\"nika:wait\""
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Trust class unchanged (both were PURE · wait stays PURE · pure\ncontrol-flow timer · no I/O · no data flow). Total PURE 14 → "
            },
            {
              "k": "strong",
              "text": "13"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine codegen.rs mutation (ADR-085 no-drift gate)",
          "id": "engine-codegenrs-mutation-adr-085-no-drift-gate"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "enum_excludes_legacy_post_d_n6"
              },
              {
                "k": "text",
                "text": " · ADD "
              },
              {
                "k": "code",
                "text": "\"nika:sleep\""
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "\"nika:wait_until\""
              },
              {
                "k": "text",
                "text": "\nto legacy exclusion list (6 → 8 legacy names guarded)."
              }
            ],
            [
              {
                "k": "code",
                "text": "enum_includes_canonical_anchors"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "\"nika:sleep\""
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "\"nika:wait\""
              },
              {
                "k": "text",
                "text": " (the\ncanonical-anchor sentinel for Core category)."
              }
            ],
            [
              {
                "k": "code",
                "text": "enum_has_26_entries"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "enum_has_25_entries"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine catalog test mutations",
          "id": "engine-catalog-test-mutations"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "builtin_count"
              },
              {
                "k": "text",
                "text": " · 26 → 25"
              }
            ],
            [
              {
                "k": "code",
                "text": "category_counts"
              },
              {
                "k": "text",
                "text": " · Core 7 → 6 · total 25"
              }
            ],
            [
              {
                "k": "code",
                "text": "find_known_builtins"
              },
              {
                "k": "text",
                "text": " · sleep → wait sentinel"
              }
            ],
            [
              {
                "k": "code",
                "text": "is_known_builtin_works"
              },
              {
                "k": "text",
                "text": " · sleep is now legacy (not categorized) · wait\nis the canonical · ADD sleep + wait_until to negative-assertion list"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Spec mutation (companion · nika/spec submodule `8372ca2`)",
          "id": "spec-mutation-companion--nikaspec-submodule-8372ca2"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " · §0 + §1 Core count 26 → 25 · 7 →\n6 · §3 Core section DELETE sleep + wait_until subsections · ADD wait\nsubsection with full duration:/until:/timeout: API · 3 error codes\ndocumented."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " · invoke.tool oneOf nika\nbranch enum · DELETE 2 entries · ADD 1 · 26 → 25 · description string\nupdated (Core 7 → 6)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/spec/06-stdlib-contract.md"
              },
              {
                "k": "text",
                "text": " · enumeration line + total\nupdated · sleep + wait_until merge cited."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Symmetry signal extends"
              },
              {
                "k": "text",
                "text": " · same « one super-powerful builtin · multi-\nmode args » canonical pattern now realized at 4 distinct layers ·\nHTTP (fetch+extract) · data transform (jq subsumption) · format\nconversion (convert) · temporal control (wait). Cookbook authors +\nLSP consumers get consistent cognitive surface."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Unified parse-time validation"
              },
              {
                "k": "text",
                "text": " · the exactly-one-of check is a\nsingle canonical surface · the prior split required the engine to\nunderstand TWO trust paths + TWO arg shapes for what was always one\nfamily."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Trust class preserved"
              },
              {
                "k": "text",
                "text": " · both were PURE · wait stays PURE · zero\nsecurity regression."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No-drift gate auto-validates"
              },
              {
                "k": "text",
                "text": " · ADR-085 codegen.rs test\n"
              },
              {
                "k": "code",
                "text": "enum_matches_catalog_one_to_one"
              },
              {
                "k": "text",
                "text": " AUTO-detects the spec ↔ engine\nparity · the rename propagates automatically through\n"
              },
              {
                "k": "code",
                "text": "nika_builtin_tool_enum_schema()"
              },
              {
                "k": "text",
                "text": " since it reads ALL_BUILTINS at call\ntime."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Public API breaking"
              },
              {
                "k": "text",
                "text": " · existing workflows using "
              },
              {
                "k": "code",
                "text": "tool: nika:sleep"
              },
              {
                "k": "text",
                "text": "\nOR "
              },
              {
                "k": "code",
                "text": "tool: nika:wait_until"
              },
              {
                "k": "text",
                "text": " fail against the spec-25 closed enum +\n"
              },
              {
                "k": "code",
                "text": "is_known_builtin"
              },
              {
                "k": "text",
                "text": " returns false for both. Acceptable per ADR-002\n(amended D-2026-06-20-N1 · real semver toward 1.0 · pre-1.0 breakage OK ·\nNUKE-LEGACY · git is the archive · zero deprecation alias)."
              },
              {
                "k": "text",
                "text": "- Mitigation · spec is pre-v1.0 GA · cookbook adoption is early ·\n  breakage cost ≈ 0 today.\n- Mitigation · the trust.rs `legacy_builtins_unknown_post_d_n6` test\n  extends with both legacy names · fail-closed regression guard."
              }
            ],
            [
              {
                "k": "strong",
                "text": "New error code"
              },
              {
                "k": "text",
                "text": " · NIKA-BUILTIN-WAIT-003 (exactly-one-of) is new ·\ndocumented in the spec error namespace · engine impl gates on the L2\nnika-builtin admission (future)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Tests · +2 net new legacy guards"
              },
              {
                "k": "text",
                "text": " in trust.rs +  +2 in codegen.rs ·\nexisting 6 conformance tests + 2 spec-25 gates stay GREEN (renamed ·\ntotal counts asserted)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "L0 layer constraint preserved"
              },
              {
                "k": "text",
                "text": " · pure data + pure test mutation ·\nzero L0 violation."
              }
            ],
            [
              {
                "k": "strong",
                "text": "TTL graph projection"
              },
              {
                "k": "text",
                "text": " · monorepo "
              },
              {
                "k": "code",
                "text": "nika-language-v1.ttl"
              },
              {
                "k": "text",
                "text": " updates ·\n4 sleep refs + 4 wait_until refs → 4 wait refs (instance · family\nmember-list · raises arc · invokedBy arc) · count 26 → 25 in family\nlist."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "A · Keep both builtins · accept the duplication",
          "id": "a--keep-both-builtins--accept-the-duplication"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · the operator-led directive is explicit (« faut pas hésiter à\naméliorer · réunir · Rams ») · the temporal duplication is the\ncanonical « less but better » anti-pattern · keeping it leaves the\nsymmetry signal incomplete (3-of-4 canonical layers Rams'd · skipping\nthis layer is structural inconsistency)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "B · Two flat builtins with shared trust list",
          "id": "b--two-flat-builtins-with-shared-trust-list"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · doesn't address the operator's directive · just shuffles\nthe trust.rs list without consolidating the builtin surface · cookbook\nauthors still need to choose sleep vs wait_until · cognitive load\nunchanged."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "C · `nika:wait` with mode discriminator field (mode: \"relative\"|\"absolute\")",
          "id": "c--nikawait-with-mode-discriminator-field-mode-relativeabsolute"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · adds redundant ceremony · the discriminator is already\nimplicit in WHICH arg is present ("
            },
            {
              "k": "code",
              "text": "duration:"
            },
            {
              "k": "text",
              "text": " ↔ relative · "
            },
            {
              "k": "code",
              "text": "until:"
            },
            {
              "k": "text",
              "text": " ↔\nabsolute) · the exactly-one-of check is the cleaner self-documenting\nsurface. Operator-facing prose just says « set duration OR until »."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate verification",
          "id": "gate-verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per ADR-084/086 precedent (data refactor not crate admission · 12-gate\nreduces to applicable gates) ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Gate 2 TDD · trust.rs + codegen.rs tests updated · 226 nika-catalog"
              },
              {
                "k": "text",
                "text": "+ 205 nika-schema lib tests GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 3 IMPL · "
              },
              {
                "k": "code",
                "text": "cargo check --workspace"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 4 CLIPPY · "
              },
              {
                "k": "code",
                "text": "cargo clippy --workspace --all-targets -- -D warnings"
              },
              {
                "k": "text",
                "text": " 0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 8 DOCS · "
              },
              {
                "k": "code",
                "text": "cargo doc --no-deps -p nika-catalog -p nika-schema"
              },
              {
                "k": "text",
                "text": "\n0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 12 ATOMIC · single engine commit + single monorepo bump."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo fmt --check"
              },
              {
                "k": "text",
                "text": " GREEN (post auto-fmt)."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo deny check"
              },
              {
                "k": "text",
                "text": " advisories+bans+licenses+sources ok (no new deps)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gates 1 / 5 / 6 / 7 / 9 / 10 / 11 · N/A for data refactor."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Future Rams ratchets (trigger-gated)",
          "id": "future-rams-ratchets-trigger-gated"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Remaining from the operator-led 26-set audit ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "ADR-088 · `nika:inspect`"
              },
              {
                "k": "text",
                "text": " · collapse 4 introspection builtins\n(cost · records · dag_info · threads) into one with "
              },
              {
                "k": "code",
                "text": "view:"
              },
              {
                "k": "text",
                "text": " enum.\nNet 25 → 22. Trigger · workflow.schema.json oneOf vs untyped design\ndecision (return-shape heterogeneity)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-089 · `nika:json_diff` jq-subsume feasibility"
              },
              {
                "k": "text",
                "text": " · jq CAN\nexpress diff (recipe documented internally) but loses RFC-6902\nPatch output format. Trigger · cookbook adoption of jq-diff recipe"
              },
              {
                "k": "text",
                "text": "+ operator preference signal."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When all land · 25 → ~21 collapse. Same "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": " envelope · forever-\nv0.x discipline."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Companion artefacts",
          "id": "companion-artefacts"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-catalog/src/data/builtins.rs"
              },
              {
                "k": "text",
                "text": " · ALL_BUILTINS · DELETE 2 ·\nINSERT 1 · 26 → 25 · test counts adjusted."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog/src/types/builtin.rs"
              },
              {
                "k": "text",
                "text": " · BuiltinCategory Core doc-comment\nrefreshed ("
              },
              {
                "k": "code",
                "text": "log · emit · assert · prompt · done · wait"
              },
              {
                "k": "text",
                "text": " · 7 → 6)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog/src/lib.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "all_builtins_non_empty"
              },
              {
                "k": "text",
                "text": " test 26 → 25."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/trust.rs"
              },
              {
                "k": "text",
                "text": " · TRUST_PURE_BUILTINS rename + legacy\nexclusion test extended +sleep +wait_until + "
              },
              {
                "k": "code",
                "text": "category_totals_match_ spec_26"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "_25"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/codegen.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "enum_has_26_entries"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "_25_entries"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "enum_includes_canonical_anchors"
              },
              {
                "k": "text",
                "text": " sleep → wait + "
              },
              {
                "k": "code",
                "text": "enum_excludes_legacy_ post_d_n6"
              },
              {
                "k": "text",
                "text": " extended."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " (submodule "
              },
              {
                "k": "code",
                "text": "8372ca2"
              },
              {
                "k": "text",
                "text": ") · sleep +\nwait_until sections deleted · wait section added · counts updated."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " (same submodule commit) ·\ninvoke.tool oneOf nika branch enum 26 → 25."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/spec/06-stdlib-contract.md"
              },
              {
                "k": "text",
                "text": " (same submodule commit) ·\nenumeration + total updated."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-086 · csv_to_json → convert (prerequisite · same Rams sweep)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-085 · spec workflow.schema.json oneOf bridge (no-drift gate)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-084 · nika-catalog reconciliation to spec 26 (foundation)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-082 · envelope "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " single-version-marker"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · LOCK-031 trigger-gated discipline (no infra behind locked\ngate · ADR-088 + ADR-089 deferred)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · cross-source-validation §2.6 Signal 7 (verify-the-auditor)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · no-legacy-no-back-compat Class 2 (git is the archive)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-088",
      "file": "adr-088-nika-inspect-introspection-collapse.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Gate verification",
          "anchor": "gate-verification"
        },
        {
          "title": "Rams sweep · COMPLETE state post-ADR-088",
          "anchor": "rams-sweep--complete-state-post-adr-088"
        },
        {
          "title": "Optional future Rams ratchets (LOCK-031 trigger-gated)",
          "anchor": "optional-future-rams-ratchets-lock-031-trigger-gated"
        },
        {
          "title": "Companion artefacts",
          "anchor": "companion-artefacts"
        },
        {
          "title": "References",
          "anchor": "references"
        }
      ],
      "words": 1662,
      "sha256": "40ead882ff20f5974fca84be6edae779d2f7f8187bc2b86c3f47aace5d6edfb5",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-088 · 4 introspection builtins → nika:inspect",
          "id": "adr-088--4-introspection-builtins--nikainspect"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator-led Rams audit 2026-05-27 of the canonical builtin set\nidentified the 4 introspection primitives as a Rams collapse candidate ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika:cost"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ total_usd, by_task, by_provider }"
              },
              {
                "k": "text",
                "text": " · workflow cost"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika:records"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ tasks: [{ id, status, duration_ms, ... }] }"
              },
              {
                "k": "text",
                "text": " · execution record"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika:dag_info"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ nodes, edges, waves }"
              },
              {
                "k": "text",
                "text": " · DAG topology"
              }
            ],
            [
              {
                "k": "code",
                "text": "nika:threads"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ active, queued, completed }"
              },
              {
                "k": "text",
                "text": " · task-pool state"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "All 4 are PURE trust · pure-compute queries of own workflow state · same\nsemantic family. The split into 4 separate builtins was historical\n(one-per-return-shape) not structural. ADR-088 unifies them into a\nsingle view-discriminated "
            },
            {
              "k": "code",
              "text": "nika:inspect"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This is the 4th and most ambitious Rams collapse in the operator-led\nsweep · completes the « one super-powerful builtin · multi-mode args »\nsymmetry across 5 distinct domains."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika:cost"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika:records"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika:dag_info"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "nika:threads"
            },
            {
              "k": "text",
              "text": " removed ·\n"
            },
            {
              "k": "code",
              "text": "nika:inspect"
            },
            {
              "k": "text",
              "text": " adopted as unified introspection builtin with "
            },
            {
              "k": "code",
              "text": "view:"
            },
            {
              "k": "text",
              "text": "\nenum discriminator."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "`nika:inspect` surface",
          "id": "nikainspect-surface"
        },
        {
          "k": "code",
          "lang": "yaml",
          "text": "invoke:\n  tool: \"nika:inspect\"\n  args:\n    view: cost                     # REQUIRED · enum · cost | records | dag_info | threads"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Per-view return shapes",
          "id": "per-view-return-shapes"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "view: cost"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ total_usd, by_task, by_provider }"
              },
              {
                "k": "text",
                "text": " (sleep equivalent of legacy "
              },
              {
                "k": "code",
                "text": "nika:cost"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "view: records"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ tasks: [{ id, status, duration_ms, ... }] }"
              },
              {
                "k": "text",
                "text": " (legacy "
              },
              {
                "k": "code",
                "text": "nika:records"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "view: dag_info"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ nodes, edges, waves }"
              },
              {
                "k": "text",
                "text": " (legacy "
              },
              {
                "k": "code",
                "text": "nika:dag_info"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "view: threads"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "{ active, queued, completed }"
              },
              {
                "k": "text",
                "text": " · advisory (legacy "
              },
              {
                "k": "code",
                "text": "nika:threads"
              },
              {
                "k": "text",
                "text": ")"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Validation",
          "id": "validation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Parse-time check · "
            },
            {
              "k": "code",
              "text": "view:"
            },
            {
              "k": "text",
              "text": " MUST be one of the 4 canonical enum values.\nEngine returns "
            },
            {
              "k": "code",
              "text": "NIKA-BUILTIN-INSPECT-001"
            },
            {
              "k": "text",
              "text": " if "
            },
            {
              "k": "code",
              "text": "view:"
            },
            {
              "k": "text",
              "text": " value not in the\ncanonical set."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine catalog mutation",
          "id": "engine-catalog-mutation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-catalog::ALL_BUILTINS"
            },
            {
              "k": "text",
              "text": " ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "DELETE 4 entries · "
              },
              {
                "k": "code",
                "text": "Builtin { name: \"cost\", category: Introspection }"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "Builtin { name: \"records\", category: Introspection }"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "Builtin { name: \"dag_info\", category: Introspection }"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "Builtin { name: \"threads\", category: Introspection }"
              }
            ],
            [
              {
                "k": "text",
                "text": "INSERT 1 entry (alphabetical · between "
              },
              {
                "k": "code",
                "text": "hash"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "jq"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "h-a-s"
              },
              {
                "k": "text",
                "text": " <\n"
              },
              {
                "k": "code",
                "text": "i-n-s"
              },
              {
                "k": "text",
                "text": " < "
              },
              {
                "k": "code",
                "text": "j-q"
              },
              {
                "k": "text",
                "text": " verified) · "
              },
              {
                "k": "code",
                "text": "Builtin { name: \"inspect\", category: Introspection }"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Sort invariant preserved. Total · 25 → "
            },
            {
              "k": "strong",
              "text": "22"
            },
            {
              "k": "text",
              "text": ". Introspection category ·\n4 → "
            },
            {
              "k": "strong",
              "text": "1"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine trust.rs mutation",
          "id": "engine-trustrs-mutation"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "TRUST_PURE_BUILTINS"
            },
            {
              "k": "text",
              "text": " list ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "DELETE "
              },
              {
                "k": "code",
                "text": "\"nika:cost\""
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "\"nika:records\""
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "\"nika:dag_info\""
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "\"nika:threads\""
              }
            ],
            [
              {
                "k": "text",
                "text": "INSERT "
              },
              {
                "k": "code",
                "text": "\"nika:inspect\""
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Trust class unchanged (all 4 were PURE · inspect stays PURE · pure-\ncompute query of own workflow state · no I/O · no flow change · output\ntrust = TRUSTED always). Total PURE · 13 → "
            },
            {
              "k": "strong",
              "text": "10"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine codegen.rs mutation (ADR-085 no-drift gate)",
          "id": "engine-codegenrs-mutation-adr-085-no-drift-gate"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "enum_excludes_legacy_post_d_n6"
              },
              {
                "k": "text",
                "text": " · ADD all 4 to legacy exclusion list\n(8 → "
              },
              {
                "k": "strong",
                "text": "12"
              },
              {
                "k": "text",
                "text": " legacy names guarded)."
              }
            ],
            [
              {
                "k": "code",
                "text": "enum_includes_canonical_anchors"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "\"nika:cost\""
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "\"nika:inspect\""
              },
              {
                "k": "text",
                "text": "\n(the canonical-anchor sentinel for Introspection category)."
              }
            ],
            [
              {
                "k": "code",
                "text": "enum_has_25_entries"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "enum_has_22_entries"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Engine catalog test mutations",
          "id": "engine-catalog-test-mutations"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "builtin_count"
              },
              {
                "k": "text",
                "text": " · 25 → 22"
              }
            ],
            [
              {
                "k": "code",
                "text": "category_counts"
              },
              {
                "k": "text",
                "text": " · Introspection 4 → 1 · total 22"
              }
            ],
            [
              {
                "k": "code",
                "text": "find_known_builtins"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "\"cost\""
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "\"inspect\""
              },
              {
                "k": "text",
                "text": " sentinel"
              }
            ],
            [
              {
                "k": "code",
                "text": "is_known_builtin_works"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "\"inspect\""
              },
              {
                "k": "text",
                "text": " is the canonical · 4 legacy\nadded to negative-assertion list"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Spec mutation (companion · nika/spec submodule `8437909`)",
          "id": "spec-mutation-companion--nikaspec-submodule-8437909"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " · §0 + §1 Introspection count 25\n→ 22 · 4 → 1 · §3 Introspection section DELETE 4 subsections · ADD\ninspect subsection with "
              },
              {
                "k": "code",
                "text": "view:"
              },
              {
                "k": "text",
                "text": " enum + per-view return shapes + 1\nNIKA-BUILTIN-INSPECT-001 error code."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " · invoke.tool oneOf nika\nbranch enum · DELETE 4 entries · ADD 1 · 25 → 22 · description\nIntrospection 4 → 1."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/spec/06-stdlib-contract.md"
              },
              {
                "k": "text",
                "text": " · enumeration + total updated\n· 4-into-1 inspect collapse cited per ADR-088."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Symmetry signal COMPLETE · 5 layers"
              },
              {
                "k": "text",
                "text": " · « one super-powerful builtin\n· multi-mode args » canonical pattern now realized at 5 distinct\ndomains · HTTP (fetch+extract · 8 modes) · data transform (jq ·\nsubsumes ~13) · format conversion (convert · 4 formats × 3 directions)\n· temporal control (wait · 2 modes) · introspection (inspect · 4 views).\nThe Rams sweep is structurally complete · cookbook authors + LSP\nconsumers get fully consistent cognitive surface across all 5 domains."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Trust class preserved"
              },
              {
                "k": "text",
                "text": " · all 4 were PURE · inspect stays PURE ·\nzero security regression."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No-drift gate auto-validates"
              },
              {
                "k": "text",
                "text": " · ADR-085 codegen.rs test\n"
              },
              {
                "k": "code",
                "text": "enum_matches_catalog_one_to_one"
              },
              {
                "k": "text",
                "text": " AUTO-detects spec ↔ engine parity\nvia "
              },
              {
                "k": "code",
                "text": "nika_builtin_tool_enum_schema()"
              },
              {
                "k": "text",
                "text": " reading ALL_BUILTINS at call time."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Workflow.schema.json LSP-completion ENRICHED"
              },
              {
                "k": "text",
                "text": " · 4 → 1 builtin\nreduces autocomplete surface AND focuses the prompt on the "
              },
              {
                "k": "code",
                "text": "view:"
              },
              {
                "k": "text",
                "text": "\ndiscriminator (the actual operator decision point) · cleaner LSP UX\nthan 4 sibling completions with similar names."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Return-shape heterogeneity"
              },
              {
                "k": "text",
                "text": " · the 4 views have semantically\ndistinct return shapes (cost = numeric metrics · records = list ·\ndag_info = graph · threads = pool counts) · workflow.schema.json\nmodels this as untyped output (operator-side handles per-view) ·\ncould ratchet to "
              },
              {
                "k": "code",
                "text": "oneOf"
              },
              {
                "k": "text",
                "text": " per view in a future ADR if LSP UX feedback\nsignals need typed return."
              },
              {
                "k": "text",
                "text": "- Mitigation · per-view return shapes documented inline in spec §3\n  so authors know what to expect.\n- Mitigation · the canonical fetch+extract precedent has the same\n  return-shape heterogeneity across its 8 modes · workflow author\n  pattern is well-established."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Public API breaking"
              },
              {
                "k": "text",
                "text": " · existing workflows using "
              },
              {
                "k": "code",
                "text": "tool: nika:cost"
              },
              {
                "k": "text",
                "text": "\n(or records · dag_info · threads) fail against the spec-22 closed\nenum + "
              },
              {
                "k": "code",
                "text": "is_known_builtin"
              },
              {
                "k": "text",
                "text": " returns false for all 4. Acceptable per\nADR-002 (amended D-2026-06-20-N1 · real semver toward 1.0 · pre-1.0\nbreakage OK · NUKE-LEGACY · git is the archive · zero deprecation alias)."
              },
              {
                "k": "text",
                "text": "- Mitigation · spec is pre-v1.0 GA · breakage cost ≈ 0 today.\n- Mitigation · trust.rs `legacy_builtins_unknown_post_d_n6` extends\n  with all 4 · fail-closed regression guard."
              }
            ],
            [
              {
                "k": "strong",
                "text": "NIKA-BUILTIN-INSPECT-001 new error code"
              },
              {
                "k": "text",
                "text": " · documented in spec\nerror namespace · engine impl gates on L2 nika-builtin admission."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Tests · +4 net new legacy guards"
              },
              {
                "k": "text",
                "text": " in trust.rs + +4 in codegen.rs\n· existing conformance tests stay GREEN (renamed counts asserted)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "L0 layer constraint preserved"
              },
              {
                "k": "text",
                "text": " · pure data + pure test mutation ·\nzero L0 violation."
              }
            ],
            [
              {
                "k": "strong",
                "text": "TTL graph projection"
              },
              {
                "k": "text",
                "text": " · monorepo "
              },
              {
                "k": "code",
                "text": "nika-language-v1.ttl"
              },
              {
                "k": "text",
                "text": " updates ·\n4 sets of 4 refs (cost · records · dag_info · threads) → 4 refs\n(inspect · instance + family + raises + invokedBy) · count 25 → 22\nin family list. Per ADR-087 precedent · session B's nika-spec-graph-\nparity vector auto-syncs."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "A · Keep 4 atomic introspection builtins",
          "id": "a--keep-4-atomic-introspection-builtins"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · the symmetry argument · 4-of-5 Rams collapses landed (jq ·\nconvert · wait + this) · the 5th uncollapsed (introspection) breaks\nthe canonical pattern's structural completeness · cookbook authors\nface inconsistent surface."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "B · `view:` enum but keep 4 wrappers as syntactic sugar",
          "id": "b--view-enum-but-keep-4-wrappers-as-syntactic-sugar"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "REJECTED · violates « less but better » · syntactic sugar accumulates\nback to N builtin slots · the discriminator IS the canonical API · no\nceremony needed."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "C · Typed `oneOf` return shape per view",
          "id": "c--typed-oneof-return-shape-per-view"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "DEFERRED to future ADR if LSP UX signal arrives. Per "
            },
            {
              "k": "code",
              "text": "nika:fetch"
            },
            {
              "k": "text",
              "text": "\nprecedent (8 modes · untyped return per spec · still successful) ·\nuntyped is acceptable v0.1. Trigger gate · operator-side incident\nOR LSP UX feedback citing \"missed completion for inspect view X\nreturn field Y\"."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate verification",
          "id": "gate-verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per ADR-084/086/087 precedent ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "✅ Gate 2 TDD · trust.rs + codegen.rs tests updated · catalog tests\nupdated · 226 nika-catalog + 205 nika-schema lib tests GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 3 IMPL · "
              },
              {
                "k": "code",
                "text": "cargo check --workspace"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 4 CLIPPY · "
              },
              {
                "k": "code",
                "text": "cargo clippy --workspace --all-targets -- -D warnings"
              },
              {
                "k": "text",
                "text": " 0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 8 DOCS · "
              },
              {
                "k": "code",
                "text": "cargo doc --no-deps -p nika-catalog -p nika-schema"
              },
              {
                "k": "text",
                "text": "\n0 warnings."
              }
            ],
            [
              {
                "k": "text",
                "text": "✅ Gate 12 ATOMIC · single engine commit + single monorepo bump."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo fmt --check"
              },
              {
                "k": "text",
                "text": " GREEN."
              }
            ],
            [
              {
                "k": "code",
                "text": "cargo deny check"
              },
              {
                "k": "text",
                "text": " ok (no new deps)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Gates 1 / 5 / 6 / 7 / 9 / 10 / 11 · N/A for data refactor."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Rams sweep · COMPLETE state post-ADR-088",
          "id": "rams-sweep--complete-state-post-adr-088"
        },
        {
          "k": "code",
          "lang": "text",
          "text": "RAMS COLLAPSE ARC (2026-05-27)         BEFORE   AFTER   NET     ADR\n────────────────────────                ──────   ─────   ───     ───\ncsv_to_json → convert                   1        1       0       086\nsleep + wait_until → wait               2        1       -1      087\ncost+records+dag_info+threads → inspect 4        1       -3      088\n                                                          ────\nCumulative builtin count                26       22      -4"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "5 layers now canonically consolidated · HTTP (fetch+extract) · data\ntransform (jq) · format conversion (convert) · temporal control (wait) ·\nintrospection (inspect). Symmetry signal STRUCTURALLY COMPLETE."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Optional future Rams ratchets (LOCK-031 trigger-gated)",
          "id": "optional-future-rams-ratchets-lock-031-trigger-gated"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "ADR-089 · `nika:json_diff` jq-subsume feasibility"
              },
              {
                "k": "text",
                "text": " · jq CAN\nexpress diff but loses RFC-6902 Patch output format. Trigger ·\ncookbook adoption of jq-diff recipe + operator preference signal."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Crate-level Rams audit"
              },
              {
                "k": "text",
                "text": " · 4 candidate domains identified\ninternally (insta + expect-test consolidation · YAML triumvirate\naudit · jiff date pre-decision · hash family canonical). Trigger ·\nratchet candidates accumulate ≥3 cross-product signals."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When ADR-089 + selected crate Rams land · 22 → ~21 + workspace dep\nhygiene improvements. Same "
            },
            {
              "k": "code",
              "text": "nika: v1"
            },
            {
              "k": "text",
              "text": " envelope · real-semver-toward-1.0 discipline (ADR-002 · amended D-2026-06-20-N1)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Companion artefacts",
          "id": "companion-artefacts"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-catalog/src/data/builtins.rs"
              },
              {
                "k": "text",
                "text": " · ALL_BUILTINS · DELETE 4 ·\nINSERT 1 · 25 → 22 · test counts adjusted."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog/src/types/builtin.rs"
              },
              {
                "k": "text",
                "text": " · BuiltinCategory Introspection\ndoc-comment refreshed (1 builtin · view-discriminated)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-catalog/src/lib.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "all_builtins_non_empty"
              },
              {
                "k": "text",
                "text": " test 25 → 22."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/trust.rs"
              },
              {
                "k": "text",
                "text": " · TRUST_PURE_BUILTINS · DELETE 4 +\nINSERT 1 (13 → 10) · legacy exclusion test extended +4 · total\nassertion 25 → 22."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-schema/src/codegen.rs"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "enum_has_25_entries"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "_22_entries"
              },
              {
                "k": "text",
                "text": "\n· "
              },
              {
                "k": "code",
                "text": "enum_includes_canonical_anchors"
              },
              {
                "k": "text",
                "text": " cost → inspect ·\n"
              },
              {
                "k": "code",
                "text": "enum_excludes_legacy_post_d_n6"
              },
              {
                "k": "text",
                "text": " extended +4."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " (submodule "
              },
              {
                "k": "code",
                "text": "8437909"
              },
              {
                "k": "text",
                "text": ") · 4\nintrospection sections deleted · inspect section added · counts\nupdated."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/schemas/workflow.schema.json"
              },
              {
                "k": "text",
                "text": " (same submodule commit) ·\ninvoke.tool oneOf nika branch enum 25 → 22."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika/spec/spec/06-stdlib-contract.md"
              },
              {
                "k": "text",
                "text": " (same submodule commit) ·\nenumeration + total updated."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-087 · sleep + wait_until → wait (prerequisite · same Rams sweep)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-086 · csv_to_json → convert (prerequisite · same Rams sweep)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-085 · spec workflow.schema.json oneOf bridge (no-drift gate)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-084 · nika-catalog reconciliation to spec 26 (foundation)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-082 · envelope "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " single-version-marker"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · LOCK-031 trigger-gated discipline (no infra behind locked\ngate · ADR-089 + crate Rams audit deferred)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · cross-source-validation §2.6 Signal 7 (verify-the-auditor)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Internal · no-legacy-no-back-compat Class 2 (git is the archive)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋"
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-089",
      "file": "adr-089-json-diff-jq-subsume-rejected.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Disposition",
          "anchor": "disposition"
        },
        {
          "title": "Future revisit triggers (LOCK-031 strict)",
          "anchor": "future-revisit-triggers-lock-031-strict"
        },
        {
          "title": "Cross-source verification (per §2.6 Signal 3 + §2.7.1 cascade-completeness)",
          "anchor": "cross-source-verification-per-26-signal-3--271-cascade-completeness"
        },
        {
          "title": "Cohérent",
          "anchor": "cohrent"
        },
        {
          "title": "Update log",
          "anchor": "update-log"
        }
      ],
      "words": 1367,
      "sha256": "b561f6ba1709e92aacfc1ccdd584491cf842e49e61a3599c8f2367d906bd2b06",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-089 · `nika:json_diff` jq-subsume feasibility · REJECTED",
          "id": "adr-089--nikajson_diff-jq-subsume-feasibility--rejected"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Operator-led Rams audit 2026-05-27 of the canonical 22-builtin set\n(post-ADR-086/087/088 collapses) identified "
            },
            {
              "k": "code",
              "text": "nika:json_diff"
            },
            {
              "k": "text",
              "text": " as one of\nthe residual data builtins · question to answer · « can jq subsume\njson_diff like it subsumed map/filter/group_by/json_merge? »"
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This is the same Rams test that drove the 42 → 22 cascade · the\ncanonical razor « a nika:builtin is justified SSI NOT expressible in\njq+CEL+Schema · IS the logic behind the D-N6 collapse 42→26 + ADR-086\ncsv_to_json cut + ADR-088 4-introspection collapse »."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Internal · cross-source-validation §2.6 Signal 3 · single-source jq\nresearch insufficient · this ADR documents the primary-source\nverification (jq capability matrix) and the disposition."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "REJECTED · keep `nika:json_diff` as canonical builtin emitting RFC-6902\nJSON Patch."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Rationale · what jq CAN do",
          "id": "rationale--what-jq-can-do"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "jq is Turing-equivalent for JSON transformation. A recursive walk over\ntwo documents to find differences IS expressible · the rough shape ·"
            }
          ]
        },
        {
          "k": "code",
          "lang": "jq",
          "text": "# Compare two documents at $base and $patch · recursive · produces\n# {path, base, patch} tuples for each difference (one possible shape)\ndef diff($base; $patch; path):\n  if ($base | type) == \"object\" and ($patch | type) == \"object\" then\n    (($base | keys) + ($patch | keys) | unique)[] as $k |\n    diff($base[$k]; $patch[$k]; path + [$k])\n  elif $base != $patch then\n    { path: path, before: $base, after: $patch }\n  else empty end ;\n\ndiff(.[0]; .[1]; [])"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Capability-wise · jq CAN walk + compare + emit difference tuples. The\ntest « is NOT expressible in jq+CEL+Schema » FAILS at the capability\nlayer for ARBITRARY difference reporting."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Rationale · what jq CANNOT do (the actual cut)",
          "id": "rationale--what-jq-cannot-do-the-actual-cut"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The justification for "
            },
            {
              "k": "code",
              "text": "nika:json_diff"
            },
            {
              "k": "text",
              "text": " is NOT « jq can't diff » · it's\n"
            },
            {
              "k": "strong",
              "text": "« jq cannot emit RFC-6902 JSON Patch (the canonical interop format) »"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "RFC 6902 is the canonical IETF standard for JSON document diffs ·\nshape ·"
            }
          ]
        },
        {
          "k": "code",
          "lang": "json",
          "text": "[\n  { \"op\": \"replace\", \"path\": \"/foo/bar\", \"value\": 42 },\n  { \"op\": \"add\",     \"path\": \"/baz\",     \"value\": \"new\" },\n  { \"op\": \"remove\",  \"path\": \"/qux\" }\n]"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The 6 operations ("
            },
            {
              "k": "code",
              "text": "add"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "remove"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "replace"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "move"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "copy"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "test"
            },
            {
              "k": "text",
              "text": ")\nhave semantic rules · particularly ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "replace"
              },
              {
                "k": "text",
                "text": " vs "
              },
              {
                "k": "code",
                "text": "add"
              },
              {
                "k": "text",
                "text": " discrimination · based on key existence in the\nbase · jq doesn't track « did this key exist in the original document »\nin a way that survives the recursive walk"
              }
            ],
            [
              {
                "k": "code",
                "text": "remove"
              },
              {
                "k": "text",
                "text": " requires the absence in patch (jq can express absence-of-key\nvia "
              },
              {
                "k": "code",
                "text": "has"
              },
              {
                "k": "text",
                "text": " but threading it through nested recursion is verbose · ~30+\nlines of jq for a correctness-equivalent emitter)"
              }
            ],
            [
              {
                "k": "code",
                "text": "move"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "copy"
              },
              {
                "k": "text",
                "text": " require structural pattern detection (was this\nadd+remove pair semantically a move?) · jq has no notion of « detect\nthis pair as one logical operation »"
              }
            ],
            [
              {
                "k": "code",
                "text": "test"
              },
              {
                "k": "text",
                "text": " is a runtime assertion semantic · belongs to the consumer"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A 30+ line jq diff emitter that gets RFC 6902 SEMANTICS right is ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Not « less but better »"
              },
              {
                "k": "text",
                "text": " · longer than the 22-builtin emit shape it\nwould replace · violates Rams 10 spirit"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Cookbook anti-pattern"
              },
              {
                "k": "text",
                "text": " · Internal · the « one obvious way »\nvalidator canon (D-N9/N10) requires authors to NOT memorize 30-line\njq programs · prefer the canonical builtin"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Interop loss"
              },
              {
                "k": "text",
                "text": " · the RFC 6902 output format is the contract\ndownstream tools (JSON Patch apply libraries · OpenAPI diff tooling ·\nGraphQL change-set encoders) expect. Emitting a custom\n"
              },
              {
                "k": "code",
                "text": "{path, before, after}"
              },
              {
                "k": "text",
                "text": " shape via jq breaks interop · forces every\nconsumer to write a translator"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "The other 4 introspection razor sister tests",
          "id": "the-other-4-introspection-razor-sister-tests"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Same razor applied to ADR-086/087/088 · those COLLAPSED because the\ncollapsed forms WERE strictly better (Rams 10) ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-086 "
              },
              {
                "k": "code",
                "text": "csv_to_json → convert"
              },
              {
                "k": "text",
                "text": " · was 1 narrow direction · convert\nexposes 4 formats × 3 directions = 12 capabilities · strictly more\npower · same surface"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-087 "
              },
              {
                "k": "code",
                "text": "sleep + wait_until → wait"
              },
              {
                "k": "text",
                "text": " · the two former builtins were\nartificially split by mode (relative vs absolute) · unified mode arg\nIS the « one obvious way »"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-088 "
              },
              {
                "k": "code",
                "text": "4 introspection → inspect view:"
              },
              {
                "k": "text",
                "text": " · the 4 were artificially\nsplit by query-shape · unified view-discriminator IS the « one\nobvious way »"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The "
            },
            {
              "k": "code",
              "text": "json_diff"
            },
            {
              "k": "text",
              "text": " case is different · the alternative (« express via\njq ») would be "
            },
            {
              "k": "strong",
              "text": "LESS obvious"
            },
            {
              "k": "text",
              "text": " (30+ lines vs 1 builtin call) AND\n"
            },
            {
              "k": "strong",
              "text": "LESS interoperable"
            },
            {
              "k": "text",
              "text": " (custom diff shape vs RFC 6902 standard). Rams\ntest FAILS in both directions."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Disposition",
          "id": "disposition"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "KEEP `nika:json_diff` as canonical builtin"
            },
            {
              "k": "text",
              "text": " ·"
            }
          ]
        },
        {
          "k": "code",
          "lang": "yaml",
          "text": "diff:\n  invoke:\n    tool: \"nika:json_diff\"\n    args:\n      base: ${{ vars.original }}\n      patch: ${{ vars.modified }}\n  # output · RFC 6902 JSON Patch array"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Output shape · canonical RFC 6902 "
            },
            {
              "k": "code",
              "text": "Patch"
            },
            {
              "k": "text",
              "text": " array per IETF spec ·\ndownstream-interop-clean."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Canonical 22-builtin count UNCHANGED post this ADR · the 5-layer Rams\nsymmetry (fetch+extract · jq · convert · wait · inspect) holds ·\n"
            },
            {
              "k": "code",
              "text": "json_diff"
            },
            {
              "k": "text",
              "text": " stands as the « jq can't diff to a canonical format »\nexception · same shape as "
            },
            {
              "k": "code",
              "text": "json_merge_patch"
            },
            {
              "k": "text",
              "text": " (RFC 7396 null-delete · jq\ncan't) · same shape as "
            },
            {
              "k": "code",
              "text": "validate"
            },
            {
              "k": "text",
              "text": " (JSON Schema · jq is not a\nvalidator)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Future revisit triggers (LOCK-031 strict)",
          "id": "future-revisit-triggers-lock-031-strict"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Internal · round-1-ship-pattern §6 LOCK-031 spirit · documented triggers\nthat COULD reopen this disposition ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Trigger · What it would mean · Likelihood"
              }
            ],
            [
              {
                "k": "text",
                "text": "Cookbook adopts a `jq '... diff ...'` recipe with ≥3 successful invocations · Authors prefer jq-form over builtin · razor shifts · LOW · 30+ line jq programs are off-doctrine"
              }
            ],
            [
              {
                "k": "text",
                "text": "RFC 6902 deprecated by IETF · Output-format interop argument weakens · NEAR-ZERO · RFC is stable since 2013 · widely deployed"
              }
            ],
            [
              {
                "k": "text",
                "text": "jaq ships an `--rfc6902` flag or canonical `diff` filter · jq becomes the canonical RFC 6902 emitter · LOW · would require upstream jaq change"
              }
            ],
            [
              {
                "k": "text",
                "text": "Operator-explicit `« cut json_diff »` signal · The ultimate trigger · same shape as ADR-086 · OPEN"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per Internal · cross-source-validation §2.6 Signal 3 · this ADR is the\ncanonical anti-promotion gate · any future « should we cut json_diff? »\ndiscussion routes here."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Cross-source verification (per §2.6 Signal 3 + §2.7.1 cascade-completeness)",
          "id": "cross-source-verification-per-26-signal-3--271-cascade-completeness"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Primary-source verified 2026-05-27 ·"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "jaq source"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "jaq/jaq-json/src/lib.rs"
              },
              {
                "k": "text",
                "text": " · NO native "
              },
              {
                "k": "code",
                "text": "diff"
              },
              {
                "k": "text",
                "text": " or\n"
              },
              {
                "k": "code",
                "text": "rfc6902"
              },
              {
                "k": "text",
                "text": " filter. The capability is expressible (Turing-equivalent)\nBUT no canonical output-format emitter shipped."
              }
            ],
            [
              {
                "k": "strong",
                "text": "jq manual"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "jq-1.7.1"
              },
              {
                "k": "text",
                "text": " manual at "
              },
              {
                "k": "link",
                "text": "https://jqlang.org/manual/",
                "href": "https://jqlang.org/manual/"
              },
              {
                "k": "text",
                "text": " ·\nno "
              },
              {
                "k": "code",
                "text": "diff"
              },
              {
                "k": "text",
                "text": " builtin · no RFC 6902 support in stdlib."
              }
            ],
            [
              {
                "k": "strong",
                "text": "RFC 6902"
              },
              {
                "k": "text",
                "text": " · IETF spec stable since 2013 · 6 ops canonical ·\nwidely supported by JSON Patch libraries (e.g. "
              },
              {
                "k": "code",
                "text": "serde_json_patch"
              },
              {
                "k": "text",
                "text": "\nin Rust ecosystem)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Cookbook signal"
              },
              {
                "k": "text",
                "text": " · NIKA_COOKBOOK-v0.1.md · zero recipes that\nexpress diff-via-jq · the « one obvious way » authoring contract\nwould reject 30+ line jq programs."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Bucket A (already canon) · "
            },
            {
              "k": "code",
              "text": "nika:json_diff"
            },
            {
              "k": "text",
              "text": " was a D-N5 KEEP · this ADR\nformalizes the KEEP rationale."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Cohérent",
          "id": "cohrent"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-082 "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " envelope · 22 builtins canonical · ADR-089 holds count"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-084 catalog-spec reconciliation · "
              },
              {
                "k": "code",
                "text": "json_diff"
              },
              {
                "k": "text",
                "text": " stays in catalog"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-085 schemars codegen · "
              },
              {
                "k": "code",
                "text": "json_diff"
              },
              {
                "k": "text",
                "text": " enum variant stays"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-086/087/088 · 5-layer Rams symmetry · "
              },
              {
                "k": "code",
                "text": "json_diff"
              },
              {
                "k": "text",
                "text": " is the canonical\nexception that proves the « jq can't do everything » rule"
              }
            ],
            [
              {
                "k": "text",
                "text": "Per Internal · cross-source-validation §2.6 Signal 3 verification"
              }
            ],
            [
              {
                "k": "text",
                "text": "Per Internal · cognitive-design-canon P4 cognitive load · 1-line\nbuiltin call beats 30-line jq program"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Update log",
          "id": "update-log"
        },
        {
          "k": "code",
          "lang": "text",
          "text": "2026-05-27  v1.0 — Initial ADR · disposition REJECTED (keep nika:json_diff)\n              · Trigger · operator Rams audit 2026-05-27 evening · post\n                ADR-086/087/088 5-layer symmetry close · « can jq subsume\n                json_diff like jq subsumed json_merge? »\n              · Verification · primary-source jaq + jq + RFC 6902 IETF\n                · capability YES (Turing-equivalent walk) · output-format\n                interop NO (no canonical RFC 6902 emitter)\n              · Decision · keep `nika:json_diff` · 30+ line jq programs\n                violate Rams 10 less-but-better · violate cookbook one-\n                obvious-way contract · break RFC 6902 interop\n              · Canonical 22-builtin count UNCHANGED · 5-layer Rams\n                symmetry (fetch+extract · jq · convert · wait · inspect)\n                holds · json_diff stands as the « jq can't diff to a\n                canonical format » exception\n              · Future revisit triggers documented per LOCK-031 spirit ·\n                cookbook signal · RFC deprecation · jaq native diff filter ·\n                operator explicit cut\n              · Cohérent ADR-082 envelope + ADR-084/085/086/087/088 +\n                Internal · cross-source-validation §2.6 + cognitive-design\n                P4 + one-obvious-way validator canon D-N9/N10\n              · 5 raisons coherence ✅✅✅✅✅"
        }
      ]
    },
    {
      "id": "ADR-090",
      "file": "adr-090-structural-doctrine-enforcement.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence",
          "anchor": "evidence"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 784,
      "sha256": "7273a1a99a33bf0c7e2f1078291ee46915d62a1686287ea9c8173b28e057be10",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-090: Structural doctrine enforcement — gates project the SSOT",
          "id": "adr-090-structural-doctrine-enforcement--gates-project-the-ssot"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-003's thesis is "
            },
            {
              "k": "strong",
              "text": "\"enforcement must be mechanical, not social\""
            },
            {
              "k": "text",
              "text": ". Yet\nseveral load-bearing invariants were still honor-system — recorded in prose\n(an ADR, an audit table, an ownership matrix) but with no gate that fails when\nthe prose is violated. A self-audit (2026-06-10) found four such gaps:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Gate 5 (mutation ≥90%)"
              },
              {
                "k": "text",
                "text": " — checked only that the crate-spec "
              },
              {
                "k": "em",
                "text": "mentioned"
              },
              {
                "k": "text",
                "text": "\n\"Mutation\". No "
              },
              {
                "k": "code",
                "text": "cargo-mutants"
              },
              {
                "k": "text",
                "text": " was actually run/floored at admission."
              }
            ],
            [
              {
                "k": "strong",
                "text": "ADR-081 computer-use guards"
              },
              {
                "k": "text",
                "text": " — \"MANDATORY-at-admission\" for the L1\ncrates that touch screen capture / accessibility secure-fields, but nothing\nfailed if a guard-owner crate shipped without its guard."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Supply-chain policy"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "deny.toml"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "cargo-deny"
              },
              {
                "k": "text",
                "text": " were configured but\nunwired; only "
              },
              {
                "k": "code",
                "text": "cargo audit"
              },
              {
                "k": "text",
                "text": " (advisories) ran, not bans/licenses/sources."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Error one-voice"
              },
              {
                "k": "text",
                "text": " — every error enum was migrated to the canonical\n"
              },
              {
                "k": "code",
                "text": "NikaErrorCode"
              },
              {
                "k": "text",
                "text": " trait (recorded in a completeness audit table), but a new\nerror enum could skip the trait silently."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The common shape: a "
            },
            {
              "k": "strong",
              "text": "doctrine with a written SSOT"
            },
            {
              "k": "text",
              "text": " (audit table · ownership\nmatrix · "
            },
            {
              "k": "code",
              "text": "deny.toml"
            },
            {
              "k": "text",
              "text": " policy · the 12-gate list) but "
            },
            {
              "k": "strong",
              "text": "no projection of that\nSSOT into a pass/fail gate"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Every doctrine invariant that can be mechanically checked gets a structural\ngate, and the gate PROJECTS an existing SSOT rather than hardcoding a parallel\nlist."
            },
            {
              "k": "text",
              "text": " This makes the doctrine evolutive (edit the SSOT, the gate follows) and\nantifragile (a regression that violates the doctrine turns a gate RED)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Four gates landed under this principle (2026-06-10):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Gate · SSOT it projects · Invariant"
              }
            ],
            [
              {
                "k": "text",
                "text": "`scripts/ci/check-mutation-floor.sh` · crate-spec Gate-5 row · real Gate 5 — `cargo-mutants` kill-ratio ≥ floor; BUDGET mode reads a `<!-- GATE5-EXEMPT: N -->` spec marker (survivors ≤ N)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`scripts/hygiene/check-adr-081-guards.sh` (vector 35) · the ADR-081 ownership matrix · every MANDATORY guard whose owner-crate is a workspace member has an impl-binding (`scripts/ci/adr-081-guard-manifest.tsv`) + impl/test markers"
              }
            ],
            [
              {
                "k": "text",
                "text": "`scripts/hygiene/check-cargo-deny.sh` (vector 34) · `deny.toml` · full `cargo deny check` (advisories + bans + licenses + sources)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`scripts/hygiene/check-error-one-voice.sh` (vector 37) · `error-trait-completeness-2026-06-10.md` → `error-one-voice-allowlist.tsv` · every thiserror enum in admitted-crate src impls `NikaErrorCode` or is a documented exemption"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Each gate carries an "
            },
            {
              "k": "strong",
              "text": "orphan/parity check"
            },
            {
              "k": "text",
              "text": " so its projected SSOT can't rot\n(a manifest/allowlist row must map to a real matrix row / real enum). Vector 36\n("
            },
            {
              "k": "code",
              "text": "check-unused-deps.sh"
            },
            {
              "k": "text",
              "text": ", cargo-machete) shipped in the same arc."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The four doctrines are now structurally enforced — a future under-pressure\ndev can't let them rot silently (ADR-003's thesis, completed)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Evolutive: adding a guard / error enum / banned dep updates one SSOT; the\ngate auto-extends. Zero parallel hardcoded lists."
              }
            ],
            [
              {
                "k": "text",
                "text": "The ADR-081 guard gate is a "
              },
              {
                "k": "strong",
                "text": "forcing-function for the next crate"
              },
              {
                "k": "text",
                "text": ": when\n"
              },
              {
                "k": "code",
                "text": "nika-input"
              },
              {
                "k": "text",
                "text": " (M2.4) is admitted, its Guards 1+2 become owed-and-present and\nthe gate goes RED until they ship + test."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "check-mutation-floor.sh"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "cargo deny check"
              },
              {
                "k": "text",
                "text": " are minutes-slow →\nadmission/CI-tier, not pre-commit. The 34-vector "
              },
              {
                "k": "code",
                "text": "check-all.sh"
              },
              {
                "k": "text",
                "text": " stays fast\n(the slow gates are invoked per-crate at admission)."
              }
            ],
            [
              {
                "k": "text",
                "text": "BUDGET mode is not reproducible for cross-platform L1 crates: a naive\n"
              },
              {
                "k": "code",
                "text": "cargo mutants -- --lib"
              },
              {
                "k": "text",
                "text": " mutates the cfg'd-out other-OS arms too, so the\nspec's reachable exempt-count (e.g. nika-a11y's 7) does not match the raw\nmacOS survivor count (~31). Calibration needs a per-crate "
              },
              {
                "k": "code",
                "text": "mutants.toml"
              },
              {
                "k": "text",
                "text": "\nexclude_re — a deferred-with-trigger follow-up. Until then those crates carry\nno exempt marker and are not FLOOR-gated."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The self-audit that found these gaps (and two bugs in the mutation gate as it\nwas first shipped) is itself the antifragile pattern: gates are audited like\nany other code, and a gate that would false-RED a legit admission is a bug\ncaught before it blocks."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence",
          "id": "evidence"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "scripts/hygiene/check-all.sh"
              },
              {
                "k": "text",
                "text": " — 34 live vectors (34-37 added this arc)"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/hygiene/README.md"
              },
              {
                "k": "text",
                "text": " — vector catalog 34-38"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/adr/adr-003-12-gate-admission.md"
              },
              {
                "k": "text",
                "text": " — amendment recording the Gate-5 +\nADR-081 closure"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/architecture/error-trait-completeness-2026-06-10.md"
              },
              {
                "k": "text",
                "text": " — the error\none-voice SSOT"
              }
            ],
            [
              {
                "k": "code",
                "text": "docs/adr/adr-081-l1-effect-crate-guard-contract.md"
              },
              {
                "k": "text",
                "text": " — the guard ownership\nmatrix"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/adr-081-guard-manifest.tsv"
              },
              {
                "k": "text",
                "text": " — the projected guard bindings"
              }
            ],
            [
              {
                "k": "code",
                "text": "scripts/ci/error-one-voice-allowlist.tsv"
              },
              {
                "k": "text",
                "text": " — the projected error-voice allowlist"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Leave it social (memory + reminders)"
              },
              {
                "k": "text",
                "text": " — rejected: ADR-003 already proved\nthe legacy codebase rotted under social enforcement (29 broken MCP aliases\nlived for months)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Hardcode the lists in each gate"
              },
              {
                "k": "text",
                "text": " — rejected: a parallel list drifts from\nthe SSOT. Projection (gate reads the matrix / table / policy file) keeps one\nsource."
              }
            ],
            [
              {
                "k": "strong",
                "text": "One mega-gate"
              },
              {
                "k": "text",
                "text": " — rejected: four orthogonal drift classes, four scripts,\nsingle responsibility (mirrors the existing vector design)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This ADR records a "
            },
            {
              "k": "em",
              "text": "principle"
            },
            {
              "k": "text",
              "text": ", not a frozen list. As new mechanically-checkable\ndoctrines appear (e.g. "
            },
            {
              "k": "code",
              "text": "#[must_use]"
            },
            {
              "k": "text",
              "text": " discipline, public-API surface lock via\n"
            },
            {
              "k": "code",
              "text": "cargo-public-api"
            },
            {
              "k": "text",
              "text": ", semver-checks), they fall under the same rule: find the\nSSOT, project it into a gate, add an orphan check. Gate count is not sacred —\nthe projection-from-SSOT discipline is."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-091",
      "file": "adr-091-nika-infer-local-candle-sidecar.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "SOTA checklist (must-not-forget · from the 2026-06-11 Socratic pass)",
          "anchor": "sota-checklist-must-not-forget--from-the-2026-06-11-socratic-pass"
        },
        {
          "title": "Sub-decisions — RULED 2026-07-11 (operator-delegated · the recommended defaults lock)",
          "anchor": "sub-decisions--ruled-2026-07-11-operator-delegated--the-recommended-defaults-lock"
        },
        {
          "title": "Alternatives rejected",
          "anchor": "alternatives-rejected"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        }
      ],
      "words": 970,
      "sha256": "111aced0a6edd7990ab4f4924b8823da5537e4214649b339ea4858b20635b5df",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-091 · Sovereign local inference — `nika-infer-local` (pure-Rust candle sidecar)",
          "id": "adr-091--sovereign-local-inference--nika-infer-local-pure-rust-candle-sidecar"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika's v0.1 provider canon ships "
            },
            {
              "k": "strong",
              "text": "14 providers"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "spec/canon.yaml"
            },
            {
              "k": "text",
              "text": "): 8 cloud + 5\nlocal + 1 mock. All 5 local providers ("
            },
            {
              "k": "code",
              "text": "ollama"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "lmstudio"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "llamacpp"
            },
            {
              "k": "text",
              "text": " ·\n"
            },
            {
              "k": "code",
              "text": "localai"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "vllm"
            },
            {
              "k": "text",
              "text": ") are "
            },
            {
              "k": "strong",
              "text": "external OpenAI-compatible HTTP servers"
            },
            {
              "k": "text",
              "text": " — the engine\ntalks to them over localhost. The in-process GGUF runtime "
            },
            {
              "k": "code",
              "text": "native"
            },
            {
              "k": "text",
              "text": " was\n"
            },
            {
              "k": "strong",
              "text": "DEFERRED"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "spec/stdlib/providers-v0.1.md"
            },
            {
              "k": "text",
              "text": ": \""
            },
            {
              "k": "em",
              "text": "mistral.rs crashed the host"
            },
            {
              "k": "text",
              "text": "\")\npending \"a candle/llama.cpp binding stabilizes + 30-day crash-free cohort +\ncross-platform conformance.\""
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "This leaves a "
            },
            {
              "k": "strong",
              "text": "sovereignty gap"
            },
            {
              "k": "text",
              "text": ": the local-first default ("
            },
            {
              "k": "code",
              "text": "ollama/<model>"
            },
            {
              "k": "text",
              "text": ")\ndelegates inference to an external "
            },
            {
              "k": "strong",
              "text": "Go"
            },
            {
              "k": "text",
              "text": " daemon. SuperNovae preaches local-first\nsovereign (alignment Rule 3), yet the inference runtime is the one non-Rust,\nnon-ours link in the chain. We want to close it — to "
            },
            {
              "k": "strong",
              "text": "own"
            },
            {
              "k": "text",
              "text": " the local inference\npath, in Rust."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Build "
            },
            {
              "k": "strong",
              "text": "`nika-infer-local`"
            },
            {
              "k": "text",
              "text": ": a pure-Rust, "
            },
            {
              "k": "strong",
              "text": "candle"
            },
            {
              "k": "text",
              "text": "-backed local inference\nserver we ship and spawn, that the engine talks to over the "
            },
            {
              "k": "strong",
              "text": "existing\n`OpenAiCompat` wire"
            },
            {
              "k": "text",
              "text": " (zero new dispatch seam). It replaces the dependency on\nollama for the sovereign-default path."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Locked choices:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "candle only"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "candle-core"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "candle-transformers"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "tokenizers"
              },
              {
                "k": "text",
                "text": "). The\nmodel zoo (Llama3 · Qwen3 · Mistral · Phi · Gemma) + quantized GGUF loaders\nship in "
              },
              {
                "k": "code",
                "text": "candle-transformers"
              },
              {
                "k": "text",
                "text": ". Pure Rust, no C compiler, no FFI."
              }
            ],
            [
              {
                "k": "strong",
                "text": "NO rig."
              },
              {
                "k": "text",
                "text": " rig is an app/agent framework, not a runtime, and Diamond already\nrejected it ("
              },
              {
                "k": "code",
                "text": "nika-providers"
              },
              {
                "k": "text",
                "text": " talks wire directly — "
              },
              {
                "k": "code",
                "text": "providers-v0.1.md"
              },
              {
                "k": "text",
                "text": ").\nRe-introducing it reverses a locked decision and duplicates\n"
              },
              {
                "k": "code",
                "text": "nika-verb-agent"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "nika-providers"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "NO mistral.rs"
              },
              {
                "k": "text",
                "text": " (by default). It is candle + a far larger surface (vision ·\naudio · image-gen · LoRA · AnyMoE · MCP-client · its own "
              },
              {
                "k": "code",
                "text": "unsafe"
              },
              {
                "k": "text",
                "text": " CUDA) than a\nchat-completion sidecar needs, and moves fast (0.8.x, thin stable surface).\nWe own a bounded generation loop instead (CRAFT). Revisit only if the\nself-authored loop proves insufficient."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Sidecar, not in-process"
              },
              {
                "k": "text",
                "text": " (Option B). The runtime runs in a "
              },
              {
                "k": "strong",
                "text": "supervised\nchild process"
              },
              {
                "k": "text",
                "text": " the engine spawns and owns (not an external daemon). A crash\n(panic · OOM-kill · GPU abort) kills only the child; the lean core detects\nexit/broken-pipe and restarts/degrades. This is the only model that contains\nmemory-corruption crashes ("
              },
              {
                "k": "code",
                "text": "catch_unwind"
              },
              {
                "k": "text",
                "text": " cannot) — directly answering the\ndefer's \"must not crash the host\" bar. Reuses "
              },
              {
                "k": "code",
                "text": "OpenAiCompat"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "strong",
                "text": "zero new\ndispatch code"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Feature-gated, off by default."
              },
              {
                "k": "text",
                "text": " candle never enters the lean core build\nunless the "
              },
              {
                "k": "code",
                "text": "local-infer"
              },
              {
                "k": "text",
                "text": " feature is enabled (feature-defaults discipline). A\ndefault "
              },
              {
                "k": "code",
                "text": "nika"
              },
              {
                "k": "text",
                "text": " binary ships zero inference deps."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Metal + CPU first, CUDA flagged."
              },
              {
                "k": "text",
                "text": " candle abstracts the device\n("
              },
              {
                "k": "code",
                "text": "Device::Cpu | Metal | Cuda"
              },
              {
                "k": "text",
                "text": "). v1 targets CPU + Metal — testable on the dev\nmachine (Apple Silicon · "
              },
              {
                "k": "code",
                "text": "cargo test"
              },
              {
                "k": "text",
                "text": " real · 12 gates verifiable locally),\nand the safest crash profile (the researched crash classes — mistral.rs\nCUDA-graph UAF, candle u32÷f64 silent-zeroing, stream-consistency — are all\nCUDA-specific). "
              },
              {
                "k": "code",
                "text": "cuda"
              },
              {
                "k": "text",
                "text": " is a compile feature activated when NVIDIA hardware is\nin the test loop; the device-agnostic serving loop means CUDA is a flag + a\nhardware test pass, "
              },
              {
                "k": "strong",
                "text": "not a rewrite"
              },
              {
                "k": "text",
                "text": ". Datacenter-scale CUDA throughput stays\ndelegated to the "
              },
              {
                "k": "code",
                "text": "vllm/"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "llamacpp/"
              },
              {
                "k": "text",
                "text": " providers."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "SOTA checklist (must-not-forget · from the 2026-06-11 Socratic pass)",
          "id": "sota-checklist-must-not-forget--from-the-2026-06-11-socratic-pass"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Correctness killers (v1): KV-cache wired (O(n²) without it) · per-family chat\ntemplate (wrong template = garbage) · SSE token streaming · EOS + stop-sequence\nhalt · full sampling + "
            },
            {
              "k": "strong",
              "text": "seed"
            },
            {
              "k": "text",
              "text": " (determinism for tests) · OpenAI-compat shape\nfidelity verified by feeding our output through Nika's "
            },
            {
              "k": "em",
              "text": "own"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "OpenAiCompat"
            },
            {
              "k": "text",
              "text": "\nparser (in-process self-consistency test). Scope/safety: subprocess\nlifecycle + cancellation (CANCEL SAFETY) · sequential request queue v1\n(documented limit · batching deferred) · GGUF quantization · lazy warm model\nload · typed errors (model-not-found · OOM · context-overflow · gen-timeout).\nDefault model: "
            },
            {
              "k": "strong",
              "text": "Qwen3"
            },
            {
              "k": "text",
              "text": " (BFCL tool-calling + NVIDIA SLM-agents thesis)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Sub-decisions — RULED 2026-07-11 (operator-delegated · the recommended defaults lock)",
          "id": "sub-decisions--ruled-2026-07-11-operator-delegated--the-recommended-defaults-lock"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Accepted with the ADR: the crate was admitted conformant to this design, so\na "
            },
            {
              "k": "code",
              "text": "proposed"
            },
            {
              "k": "text",
              "text": " status no longer described reality (same-day precedent: the\nADR-099/100 status realignment)."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Structured output v1 = retry-loop"
              },
              {
                "k": "text",
                "text": " (generate → validate vs schema →\nre-prompt with the error; the exact pattern the "
              },
              {
                "k": "code",
                "text": "eval/"
              },
              {
                "k": "text",
                "text": " harness already\nproves) · logit-masking / GBNF (guaranteed-valid first token, the real\nSOTA) stays v2."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Embeddings in the same sidecar = YES, at v1.1"
              },
              {
                "k": "text",
                "text": " (candle serves bge/e5 —\nthe Connectome/memory stack becomes 100% sovereign Rust). Sequenced\nstrictly after chat-completion proves stable."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Crate boundary"
              },
              {
                "k": "text",
                "text": ": "
              },
              {
                "k": "code",
                "text": "nika-infer-local"
              },
              {
                "k": "text",
                "text": " = text (+ embeddings at v1.1);\n"
              },
              {
                "k": "code",
                "text": "nika-vision-local"
              },
              {
                "k": "text",
                "text": " (ADR-081 · NIKA-1500..1599) stays a separate crate —\nheavy vision deps never enter the text sidecar."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "What acceptance does NOT ship"
            },
            {
              "k": "text",
              "text": ": the launch surface (the L3 supervisor\nthat spawns/monitors the sidecar + the CLI lane) is this ADR's "
            },
            {
              "k": "code",
              "text": "enables"
            },
            {
              "k": "text",
              "text": ",\ntracked by #146 ("
            },
            {
              "k": "code",
              "text": "nika model pull"
            },
            {
              "k": "text",
              "text": " sequences after it) — per ADR-093's\nfollow-up, the supervisor lives at the runtime layer."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives rejected",
          "id": "alternatives-rejected"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Option A · in-process `native`"
              },
              {
                "k": "text",
                "text": " — purest single binary, lowest latency, but\nre-imports the crash-takes-down-engine risk that caused the original defer.\nKept as a possible v2 behind the same feature flag once candle proves a\n30-day crash-free cohort (the canon's re-admission gate)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "llama-cpp-rs"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "llama-cpp-2"
              },
              {
                "k": "text",
                "text": ") — broadest model/quant/backend coverage and\nmost battle-tested, but its README states \""
              },
              {
                "k": "em",
                "text": "not safe … do not use where UB is\nnot acceptable"
              },
              {
                "k": "text",
                "text": "\", needs clang+bindgen (breaks pure-Rust single-binary), and\nships live multi-backend crash classes. Contradicts the all-Rust + must-not-\ncrash thesis in-process; only viable behind the same subprocess isolation,\nand then candle is the cleaner Rust-native default."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Burn-LM"
              },
              {
                "k": "text",
                "text": " — promising pure-Rust multi-backend runtime, but "
              },
              {
                "k": "strong",
                "text": "alpha"
              },
              {
                "k": "text",
                "text": " (Aug\n2025). Re-evaluate ~2 release cycles out."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Kills the ollama (Go daemon) dependency for the sovereign-default path —\ncloses the alignment Rule 3 gap."
              }
            ],
            [
              {
                "k": "text",
                "text": "The lean core stays lean (feature off by default · candle isolated)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The engine gains an all-Rust, crash-isolated, air-gapped local inference path\nreusing the existing wire — no new dispatch seam, no new provider model."
              }
            ],
            [
              {
                "k": "text",
                "text": "Re-admission of "
              },
              {
                "k": "code",
                "text": "native"
              },
              {
                "k": "text",
                "text": " (Option A, in-process) becomes a future flag flip,\nnot a redesign."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋 Nika — workflow engine for AI, AGPL, SuperNovae Studio."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-093",
      "file": "adr-093-infer-local-http-server-tiny-http.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Options considered",
          "anchor": "options-considered"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        }
      ],
      "words": 697,
      "sha256": "91f4b86c66df40ed85d5ca9977ef46acf1bda537ebaccc7d655a52b2b4288277",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-093: nika-infer-local HTTP server — tiny_http for the v1 sidecar endpoint",
          "id": "adr-093-nika-infer-local-http-server--tiny_http-for-the-v1-sidecar-endpoint"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "ADR-091 decided the sovereign local-inference architecture: a candle backend\nbehind the "
            },
            {
              "k": "code",
              "text": "local-infer"
            },
            {
              "k": "text",
              "text": " feature, "
            },
            {
              "k": "strong",
              "text": "reached over the existing `OpenAiCompat`\nwire"
            },
            {
              "k": "text",
              "text": " (no new dispatch seam). The crate spec ("
            },
            {
              "k": "code",
              "text": "docs/crate-specs/ nika-infer-local.md"
            },
            {
              "k": "text",
              "text": " §5bis) verified the remaining gap: the backend is proven\n(real-GGUF e2e green) but "
            },
            {
              "k": "strong",
              "text": "nothing serves it"
            },
            {
              "k": "text",
              "text": " — the engine reaches models\nonly through "
            },
            {
              "k": "code",
              "text": "nika-providers"
            },
            {
              "k": "text",
              "text": " HTTP profiles. The one open decision was the\nserver's HTTP framework, deliberately deferred to its own ADR because a\ndependency choice on the inference path is a 20-year commitment (Diamond\nforward-compat doctrine)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The v1 server surface is intentionally tiny:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "POST /v1/chat/completions"
              },
              {
                "k": "text",
                "text": " — deserialize "
              },
              {
                "k": "code",
                "text": "protocol::ChatRequest"
              },
              {
                "k": "text",
                "text": ", call\n"
              },
              {
                "k": "code",
                "text": "BackendDyn::generate"
              },
              {
                "k": "text",
                "text": ", serialize "
              },
              {
                "k": "code",
                "text": "protocol::ChatResponse"
              },
              {
                "k": "text",
                "text": " (the wire-contract\ntest already pins the exact JSON shape "
              },
              {
                "k": "code",
                "text": "nika-providers"
              },
              {
                "k": "text",
                "text": " parses)."
              }
            ],
            [
              {
                "k": "code",
                "text": "GET /health"
              },
              {
                "k": "text",
                "text": " — liveness for the future supervisor (ADR-091 isolation)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "One in-flight generation"
              },
              {
                "k": "text",
                "text": " (v1 queue discipline per the crate spec §4bis):\nconcurrency is a "
              },
              {
                "k": "code",
                "text": "Mutex"
              },
              {
                "k": "text",
                "text": " around the backend, not an async executor problem."
              }
            ],
            [
              {
                "k": "text",
                "text": "No TLS (loopback only) · no auth (loopback only · the supervisor owns the\nport) · no streaming yet ("
              },
              {
                "k": "code",
                "text": "stream: true"
              },
              {
                "k": "text",
                "text": " returns the non-streamed body v1)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Options considered",
          "id": "options-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Option · Deps pulled · Model · Verdict"
              }
            ],
            [
              {
                "k": "text",
                "text": "**`tiny_http`** · ~1 (+ ascii) · blocking · thread-per-connection · ✅ **chosen** — fits \"one endpoint · one in-flight\" exactly; trivially auditable; zero async runtime requirement in the server loop"
              }
            ],
            [
              {
                "k": "text",
                "text": "`hyper` (raw) · ~10 (tokio tower http body…) · async · full control · deferred — the control it buys (streamed bodies · backpressure) is only needed when SSE streaming becomes load-bearing"
              }
            ],
            [
              {
                "k": "text",
                "text": "`axum` · ~20+ · async framework · routing/extractors · refused for v1 — a router/extractor framework for two routes is dependency surface without benefit (lean-core doctrine · alignment Rule 3 ranking)"
              }
            ],
            [
              {
                "k": "text",
                "text": "hand-rolled `std::net::TcpListener` + HTTP parse · 0 · blocking · refused — re-implementing HTTP/1.1 parsing (chunked encoding · header folding · continue) is the one wheel not worth re-inventing on a security boundary"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "`tiny_http` serves the v1 sidecar"
            },
            {
              "k": "text",
              "text": ", behind a dedicated "
            },
            {
              "k": "code",
              "text": "server"
            },
            {
              "k": "text",
              "text": " feature,\northogonal to "
            },
            {
              "k": "code",
              "text": "local-infer"
            },
            {
              "k": "text",
              "text": " (both off by default — the default "
            },
            {
              "k": "code",
              "text": "nika"
            },
            {
              "k": "text",
              "text": " build\nlinks neither candle nor the server; CI exercises the full HTTP round-trip\nagainst "
            },
            {
              "k": "code",
              "text": "MockBackend"
            },
            {
              "k": "text",
              "text": " without building the candle stack; the sidecar binary\nenables both). Sizing facts that drove the call:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "The workload is sequential by design."
              },
              {
                "k": "text",
                "text": " ADR-091 + the crate spec lock\none in-flight generation per sidecar process; a multi-minute CPU/Metal\n"
              },
              {
                "k": "code",
                "text": "forward"
              },
              {
                "k": "text",
                "text": " loop dominates every request. An async stack cannot speed this\nup; it can only add dependency surface around a blocking core."
              }
            ],
            [
              {
                "k": "strong",
                "text": "The dependency tree is the moat surface."
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "tiny_http"
              },
              {
                "k": "text",
                "text": " keeps the\n"
              },
              {
                "k": "code",
                "text": "local-infer"
              },
              {
                "k": "text",
                "text": " feature's "
              },
              {
                "k": "em",
                "text": "server"
              },
              {
                "k": "text",
                "text": " increment near zero; the audit story\n(\"what runs when I enable local inference?\") stays answerable in one\nsitting."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Escape hatch is real, not hypothetical."
              },
              {
                "k": "text",
                "text": " The server module is ~200 LOC\nover "
              },
              {
                "k": "code",
                "text": "BackendDyn"
              },
              {
                "k": "text",
                "text": " + the "
              },
              {
                "k": "code",
                "text": "protocol"
              },
              {
                "k": "text",
                "text": " types. If SSE streaming graduates from\nfollow-up to requirement, swapping the listener layer for hyper is a\ncontained rewrite of one module — the wire types, backend seam, and tests\nall survive unchanged."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Tests stay hermetic."
              },
              {
                "k": "text",
                "text": " A blocking server on an ephemeral port +\n"
              },
              {
                "k": "code",
                "text": "MockBackend"
              },
              {
                "k": "text",
                "text": " gives a full client→server→backend round-trip in a plain\n"
              },
              {
                "k": "code",
                "text": "#[test]"
              },
              {
                "k": "text",
                "text": " (no tokio runtime juggling), including the self-consistency\ncheck: "
              },
              {
                "k": "code",
                "text": "nika-providers"
              },
              {
                "k": "text",
                "text": "' own "
              },
              {
                "k": "code",
                "text": "OpenAiCompat"
              },
              {
                "k": "text",
                "text": " parser reading a live response."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika-infer-local"
              },
              {
                "k": "text",
                "text": " gains a "
              },
              {
                "k": "code",
                "text": "server"
              },
              {
                "k": "text",
                "text": " module (feature "
              },
              {
                "k": "code",
                "text": "local-infer"
              },
              {
                "k": "text",
                "text": "):\n"
              },
              {
                "k": "code",
                "text": "serve(addr, backend) -> ServerHandle"
              },
              {
                "k": "text",
                "text": " with graceful shutdown; errors map\nto OpenAI-compat error JSON ("
              },
              {
                "k": "code",
                "text": "{\"error\": {\"message\", \"type\"}}"
              },
              {
                "k": "text",
                "text": ") so the\nexisting provider-side error mapping in "
              },
              {
                "k": "code",
                "text": "nika-providers"
              },
              {
                "k": "text",
                "text": " applies unchanged."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-providers"
              },
              {
                "k": "text",
                "text": " gains the "
              },
              {
                "k": "code",
                "text": "local"
              },
              {
                "k": "text",
                "text": " profile row (wire "
              },
              {
                "k": "code",
                "text": "OpenAiCompat"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "base_url http://127.0.0.1:<port>"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "requires_key: false"
              },
              {
                "k": "text",
                "text": ") — one catalog\nrow, the same shape as "
              },
              {
                "k": "code",
                "text": "ollama"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "lmstudio"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "llamacpp"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "The engine's local-first default path becomes fully ours in Rust:\n"
              },
              {
                "k": "code",
                "text": ".nika.yaml"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "model: local/<alias>"
              },
              {
                "k": "text",
                "text": " → providers wire → tiny_http server →\ncandle backend. The external-daemon profiles ("
              },
              {
                "k": "code",
                "text": "ollama"
              },
              {
                "k": "text",
                "text": " etc.) remain as\npeer options — additive, no removal (the catalog is a menu, not a fork)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Streaming ("
              },
              {
                "k": "code",
                "text": "stream: true"
              },
              {
                "k": "text",
                "text": " SSE deltas) is explicitly a follow-up: the\n"
              },
              {
                "k": "code",
                "text": "GenerationChunk"
              },
              {
                "k": "text",
                "text": " type exists; the transport decision re-opens (hyper)\nonly if tiny_http chunked responses prove insufficient in practice."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋 Nika — workflow engine for AI, AGPL, SuperNovae Studio."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-094",
      "file": "adr-094-nika-pck-registry-architecture.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        }
      ],
      "words": 890,
      "sha256": "85c44a3d1fbc61ff288ef4147caa7c6ffc8d15a488ad2384749ee4b66f56ea28",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-094: nika-pck — content-addressed sharing with trust in the artifact",
          "id": "adr-094-nika-pck--content-addressed-sharing-with-trust-in-the-artifact"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "ACCEPTED 2026-07-08"
            },
            {
              "k": "text",
              "text": " (operator ratification · D-2026-07-08-N2): the D4\nreserved-core taxonomy is LOCKED and FCI-004's pck-types list is amended to\nmatch in the same PR (the prior 9-list was aspirational — "
            },
            {
              "k": "code",
              "text": "recipe"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "shield"
            },
            {
              "k": "text",
              "text": "/\n"
            },
            {
              "k": "code",
              "text": "lints"
            },
            {
              "k": "text",
              "text": " had zero code usage). This unblocks "
            },
            {
              "k": "code",
              "text": "nika-pck-manifest"
            },
            {
              "k": "text",
              "text": ", the 42nd\ncrate (the 42/42 master plan §2: closed "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " ArtifactKind of\nthe 11 named classes + "
            },
            {
              "k": "code",
              "text": "CustomTool(String)"
            },
            {
              "k": "text",
              "text": " for "
            },
            {
              "k": "code",
              "text": "x-<vendor>:*"
            },
            {
              "k": "text",
              "text": ", total\ndeserialization)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Nika already ships the seed of a sharing layer: "
            },
            {
              "k": "code",
              "text": "nika-pack"
            },
            {
              "k": "text",
              "text": " (a versioned\ncontent pack — manifest + per-file sha256, embedded in the binary) and\n"
            },
            {
              "k": "code",
              "text": "nika-blob"
            },
            {
              "k": "text",
              "text": " (a blake3 content-addressed store, admitted). The planned "
            },
            {
              "k": "code",
              "text": "pck"
            },
            {
              "k": "text",
              "text": "\ncluster (5 crates, reserved in the crate plan) needs an architecture before\nany scaffold. Meanwhile "
            },
            {
              "k": "code",
              "text": "nika check"
            },
            {
              "k": "text",
              "text": " (ADR-092) statically proves what a\nworkflow "
            },
            {
              "k": "em",
              "text": "does"
            },
            {
              "k": "text",
              "text": " — schema validity, DAG soundness, effects, permits, secrets\nread, cost interval — "
            },
            {
              "k": "strong",
              "text": "before it runs"
            },
            {
              "k": "text",
              "text": ". That proof is the design input\neverything below follows from."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Two decades of package-registry failure modes inform the constraints\n(install-script worms · unpublish cascades · namespace squatting · LLM-era\nhallucinated-name squatting per arXiv:2406.10279 · registry capture):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "No code executes at install time."
              },
              {
                "k": "text",
                "text": " Ever. Artifacts are data."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Nothing installed can disappear or change."
              },
              {
                "k": "text",
                "text": " Content-addressing +\nlockfiles, not mutable tags."
              }
            ],
            [
              {
                "k": "strong",
                "text": "A name must not be the security boundary."
              },
              {
                "k": "text",
                "text": " Names get 2 of\n{human-meaningful, decentralized, secure} — so identity must not depend\non a name resolver staying honest."
              }
            ],
            [
              {
                "k": "strong",
                "text": "The index must be losable."
              },
              {
                "k": "text",
                "text": " If the discovery surface dies or\nmisbehaves, installed software and pinned builds keep working and the\nindex is re-hostable by anyone (it's a git repo)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D1 · Identity ⊥ discovery (the structural split)",
          "id": "d1--identity--discovery-the-structural-split"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Identity"
              },
              {
                "k": "text",
                "text": " is fully decentralized: an artifact IS its blake3 content\nhash; a "
              },
              {
                "k": "em",
                "text": "ref"
              },
              {
                "k": "text",
                "text": " is a git URL + path + version (Go-module style naming — the\nhosting platform is the namespace, no global name table to squat).\nPublishing = pushing to a git repo you own. Authors sign manifests\n(minisign · detached, offline-verifiable)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Discovery"
              },
              {
                "k": "text",
                "text": " is a thin, forkable, git-hosted "
              },
              {
                "k": "strong",
                "text": "index"
              },
              {
                "k": "text",
                "text": ": rows of\n"
              },
              {
                "k": "code",
                "text": "ref → {hash, sig, cert-summary, tags}"
              },
              {
                "k": "text",
                "text": ". The default index is a\nconvenience, not an authority — "
              },
              {
                "k": "code",
                "text": "nika"
              },
              {
                "k": "text",
                "text": " accepts any index URL, and the\nlockfile pins hashes so builds survive any index dying."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D2 · One CAS, two object sizes",
          "id": "d2--one-cas-two-object-sizes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "All artifact content lands in the existing blake3 CAS ("
            },
            {
              "k": "code",
              "text": "nika-blob"
            },
            {
              "k": "text",
              "text": ") — the\nplanned separate "
            },
            {
              "k": "code",
              "text": "nika-pck-store"
            },
            {
              "k": "text",
              "text": " crate is "
            },
            {
              "k": "strong",
              "text": "folded into `nika-blob`"
            },
            {
              "k": "text",
              "text": "\n(one store, one dedup domain, resumable fetch for free). Text artifacts\n(workflows/packs/skills/agents/configs — KB) ride git; the one heavy class\n("
            },
            {
              "k": "strong",
              "text": "model weights"
            },
            {
              "k": "text",
              "text": ", GB) is fetched from its upstream source (e.g. HF) by\nhash-pinned pointer and landed in the same CAS. Weights are "
            },
            {
              "k": "strong",
              "text": "GGUF /\nsafetensors only"
            },
            {
              "k": "text",
              "text": " — formats whose load path executes no code."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D3 · Trust lives in the artifact (conformance certs)",
          "id": "d3--trust-lives-in-the-artifact-conformance-certs"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Every shared artifact carries a "
            },
            {
              "k": "strong",
              "text": "cert"
            },
            {
              "k": "text",
              "text": ": the "
            },
            {
              "k": "code",
              "text": "nika check"
            },
            {
              "k": "text",
              "text": " static proof\n(conformance PASS · effects · permits · secrets-read enumeration · cost\ninterval · content sha set), signed by the author, "
            },
            {
              "k": "strong",
              "text": "re-verifiable locally\nby the installer"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "nika verify"
            },
            {
              "k": "text",
              "text": " re-runs the oracle — the cert is a claim\nthe installer's own engine re-derives, not a badge it must believe).\nInstall UX surfaces the cert before anything lands: what it touches, what\nit may spend, what it reads."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D4 · The artifact taxonomy is closed-but-extensible",
          "id": "d4--the-artifact-taxonomy-is-closed-but-extensible"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "v1 classes: workflow · pack · skill · agent-preset · template-variant ·\nmodel-pointer · mcp-config · conformance-fixture · custom-tool declaration\n("
            },
            {
              "k": "code",
              "text": "x-<vendor>:*"
            },
            {
              "k": "text",
              "text": " — capability-scoped, the tool's "
            },
            {
              "k": "em",
              "text": "declaration"
            },
            {
              "k": "text",
              "text": " is data; any\nbinary it names is sandbox-scoped and never auto-fetched) · provider-profile\n· policy-preset · bench-suite. A future class is "
            },
            {
              "k": "strong",
              "text": "reserved, not built"
            },
            {
              "k": "text",
              "text": ":\ncognitive-machinery bundles (ontology schema · retrieval-pipeline config ·\nscheduling params — configuration for the memory subsystem; "
            },
            {
              "k": "strong",
              "text": "never user\ngraph data"
            },
            {
              "k": "text",
              "text": ", which is local-only by doctrine)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D5 · Anti-hallucination guard (LLM-era squatting)",
          "id": "d5--anti-hallucination-guard-llm-era-squatting"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika"
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "strong",
              "text": "never auto-installs a name an LLM suggested"
            },
            {
              "k": "text",
              "text": ": "
            },
            {
              "k": "code",
              "text": "nika add"
            },
            {
              "k": "text",
              "text": " resolves\nrefs interactively (show author · sig state · cert summary · download\nprovenance) unless the exact hash is already lockfile-pinned or\n"
            },
            {
              "k": "code",
              "text": "--yes --ref-hash"
            },
            {
              "k": "text",
              "text": " is passed (CI). Typo-distance warnings against the\nindex's existing refs apply at resolve time (the did-you-mean machinery the\nCLI already has)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D6 · Crate mapping (amended cluster)",
          "id": "d6--crate-mapping-amended-cluster"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crate · Layer · Role"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-pck-manifest` · L0 · manifest + cert + lockfile types (pure · serde)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-pck-registry` · L1 · index fetch/parse · ref resolution (over kernel http seam)"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-git` · L1 · gix wrapper · ref → repo content"
              }
            ],
            [
              {
                "k": "text",
                "text": "`nika-pck` · L2 · orchestrator · resolve→fetch→verify→land pipeline"
              }
            ],
            [
              {
                "k": "text",
                "text": "~~`nika-pck-store`~~ · — · **folded into `nika-blob`** (D2)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "CLI verbs (L4 · the "
            },
            {
              "k": "code",
              "text": "nika"
            },
            {
              "k": "text",
              "text": " binary): "
            },
            {
              "k": "code",
              "text": "add · remove · search · publish · verify · model pull|list|rm"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "No central artifact server exists to operate, fund, or capture; the\ndefault index is a git repo anyone can fork, and losing it loses nothing\ninstalled."
              }
            ],
            [
              {
                "k": "text",
                "text": "Supply-chain posture is structural, not moderated: data-only artifacts\n(no install hooks), hash-pinned content, offline signature + cert\nre-verification, code-free model formats."
              }
            ],
            [
              {
                "k": "text",
                "text": "The cert turns "
              },
              {
                "k": "code",
                "text": "nika check"
              },
              {
                "k": "text",
                "text": " (ADR-092) into the sharing layer's trust\nprimitive — one oracle serves authoring, CI, and distribution."
              }
            ],
            [
              {
                "k": "text",
                "text": "Cost: signing/key UX and index curation conventions need their own pass\n(follow-ups); resumable big-blob fetch rides the CAS."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "🦋 Nika — workflow engine for AI, AGPL, SuperNovae Studio."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-095",
      "file": "adr-095-exec-security-architecture.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "The layers",
          "anchor": "the-layers"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Implementation status",
          "anchor": "implementation-status"
        },
        {
          "title": "References",
          "anchor": "references"
        }
      ],
      "words": 2371,
      "sha256": "61b9ffe540bdfcbd7da724420bd8c3819d4b8822bc540534dc52e3dfd215cf5f",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-095 · exec verb security architecture",
          "id": "adr-095--exec-verb-security-architecture"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The "
            },
            {
              "k": "code",
              "text": "exec"
            },
            {
              "k": "text",
              "text": " verb is the most dangerous of the 4 verbs: it runs OS processes\nwhose arguments are CEL-interpolated from upstream task outputs — including\n"
            },
            {
              "k": "code",
              "text": "infer"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "agent"
            },
            {
              "k": "text",
              "text": " (LLM) outputs, which are attacker-influenceable by\nconstruction (indirect prompt injection · arXiv 2302.12173). A deep audit\n(2026-06-12) plus two primary-source research sweeps (arXiv agent-security\nliterature + 2026 competitor practice) found one active vulnerability and\nseveral missing SOTA layers."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "The audit findings",
          "id": "the-audit-findings"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Finding · Severity · Was"
              }
            ],
            [
              {
                "k": "text",
                "text": "**Array form silently shell-joined** · P0 · The spec (02-verbs §exec) sells `command: [\"prog\", \"{{ v }}\"]` as \"the STRUCTURAL fix for command injection\". But the runtime joined the rendered argv with spaces and ran it through the OS shell (`ExecInput` carried only a `String`), so the *safe* form was injection-VULNERABLE while looking safe."
              }
            ],
            [
              {
                "k": "text",
                "text": "No OS sandbox · P1 · The verb summary reads \"exec: Shell command **with sandboxing**\"; none existed. The child ran with the engine's full privileges/network/filesystem/env."
              }
            ],
            [
              {
                "k": "text",
                "text": "Blocklist = denylist · P1 · A string-matching denylist that both leaks (∞ bypass variants) AND false-positives legit CI (`timeout`/`env`/`nice`/`printenv` blocked), re-scanning a flattened argv line that has no shell to parse."
              }
            ],
            [
              {
                "k": "text",
                "text": "Env inherited wholesale · P1 · The child inherited the engine's full environment — the env-var-injection class (`LD_PRELOAD`/`BASH_ENV`/`GIT_SSH_COMMAND` …) and secret leakage."
              }
            ],
            [
              {
                "k": "text",
                "text": "Grandchildren leak · P2 · `kill_on_drop` killed only the direct child; a backgrounded grandchild orphaned on timeout."
              }
            ],
            [
              {
                "k": "text",
                "text": "`permits:` not enforced at exec · P1 · `Permits {fs,net,exec,tool}` is fully modeled and spec-mandated (\"the engine MUST enforce on both surfaces\" · NIKA-SEC-004), but the runtime sink never consulted it."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Research convergence (theory + practice agree — high signal)",
          "id": "research-convergence-theory--practice-agree--high-signal"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Control/data-plane separation"
              },
              {
                "k": "text",
                "text": " (CaMeL/DeepMind 2503.18813, design-patterns 2506.08837, Fides/MS 2505.23643): the workflow YAML is the "
              },
              {
                "k": "em",
                "text": "trusted control plane"
              },
              {
                "k": "text",
                "text": "; interpolated values are "
              },
              {
                "k": "em",
                "text": "untrusted data"
              },
              {
                "k": "text",
                "text": " that may fill declared slots but never select the program, flags, or DAG edges. A declarative DAG + "
              },
              {
                "k": "code",
                "text": "permits:"
              },
              {
                "k": "text",
                "text": " give this almost for free."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Array-not-shell"
              },
              {
                "k": "text",
                "text": " (GitHub Actions script-injection: 24,905 issues / 50 repos, arXiv 2208.03837; CVE-2025-30066): string-templating untrusted values into a shell is THE workflow-engine injection class."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Default-deny symbolic policy"
              },
              {
                "k": "text",
                "text": " (Progent 2504.11703, AgentSpec 2503.18666, AgentBound 2510.21236): an allowlist over binary + argv, deterministic, default-deny = "
              },
              {
                "k": "code",
                "text": "permits.exec"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "OS sandbox floor"
              },
              {
                "k": "text",
                "text": " (RedCode 2411.07781, SandboxEval 2504.00018; Claude Code / Codex / Cursor 2026): \"assume the command is hostile\" is uncontested; every coding-agent CLI ships an OS-level safe-by-default sandbox, all 100% local/OSS (sovereignty-aligned). Workflow engines (n8n/Airflow/Temporal) ship exec unsandboxed — Nika's chance to lead."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Argument-injection canon"
              },
              {
                "k": "text",
                "text": " (GTFOArgs, SonarSource/CAPEC-6): even without a shell, an argv value injects via flags ("
              },
              {
                "k": "code",
                "text": "ssh -o ProxyCommand"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "tar --checkpoint-action"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "git -c core.sshCommand"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "curl -K"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "find -exec"
              },
              {
                "k": "text",
                "text": "). The "
              },
              {
                "k": "code",
                "text": "--"
              },
              {
                "k": "text",
                "text": " end-of-options discipline + a per-binary flag denylist."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Detection ⇒ telemetry only"
              },
              {
                "k": "text",
                "text": " (2503.00061: 8/8 published detector defenses bypassed >50% under adaptive attack): a string-matching detector must never be the sole boundary."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "permits:"
            },
            {
              "k": "text",
              "text": " is the "
            },
            {
              "k": "strong",
              "text": "declaration"
            },
            {
              "k": "text",
              "text": " (the trusted control plane, author-written,\nin-file · spec 01 §permits). Three layers "
            },
            {
              "k": "strong",
              "text": "enforce"
            },
            {
              "k": "text",
              "text": " it; the blocklist\ndemotes from \"the wall\" to a shell-mode tripwire."
            }
          ]
        },
        {
          "k": "code",
          "lang": "text",
          "text": "  permits: { fs, net, exec, tool }      DECLARATION · trusted · spec 01 §permits\n        |  enforced at 3 layers\n  L2 nika-verb-exec  — ExecCommand::{ Shell(String) | Argv(Vec<String>) }\n     • argv = the structural injection fix (execve, no shell)        [LAYER 1]\n     • env scrub (deny LD_PRELOAD/BASH_ENV/… · reset PATH)           [LAYER 3]\n  L3 nika-runtime    — dispatch the array form as a vector (no join)\n     • permits.exec allowlist on the resolved program (NIKA-SEC-004) [LAYER 2]\n  L0 nika-schema     — one-obvious-way/008 steers string→array; the\n     arg-injection rule-pack flags dangerous flags from interpolation [LAYER 2b]\n  L1 nika-exec-runner — shell-mode blocklist tripwire · argv program-floor\n     • process-group kill (reap grandchildren) · output cap · timeout [LAYER 4/5]\n  L1 nika-sandbox    — Landlock+seccomp (Linux) / Seatbelt (macOS):\n     net-deny + FS-confine from permits.fs/net + rlimits             [LAYER 6]"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The inversion: "
            },
            {
              "k": "strong",
              "text": "array (structural) + sandbox (OS) are the walls; the\n`permits.exec` allowlist is the intent gate; the blocklist is the shell-mode\ntripwire"
            },
            {
              "k": "text",
              "text": " — not the sole, leaky, false-positive denylist it was."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "The layers",
          "id": "the-layers"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 1 — Array execution, no shell (L2 + L3 · SHIPPED)",
          "id": "layer-1--array-execution-no-shell-l2--l3--shipped"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "ExecInput.command"
            },
            {
              "k": "text",
              "text": " is now "
            },
            {
              "k": "code",
              "text": "ExecCommand { Shell(String) | Argv(Vec<String>) }"
            },
            {
              "k": "text",
              "text": ".\nThe array form maps to "
            },
            {
              "k": "code",
              "text": "execve"
            },
            {
              "k": "text",
              "text": " with the shell flag OFF; every element is one\nliteral argv token — an interpolated value can never break out of its argument\nbecause there is no shell to parse it. The runtime renders each array element\nand passes a vector (no join). The string form keeps the OS shell for genuine\npipelines. This closes the P0 and is the structural fix the spec mandates."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 2 — permits.exec allowlist (L3 · SHIPPED)",
          "id": "layer-2--permitsexec-allowlist-l3--shipped"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The runtime threads the workflow's "
            },
            {
              "k": "code",
              "text": "Permits"
            },
            {
              "k": "text",
              "text": " to the exec sink. "
            },
            {
              "k": "code",
              "text": "exec: false"
            },
            {
              "k": "text",
              "text": "\n(or omitted under a declared boundary) refuses any process; a program\nallowlist must contain the resolved leading token (argv[0], or the first word\nof a shell line) — matching the static "
            },
            {
              "k": "code",
              "text": "nika check"
            },
            {
              "k": "text",
              "text": " (permits_fit) so a\nworkflow that passes the check never fails at runtime for a different reason.\nThe static check owns statically-detectable escapes; the runtime owns the\ninterpolated programs static analysis defers. A "
            },
            {
              "k": "code",
              "text": "#[non_exhaustive]"
            },
            {
              "k": "text",
              "text": " future\npermit form fails CLOSED."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 2b — Discoverability + arg-injection lints (L0 nika-schema)",
          "id": "layer-2b--discoverability--arg-injection-lints-l0-nika-schema"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "one-obvious-way/008"
              },
              {
                "k": "text",
                "text": " (SHIPPED) warns when an interpolated value lands in a\nstring command that needs no shell, pointing to the injection-safe array\nform — fires only when the literal parts carry no shell metacharacter (a\ngenuine pipeline keeps the string form · low-false-positive)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "arg-injection rule-pack"
              },
              {
                "k": "text",
                "text": " (DESIGNED · the differentiator) — a clean-room\ncatalog (from the public GTFOArgs + SonarSource/CAPEC-6 facts) of dangerous\nflags per binary; flags an interpolated value landing in a flag slot / a\nleading-dash value for a known binary, and recommends the "
              },
              {
                "k": "code",
                "text": "--"
              },
              {
                "k": "text",
                "text": " end-of-options\nseparator. No competing engine ships this."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 3 — Env hygiene (L1 + L2 · SHIPPED · three sub-layers)",
          "id": "layer-3--env-hygiene-l1--l2--shipped--three-sub-layers"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Three sub-layers, weakest-to-strongest, each independent of the OS sandbox:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "POSIX-name validation (L2 · `nika-verb-exec`)."
              },
              {
                "k": "text",
                "text": " An "
              },
              {
                "k": "code",
                "text": "env:"
              },
              {
                "k": "text",
                "text": " key must match\n"
              },
              {
                "k": "code",
                "text": "[A-Za-z_][A-Za-z0-9_]*"
              },
              {
                "k": "text",
                "text": " — this refuses the dynamic-name carrier class,\nchiefly the exported-shell-function form "
              },
              {
                "k": "code",
                "text": "BASH_FUNC_x%%"
              },
              {
                "k": "text",
                "text": " that a bash "
              },
              {
                "k": "code",
                "text": "sh -c"
              },
              {
                "k": "text",
                "text": "\nchild imports and runs (the payload never appears in the scanned command\nstring). Empty / "
              },
              {
                "k": "code",
                "text": "="
              },
              {
                "k": "text",
                "text": " / NUL keys still fail (a strict narrowing)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Dangerous-env denylist (L1 · `nika-exec-runner`)."
              },
              {
                "k": "text",
                "text": " The runner ALWAYS\nstrips the injection vectors that grant code/library injection with no\ndangerous flag: "
              },
              {
                "k": "code",
                "text": "LD_PRELOAD"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "DYLD_INSERT_LIBRARIES"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "BASH_ENV"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "GIT_SSH_COMMAND"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "PYTHONSTARTUP"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "NODE_OPTIONS"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "IFS"
              },
              {
                "k": "text",
                "text": ", plus\n"
              },
              {
                "k": "code",
                "text": "HOSTALIASES"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "TERMINFO(_DIRS)"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "TERMCAP"
              },
              {
                "k": "text",
                "text": " (file-read / crafted-entry\nload) — independent of "
              },
              {
                "k": "code",
                "text": "pre_validated"
              },
              {
                "k": "text",
                "text": " (the floor wins over an explicit set)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Ambient-secret scrub (L1 · `nika-exec-runner` ← composed by `nika-cli`)."
              },
              {
                "k": "text",
                "text": "\nThe engine loads provider API keys from ITS env ("
              },
              {
                "k": "code",
                "text": "config_from_env"
              },
              {
                "k": "text",
                "text": ") for its\nOWN inference calls; an exec child inherits them by ambient default and an\nuntrusted command could exfiltrate them ("
              },
              {
                "k": "code",
                "text": "printenv"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "cat /proc/self/environ"
              },
              {
                "k": "text",
                "text": ").\nThe composition root injects the provider-key NAMES (from the catalog) into\n"
              },
              {
                "k": "code",
                "text": "TokioShell"
              },
              {
                "k": "text",
                "text": ", which strips each from every child UNLESS the workflow set it\nEXPLICITLY in "
              },
              {
                "k": "code",
                "text": "env:"
              },
              {
                "k": "text",
                "text": " (explicit intent wins · only the ambient copy is\nremoved). Least-privilege by default; empty scrub set = legacy behavior."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The stronger "
            },
            {
              "k": "em",
              "text": "full"
            },
            {
              "k": "text",
              "text": " clean-slate posture (drop ALL inherited env + a curated\n"
            },
            {
              "k": "code",
              "text": "PATH"
            },
            {
              "k": "text",
              "text": ") still ships with the sandbox (Layer 6), which knows the confined\nworkflow's complete declared env needs; sub-layer 3 is the targeted\nsecret-scrub floor that holds before the sandbox is composed in."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 4/5 — Process lifecycle (L1 · SHIPPED partial)",
          "id": "layer-45--process-lifecycle-l1--shipped-partial"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The child spawns in its own process group (the safe std "
            },
            {
              "k": "code",
              "text": "process_group(0)"
            },
            {
              "k": "text",
              "text": ", no\nunsafe "
            },
            {
              "k": "code",
              "text": "pre_exec"
            },
            {
              "k": "text",
              "text": "); on timeout/cancel the whole group is signalled (SIGTERM →\ngrace → SIGKILL via "
            },
            {
              "k": "code",
              "text": "nix::killpg"
            },
            {
              "k": "text",
              "text": ") so detached grandchildren die. Output is\ncapped (64 MiB/stream) and the timeout hard-kills. Resource rlimits\n(RLIMIT_CPU/FSIZE/NPROC + no-core-dump) need a child-side "
            },
            {
              "k": "code",
              "text": "pre_exec"
            },
            {
              "k": "text",
              "text": " (unsafe),\nso they move to "
            },
            {
              "k": "code",
              "text": "nika-sandbox"
            },
            {
              "k": "text",
              "text": " (which owns the low-level confinement)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 6 — OS sandbox (PER-PLATFORM L1 crates · macOS SHIPPED + verified)",
          "id": "layer-6--os-sandbox-per-platform-l1-crates--macos-shipped--verified"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Reconciliation (2026-06-13)."
            },
            {
              "k": "text",
              "text": " The kernel already RESERVES a capability\n"
            },
            {
              "k": "code",
              "text": "Sandbox"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "plugin::sandbox"
            },
            {
              "k": "text",
              "text": " · WASM/MCP · the "
            },
            {
              "k": "code",
              "text": "enter()"
            },
            {
              "k": "text",
              "text": " self-restriction model\n· v0.100), whose doc names the future impl crates "
            },
            {
              "k": "code",
              "text": "nika-sandbox-landlock"
            },
            {
              "k": "text",
              "text": "\n(Linux) and "
            },
            {
              "k": "code",
              "text": "nika-sandbox-seatbelt"
            },
            {
              "k": "text",
              "text": " (macOS). Confining the "
            },
            {
              "k": "code",
              "text": "exec"
            },
            {
              "k": "text",
              "text": " verb's CHILD\nprocess is a DISTINCT operation — \"transform the command into its confined\nform\" — so it rides a NEW additive kernel seam, NOT the reserved trait, and\nships as the per-platform crates the reservation already names (operator-locked\n2026-06-13: per-platform crates + a new trait · supersedes the original single\n"
            },
            {
              "k": "code",
              "text": "nika-sandbox"
            },
            {
              "k": "text",
              "text": " framing of this ADR)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "The seam (L0.5 · `nika-kernel-core/src/io/command_sandbox.rs` · SHIPPED)."
            },
            {
              "k": "text",
              "text": "\nA new "
            },
            {
              "k": "code",
              "text": "CommandSandbox"
            },
            {
              "k": "text",
              "text": " trait — sync, object-safe, "
            },
            {
              "k": "code",
              "text": "confine(spec, ShellCommand) -> ShellCommand"
            },
            {
              "k": "text",
              "text": " — plus "
            },
            {
              "k": "code",
              "text": "SandboxSpec"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "fs_read"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "fs_write"
            },
            {
              "k": "text",
              "text": " prefixes ·\n"
            },
            {
              "k": "code",
              "text": "allow_network"
            },
            {
              "k": "text",
              "text": " · derived from "
            },
            {
              "k": "code",
              "text": "permits.fs/net"
            },
            {
              "k": "text",
              "text": "), "
            },
            {
              "k": "code",
              "text": "CommandSandboxError"
            },
            {
              "k": "text",
              "text": "\n("
            },
            {
              "k": "code",
              "text": "Unavailable"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "Profile"
            },
            {
              "k": "text",
              "text": " · fail-closed), and a "
            },
            {
              "k": "code",
              "text": "NoopSandbox"
            },
            {
              "k": "text",
              "text": " (the explicit,\nauditable \"unconfined\" choice). Add a "
            },
            {
              "k": "code",
              "text": "ShellCommand.sandbox"
            },
            {
              "k": "text",
              "text": " field. The\n"
            },
            {
              "k": "strong",
              "text": "wrapper model"
            },
            {
              "k": "text",
              "text": " (no "
            },
            {
              "k": "code",
              "text": "unsafe"
            },
            {
              "k": "text",
              "text": ", no FFI · the seam returns a TRANSFORMED command,\nnot a "
            },
            {
              "k": "code",
              "text": "pre_exec"
            },
            {
              "k": "text",
              "text": " closure) replaces the original "
            },
            {
              "k": "code",
              "text": "Confinement"
            },
            {
              "k": "text",
              "text": "-closure design —\nit keeps the crates at "
            },
            {
              "k": "code",
              "text": "unsafe_code = forbid"
            },
            {
              "k": "text",
              "text": " and is what every coding-agent CLI\nuses."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "macOS — `nika-sandbox-seatbelt` (SHIPPED · adversarially verified)."
            },
            {
              "k": "text",
              "text": " Wraps a\ncommand in the OS-shipped "
            },
            {
              "k": "code",
              "text": "sandbox-exec"
            },
            {
              "k": "text",
              "text": " launcher with a deny-default SBPL\nprofile generated from the spec: network denied unless granted; writes only\nunder declared "
            },
            {
              "k": "code",
              "text": "fs_write"
            },
            {
              "k": "text",
              "text": " + scratch; reads of the system paths every binary\nneeds, plus declared "
            },
            {
              "k": "code",
              "text": "fs_read"
            },
            {
              "k": "text",
              "text": " — so sensitive home paths ("
            },
            {
              "k": "code",
              "text": "~/.ssh"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "~/.aws"
            },
            {
              "k": "text",
              "text": ")\nare denied. Profile-injection via a malicious path is closed (control-char\nrefusal + quote-escaping). An adversarial jail suite RUNS "
            },
            {
              "k": "code",
              "text": "sandbox-exec"
            },
            {
              "k": "text",
              "text": " on\ndarwin and proves: a home-secret read is denied, a declared path is readable, a\nwrite outside the allowlist is denied, an ordinary command still runs. Wired\ninto "
            },
            {
              "k": "code",
              "text": "TokioShell::with_sandbox"
            },
            {
              "k": "text",
              "text": " (applied after the blocklist · fail-closed)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Linux — `nika-sandbox-landlock` (CI-GATED)."
            },
            {
              "k": "text",
              "text": " The sibling crate: "
            },
            {
              "k": "code",
              "text": "landlock"
            },
            {
              "k": "text",
              "text": "\n(FS allowlist) + "
            },
            {
              "k": "code",
              "text": "extrasafe"
            },
            {
              "k": "text",
              "text": "/seccomp (block ptrace/unshare/AF_UNIX) + net-deny"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "rlimits, via "
              },
              {
                "k": "code",
                "text": "bwrap"
              },
              {
                "k": "text",
                "text": " or a Landlock helper. Only partly verifiable off a Linux\nbox → a focused CI-backed arc. Same "
              },
              {
                "k": "code",
                "text": "CommandSandbox"
              },
              {
                "k": "text",
                "text": " seam."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Follow-on plumbing."
            },
            {
              "k": "text",
              "text": " Runtime derivation ("
            },
            {
              "k": "code",
              "text": "permits.fs/net"
            },
            {
              "k": "text",
              "text": " → "
            },
            {
              "k": "code",
              "text": "SandboxSpec"
            },
            {
              "k": "text",
              "text": " →\n"
            },
            {
              "k": "code",
              "text": "ExecInput.sandbox"
            },
            {
              "k": "text",
              "text": ") and composer injection (select + inject the platform\nbackend into "
            },
            {
              "k": "code",
              "text": "TokioShell"
            },
            {
              "k": "text",
              "text": ") connect a workflow's "
            },
            {
              "k": "code",
              "text": "permits:"
            },
            {
              "k": "text",
              "text": " to confinement\nend-to-end; the composer is the "
            },
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": "/engine surface (in flight)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Honest limits (documented in the crate): glob→subpath is a coarsening (path-\nprefix, not per-file); network is allow-all-or-deny (host-granular needs a\nproxy); resource rlimits + the SandboxEval egress suite are part of the\nlandlock arc. Full 12-gate admission (Linux backend · mutation ≥90% · review\nswarm) is the CI completion."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Layer 7 — Blocklist demoted to a tripwire (L1 · SHIPPED)",
          "id": "layer-7--blocklist-demoted-to-a-tripwire-l1--shipped"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The string-matching blocklist is no longer the wall. Shell mode keeps the full\nbattle-tested blocklist + the expansion-char refusal (the tripwire for the\ngenuinely-dangerous string path). Array mode gets an exact-basename\n"
            },
            {
              "k": "code",
              "text": "check_program"
            },
            {
              "k": "text",
              "text": " against a small dangerous-program set (privilege escalation +\nsystem control) — no fake-shell scan, which removes the "
            },
            {
              "k": "code",
              "text": "timeout"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "env"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "nice"
            },
            {
              "k": "text",
              "text": "/\n"
            },
            {
              "k": "code",
              "text": "printenv"
            },
            {
              "k": "text",
              "text": " false-positives. Per the adaptive-attack evidence, the blocklist is\ndefense-in-depth, never the primary boundary."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Positive."
            },
            {
              "k": "text",
              "text": " The structural injection hole is closed; the safe form is the\ndefault and discoverable; the capability boundary is enforced; the env / process\nclasses are shut; the sandbox (when shipped) makes \"even if the blocklist\nmisses it, it can't hurt you\" true. The architecture matches the converging\nSOTA and stays 100% local/OSS (sovereignty). The "
            },
            {
              "k": "code",
              "text": "permits:"
            },
            {
              "k": "text",
              "text": " declaration unifies\nall enforcement (the file is the security boundary)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Negative / interim gaps (logged, not silent)."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The argv program-floor no longer blocks a directly-authored dangerous command\n(e.g. a destructive "
              },
              {
                "k": "code",
                "text": "rm"
              },
              {
                "k": "text",
                "text": " on root) — that is gated by "
              },
              {
                "k": "code",
                "text": "permits.exec"
              },
              {
                "k": "text",
                "text": " + the\nsandbox, not the floor. Acceptable: the floor is identity-based; intent is\npolicy + sandbox."
              }
            ],
            [
              {
                "k": "text",
                "text": "The OS sandbox (Layer 6) is the big remaining lift, cross-platform and only\npartly verifiable off a Linux box — a focused CI-backed admission arc."
              }
            ],
            [
              {
                "k": "text",
                "text": "Runtime taint propagation (which resolved values are interpolation-derived)\nis deferred — the arg-injection guard ships as a static lint; runtime\ntaint-enforcement lands when the binding layer carries taint (shadow zone\n"
              },
              {
                "k": "code",
                "text": "l1-taint-runtime"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Operator/host policy + token budgets remain "
              },
              {
                "k": "code",
                "text": "nika-policy"
              },
              {
                "k": "text",
                "text": "'s job (gated on the\nkernel migration); the runtime "
              },
              {
                "k": "code",
                "text": "permits"
              },
              {
                "k": "text",
                "text": " enforcement is the interim."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Blocklist-only (status quo)."
              },
              {
                "k": "text",
                "text": " Rejected: both leaks and false-positives;\nthe adaptive-attack literature refutes detector-as-boundary."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Container/microVM per exec"
              },
              {
                "k": "text",
                "text": " (e2b, Firecracker). Rejected as the default:\nnon-sovereign (hosted) or heavy startup latency; the agent-CLI OS-sandbox\nmodel fits a local-first engine better."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Sandbox as a module in nika-exec-runner."
              },
              {
                "k": "text",
                "text": " Rejected: heavy platform deps +\nunsafe "
              },
              {
                "k": "code",
                "text": "pre_exec"
              },
              {
                "k": "text",
                "text": " + independent adversarial testing → its own L1 crate behind\na kernel seam is the Diamond-coherent shape."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Implementation status",
          "id": "implementation-status"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Layer · Crate(s) · Status"
              }
            ],
            [
              {
                "k": "text",
                "text": "1 array execution · nika-verb-exec · nika-runtime · ✅ shipped"
              }
            ],
            [
              {
                "k": "text",
                "text": "2 permits.exec · nika-runtime · ✅ shipped"
              }
            ],
            [
              {
                "k": "text",
                "text": "2b /008 steer · nika-schema · ✅ shipped"
              }
            ],
            [
              {
                "k": "text",
                "text": "2b arg-injection pack · nika-schema · ✅ shipped (arg-injection/001)"
              }
            ],
            [
              {
                "k": "text",
                "text": "3 env POSIX-name validation · nika-verb-exec · ✅ shipped (kills `BASH_FUNC_x%%` carrier)"
              }
            ],
            [
              {
                "k": "text",
                "text": "3 env dangerous-denylist · nika-exec-runner · ✅ shipped (+HOSTALIASES/TERMINFO/TERMCAP)"
              }
            ],
            [
              {
                "k": "text",
                "text": "3 env ambient-secret scrub · nika-exec-runner ← nika-cli · ✅ shipped (provider keys · explicit `env:` wins)"
              }
            ],
            [
              {
                "k": "text",
                "text": "3 env full clean-slate · nika-sandbox · 🔜 → L6 (drop-all + curated PATH)"
              }
            ],
            [
              {
                "k": "text",
                "text": "4/5 process group · nika-exec-runner · ✅ shipped · rlimits → L6"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 sandbox seam · nika-kernel-core (CommandSandbox) · ✅ shipped"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 sandbox macOS · nika-sandbox-seatbelt · ✅ shipped · adversarially verified"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 sandbox wiring · nika-exec-runner (with_sandbox) · ✅ shipped"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 sandbox Linux · nika-sandbox-landlock · ✅ shipped · CI-gated (bwrap in the tests leg · 4 jail proofs)"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 permits→spec + composer inject · nika-runtime · engine · ✅ shipped (v0.105.x · dispatch derives the spec from `permits:` · the composer wires seatbelt/landlock · noop note when no backend)"
              }
            ],
            [
              {
                "k": "text",
                "text": "7 blocklist tripwire · nika-exec-runner · ✅ shipped (mode-split)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "References",
          "id": "references"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Spec: "
              },
              {
                "k": "code",
                "text": "nika-spec spec/02-verbs.md §exec"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "spec/01-envelope.md §permits"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "spec/05-errors.md"
              },
              {
                "k": "text",
                "text": " (NIKA-EXEC-001/002 · NIKA-SEC-001/004)."
              }
            ],
            [
              {
                "k": "text",
                "text": "arXiv: 2302.12173 · 2208.03837 · 2503.18813 (CaMeL) · 2506.08837 · 2505.23643 (Fides) · 2504.11703 (Progent) · 2503.18666 (AgentSpec) · 2510.21236 (AgentBound) · 2411.07781 (RedCode) · 2504.00018 (SandboxEval) · 2503.00061 (adaptive attacks)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Catalogs: GTFOArgs · SonarSource argument-injection-vectors (CAPEC-6) · CVE-2024-24576 (BatBadBut · Windows guard)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Crates: "
              },
              {
                "k": "code",
                "text": "landlock"
              },
              {
                "k": "text",
                "text": " 0.4.5 · "
              },
              {
                "k": "code",
                "text": "extrasafe"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "nix"
              },
              {
                "k": "text",
                "text": " 0.30 · std "
              },
              {
                "k": "code",
                "text": "CommandExt::process_group"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "text",
                "text": "Related: ADR-001 (CRAFT) · ADR-003 (12-gate) · ADR-016 (cancellation) · ADR-080 (mcp-stdio sandbox) · ADR-081 (L1 guard contract)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-096",
      "file": "adr-096-agent-loop-intelligence.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "The `skill:` forward direction (NOT a v0.1 namespace)",
          "anchor": "the-skill-forward-direction-not-a-v01-namespace"
        },
        {
          "title": "Alternatives rejected",
          "anchor": "alternatives-rejected"
        },
        {
          "title": "Verification",
          "anchor": "verification"
        }
      ],
      "words": 1063,
      "sha256": "7ddc8f3ffff42df8950c58584bd92bd16995a210c05e6b6d562eca7c68a248fa",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-096 — The agent-loop intelligence layer",
          "id": "adr-096--the-agent-loop-intelligence-layer"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Status"
              },
              {
                "k": "text",
                "text": ": Accepted (2026-06-12)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Layer"
              },
              {
                "k": "text",
                "text": ": L2 ("
              },
              {
                "k": "code",
                "text": "nika-verb-agent"
              },
              {
                "k": "text",
                "text": ") + L0 vocabulary ("
              },
              {
                "k": "code",
                "text": "nika-event"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Relates"
              },
              {
                "k": "text",
                "text": ": ADR-092 (static-analysis ladder · the compose gate's checker),\nspec "
              },
              {
                "k": "code",
                "text": "02-verbs.md"
              },
              {
                "k": "text",
                "text": " §agent (the YAML contract — UNTOUCHED by this ADR)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The s12 "
            },
            {
              "k": "code",
              "text": "agent"
            },
            {
              "k": "text",
              "text": " verb shipped the canonical ReAct loop: infer → whitelisted\ndispatch → feed back, bounded by turns/tokens. Production agent traces across\nthe field show four recurring gaps, each with published evidence:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "No-progress loops"
              },
              {
                "k": "text",
                "text": " — repetitive-action cycles are a dominant agentic\nfailure class (TRAIL, arXiv:2505.08638); a turn budget alone burns the\nwhole budget before stopping."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Tool-context crowding"
              },
              {
                "k": "text",
                "text": " — injecting every tool schema every turn\ndegrades context budget and selection quality at scale (MCP-Zero,\narXiv:2506.01056; Gorilla, arXiv:2305.15334)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Self-composition without verification"
              },
              {
                "k": "text",
                "text": " — agents that generate\nartifacts (here: workflows) need «generation is not permission»\ndiscipline (PCE, arXiv:2605.24462; CodeAct, arXiv:2402.01030)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Opaque internals"
              },
              {
                "k": "text",
                "text": " — observability must expose the agent's DECISIONS,\nnot just its I/O (AgentOps, arXiv:2411.05285)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Four orthogonal, deterministic mechanisms inside the loop — "
            },
            {
              "k": "strong",
              "text": "zero new YAML"
            },
            {
              "k": "text",
              "text": "\n(the spec's "
            },
            {
              "k": "code",
              "text": "agent:"
            },
            {
              "k": "text",
              "text": " field table is untouched; tuning is engine-internal\n"
            },
            {
              "k": "code",
              "text": "AgentConfig"
            },
            {
              "k": "text",
              "text": ", embedder-set with production defaults):"
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "1 · Stall guard (`guard.rs` · NIKA-467)",
          "id": "1--stall-guard-guardrs--nika-467"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Turn signatures hash the turn's tool calls (key-order-canonical args) AND\ntheir observations. Windowed max-repeats cycle scan (W=16 · ≤W²/4 u64\ncompares/turn). Ladder: "
            },
            {
              "k": "code",
              "text": "nudge_after"
            },
            {
              "k": "text",
              "text": " (3) occurrences → ONE Reflexion-style\ncorrective message in the transcript (arXiv:2303.11366 · bounded by\n"
            },
            {
              "k": "code",
              "text": "max_reflections"
            },
            {
              "k": "text",
              "text": ", default 1; an all-error streak shares the budget);\n"
            },
            {
              "k": "code",
              "text": "stall_after"
            },
            {
              "k": "text",
              "text": " (5) → stop as "
            },
            {
              "k": "code",
              "text": "NIKA-467 Stalled {period, repeats}"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Because observations are part of the signature, "
            },
            {
              "k": "strong",
              "text": "legitimate polling (same\ncall, changing result) never trips it"
            },
            {
              "k": "text",
              "text": " — only byte-identical\naction+outcome cycles do. Stalls are terminal verdicts ("
            },
            {
              "k": "code",
              "text": "is_transient() = false"
            },
            {
              "k": "text",
              "text": "): a blind rerun replays the proof."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "2 · Per-turn tool routing (`router.rs`)",
          "id": "2--per-turn-tool-routing-routerrs"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Below 24 whitelisted definitions: passthrough (stable prompts win —\nprovider prompt-caches stay warm). At ≥24: rank definitions against the\nlive context (prompt + last assistant text + last observations) with\n"
            },
            {
              "k": "strong",
              "text": "`nika-bm25`"
            },
            {
              "k": "text",
              "text": " (L2→L1 downward dep — the engine's own BM25S/eager\nsatellite; sovereign, zero LLM calls, bit-reproducible) and offer top-12\n∪ pinned (intrinsics + "
            },
            {
              "k": "code",
              "text": "nika:done"
            },
            {
              "k": "text",
              "text": ") ∪ recently-used (≤2 turns).\n"
            },
            {
              "k": "strong",
              "text": "Fail-open"
            },
            {
              "k": "text",
              "text": ": zero lexical overlap ships the full universe — a blind\nmodel is worse than a crowded one."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "3 · Intrinsics (`intrinsic.rs` · `nika:compose`)",
          "id": "3--intrinsics-intrinsicrs--nikacompose"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "Amendment 2026-06-13 · `nika:compose`, not `agent:compose`."
            },
            {
              "k": "text",
              "text": " The\nfirst cut put this in an "
            },
            {
              "k": "code",
              "text": "agent:"
            },
            {
              "k": "text",
              "text": " tool namespace — but the spec's tool\nnamespace set is CLOSED at "
            },
            {
              "k": "code",
              "text": "{nika:, mcp:}"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "02-verbs"
            },
            {
              "k": "text",
              "text": " §218, enforced in\nthe prose + the JSON schema + "
            },
            {
              "k": "code",
              "text": "validate_tool_ref"
            },
            {
              "k": "text",
              "text": "), so a third namespace\nwas a grammar violation that only \"worked\" through a gap in the agent\nwhitelist's namespace check. The precedent "
            },
            {
              "k": "code",
              "text": "nika:done"
            },
            {
              "k": "text",
              "text": " already shows the\nright shape: a loop-served tool is a "
            },
            {
              "k": "code",
              "text": "nika:"
            },
            {
              "k": "text",
              "text": " builtin, marked loop-only.\nSo "
            },
            {
              "k": "code",
              "text": "compose"
            },
            {
              "k": "text",
              "text": " is now "
            },
            {
              "k": "code",
              "text": "nika:compose"
            },
            {
              "k": "text",
              "text": " — the 23rd canonical builtin\n(Introspection, the static sibling of "
            },
            {
              "k": "code",
              "text": "inspect"
            },
            {
              "k": "text",
              "text": "), catalogued in\n"
            },
            {
              "k": "code",
              "text": "nika-catalog"
            },
            {
              "k": "text",
              "text": ", rejected standalone in "
            },
            {
              "k": "code",
              "text": "nika-builtin"
            },
            {
              "k": "text",
              "text": " (NIKA-BUILTIN-\nCOMPOSE-001), documented in the public spec. The whitelist gap is\nclosed (the parser now enforces the closed set). "
            },
            {
              "k": "code",
              "text": "ToolSource"
            },
            {
              "k": "text",
              "text": " collapses\nto "
            },
            {
              "k": "code",
              "text": "{Builtin, Mcp, Other}"
            },
            {
              "k": "text",
              "text": " (the spec's set + a catch-all); the "
            },
            {
              "k": "code",
              "text": "Skill"
            },
            {
              "k": "text",
              "text": "/\n"
            },
            {
              "k": "code",
              "text": "Intrinsic"
            },
            {
              "k": "text",
              "text": " variants — which anticipated namespaces the spec doesn't\nhave — are removed, re-added when those namespaces actually land."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika:compose"
            },
            {
              "k": "text",
              "text": " is a loop-served "
            },
            {
              "k": "code",
              "text": "nika:"
            },
            {
              "k": "text",
              "text": " builtin (like "
            },
            {
              "k": "code",
              "text": "nika:done"
            },
            {
              "k": "text",
              "text": "):\nsynthesized by the loop (any upstream def of the same name is dropped —\npoison-shadow proof), whitelist-gated, NEVER dispatched to the executor\nseam. It takes a full workflow YAML draft and returns the complete\n"
            },
            {
              "k": "code",
              "text": "nika check"
            },
            {
              "k": "text",
              "text": " verdict as JSON — conformance violations with prescriptive\ncodes, secret flows, permits escapes, and the AARA cost/termination\n"
            },
            {
              "k": "strong",
              "text": "certificate"
            },
            {
              "k": "text",
              "text": " (ADR-092 · via "
            },
            {
              "k": "code",
              "text": "nika-schema"
            },
            {
              "k": "text",
              "text": ", L2→L0). The draft never\nexecutes here: composition yields an artifact + its certificate; running\nit stays a separate, gated decision. Drafts are size-capped (256 KiB)\nahead of the parser."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "4 · Telemetry (`observe.rs` + 5 `nika-event` kinds)",
          "id": "4--telemetry-observers--5-nika-event-kinds"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "An injected "
            },
            {
              "k": "code",
              "text": "AgentObserver"
            },
            {
              "k": "text",
              "text": " seam (4th seam · optional · "
            },
            {
              "k": "code",
              "text": "Arc<dyn>"
            },
            {
              "k": "text",
              "text": " — a\ngeneric would infect every embedder signature for a non-data-path\nconcern). Every decision is a typed "
            },
            {
              "k": "code",
              "text": "AgentEvent"
            },
            {
              "k": "text",
              "text": ": "
            },
            {
              "k": "code",
              "text": "RunStarted · TurnStarted · ToolsSelected{offered, universe, by_source} · ToolCompleted · ComposeChecked · Nudged · Stalled{period, repeats} · BudgetCheckpoint · Finished"
            },
            {
              "k": "text",
              "text": ". The L3 runtime maps these 1:1 onto the new\n"
            },
            {
              "k": "code",
              "text": "nika-event"
            },
            {
              "k": "text",
              "text": " kinds ("
            },
            {
              "k": "code",
              "text": "agent_tools_selected · agent_nudge · agent_stalled · agent_compose_checked · agent_budget_checkpoint"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "EventClass::Agent"
            },
            {
              "k": "text",
              "text": ") —\nINV-024 holds: the runtime adapter is the ONE emission site."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "The `skill:` forward direction (NOT a v0.1 namespace)",
          "id": "the-skill-forward-direction-not-a-v01-namespace"
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "Amended 2026-06-13."
            },
            {
              "k": "text",
              "text": " "
            },
            {
              "k": "code",
              "text": "ToolSource"
            },
            {
              "k": "text",
              "text": " classifies ONLY the spec's closed\nv1 set ("
            },
            {
              "k": "code",
              "text": "nika:"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "mcp:"
            },
            {
              "k": "text",
              "text": ") + a catch-all. Stored-workflow skills\n(Voyager-style · arXiv:2305.16291; AWM · arXiv:2409.07429) are a real\nFUTURE direction, but "
            },
            {
              "k": "code",
              "text": "skill:"
            },
            {
              "k": "text",
              "text": " is not a v0.1 tool namespace (the spec's\nset is closed; the parser rejects it). When "
            },
            {
              "k": "code",
              "text": "skill:"
            },
            {
              "k": "text",
              "text": " lands — a future\nadditive MINOR per "
            },
            {
              "k": "code",
              "text": "02-verbs"
            },
            {
              "k": "text",
              "text": " §218 — its "
            },
            {
              "k": "code",
              "text": "ToolSource"
            },
            {
              "k": "text",
              "text": " variant + the\nparser acceptance + the served definitions land together. The earlier\n\"routed and counted today\" framing was the same ahead-of-contract\nmistake "
            },
            {
              "k": "code",
              "text": "agent:compose"
            },
            {
              "k": "text",
              "text": " was, now corrected."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives rejected",
          "id": "alternatives-rejected"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Tree search (LATS, arXiv:2310.04406)"
              },
              {
                "k": "text",
                "text": " — multiplies provider spend\nagainst the budget-first posture; revisit post-v0.81 behind the same\nobserver seam."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Result caching for repeated identical calls"
              },
              {
                "k": "text",
                "text": " — breaks legitimate\npolling semantics (a cached poll lies); the observation-aware stall\nguard covers the pathological case without faking results."
              }
            ],
            [
              {
                "k": "strong",
                "text": "LLM-based reflection/self-evaluation"
              },
              {
                "k": "text",
                "text": " — costs a provider call to\ndecide whether to spend provider calls; the deterministic detector is\nfree and evidence-carrying."
              }
            ],
            [
              {
                "k": "strong",
                "text": "New YAML fields"
              },
              {
                "k": "text",
                "text": " — the spec is a public contract with its own\n4-surface cascade; everything here has sane engine defaults and zero\nauthor-facing surface."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Verification",
          "id": "verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "tests/research_conformance.rs"
            },
            {
              "k": "text",
              "text": " — one executable property per claim,\nthrough the REAL loop with mock seams: the nudge lands in the transcript\nexactly once and the loop recovers · identical cycles stall as NIKA-467\nwith evidence and the queued 6th turn is never requested · polling with\nchanging observations never trips the guard · routing measurably narrows\nthe request's tool list (telemetry mirrors the request) · zero-overlap\nfails open · the compose check-repair loop converges in 2 rounds with the\ncertificate riding the verdict and ZERO executor dispatches · a poisoned\nupstream "
            },
            {
              "k": "code",
              "text": "agent:compose"
            },
            {
              "k": "text",
              "text": " def never shadows the loop-owned one · the\nobserver sequence is bracketed (RunStarted … Finished) with per-turn\nrouting + budget events. Unit property tests pin the detector (injected\np-cycles detected; unique streams never fire) and the canonical hash\n(key-order invariance · observation sensitivity)."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-097",
      "file": "adr-097-parallel-intra-turn-dispatch.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Verification",
          "anchor": "verification"
        },
        {
          "title": "Alternatives rejected",
          "anchor": "alternatives-rejected"
        }
      ],
      "words": 479,
      "sha256": "9e1c37f5fa061eaf3c58871d4c443aedb07ec3e55da25bd6c2961ad3da600757",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-097 — Parallel intra-turn tool dispatch",
          "id": "adr-097--parallel-intra-turn-tool-dispatch"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Status"
              },
              {
                "k": "text",
                "text": ": Accepted (2026-06-12)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Layer"
              },
              {
                "k": "text",
                "text": ": L2 ("
              },
              {
                "k": "code",
                "text": "nika-verb-agent"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Relates"
              },
              {
                "k": "text",
                "text": ": ADR-096 (the intelligence layer this rides in); amends the\nspec §5 « sequential dispatch » fence the same way ADR-096 amended the\n« no reflection » fence — engine-internal, zero YAML surface."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "When a model batches several tool calls into ONE turn, those calls are\nindependent by construction — the model had no observations between them\nto condition on. Dispatching them sequentially serializes I/O latency\nfor no semantic gain; LLMCompiler (Kim · Moon · Tabrizi et al. 2023,\narXiv:2312.04511) measures the latency win of executing such calls in\nparallel, and ReWOO (Xu · Peng · Lei et al. 2023, arXiv:2305.18323)\nmakes the broader case against interleaving halts where no dependency\nexists."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The constraint that matters more than speed: Nika's event stream and\ntranscript are DETERMINISTIC contracts (replay-stable, byte-comparable\nacross runs). Naive "
            },
            {
              "k": "code",
              "text": "join_all"
            },
            {
              "k": "text",
              "text": " + completion-order folding would leak\nscheduling nondeterminism into both."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "run_batch"
            },
            {
              "k": "text",
              "text": " resolves one turn's (already whole-batch whitelist-validated)\ntool calls in TWO phases:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Concurrent resolve"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "futures_util::stream::iter(...) .buffered(max_parallel_tools)"
              },
              {
                "k": "text",
                "text": ": up to N calls in flight; "
              },
              {
                "k": "code",
                "text": "buffered"
              },
              {
                "k": "text",
                "text": "\nyields in INPUT order. The phase is pure with respect to loop state:\nno observer calls, no router mutation (two concurrent "
              },
              {
                "k": "code",
                "text": "agent:compose"
              },
              {
                "k": "text",
                "text": "\nchecks must not interleave the event stream)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Sequential fold"
              },
              {
                "k": "text",
                "text": " (request order): "
              },
              {
                "k": "code",
                "text": "ComposeChecked"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "ToolCompleted"
              },
              {
                "k": "text",
                "text": "\ntelemetry, the router's recency ledger, the guard signature parts,\nthe result blocks."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Consequences, all pinned by tests:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "the transcript feeds results back in REQUEST order — byte-identical to\nsequential dispatch;"
              }
            ],
            [
              {
                "k": "text",
                "text": "the guard signature is computed over the same (calls, results) in the\nsame order — stall detection is unaffected;"
              }
            ],
            [
              {
                "k": "text",
                "text": "the observer event sequence is identical to sequential;"
              }
            ],
            [
              {
                "k": "code",
                "text": "max_parallel_tools: 1"
              },
              {
                "k": "text",
                "text": " (engine-internal "
              },
              {
                "k": "code",
                "text": "AgentConfig"
              },
              {
                "k": "text",
                "text": ") restores\nstrictly sequential dispatch exactly;"
              }
            ],
            [
              {
                "k": "text",
                "text": "CANCEL SAFETY: dropping the batch future drops the buffered stream —\nin-flight calls cancel per their seam contracts (a compose\n"
              },
              {
                "k": "code",
                "text": "spawn_blocking"
              },
              {
                "k": "text",
                "text": " completes detached, result discarded)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Verification",
          "id": "verification"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "tests/research_conformance.rs::one_turns_batched_calls_run_concurrently_ and_feed_back_in_request_order"
            },
            {
              "k": "text",
              "text": " — a rendezvous executor in which EVERY\ncall waits until 2 calls are in flight: sequential dispatch deadlocks\nthere (a 5s timeout turns the hang into a loud failure); the pass proves\ntrue intra-turn concurrency, and the same test asserts the fed-back\nblocks AND the "
            },
            {
              "k": "code",
              "text": "ToolCompleted"
            },
            {
              "k": "text",
              "text": " events arrive in request order. The\npre-existing "
            },
            {
              "k": "code",
              "text": "multiple_tools_in_one_turn_all_dispatch_in_order"
            },
            {
              "k": "text",
              "text": " keeps\nthe mock-determinism contract pinned."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives rejected",
          "id": "alternatives-rejected"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "`join_all` + completion-order fold"
              },
              {
                "k": "text",
                "text": " — leaks scheduler\nnondeterminism into the transcript and the event stream; breaks\nreplay determinism for zero additional win over ordered "
              },
              {
                "k": "code",
                "text": "buffered"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Model-declared dependency graphs inside a turn"
              },
              {
                "k": "text",
                "text": " (full LLMCompiler\nplanner) — Nika already HAS a dependency planner: the workflow DAG.\nIntra-turn, the batch is independent by construction; asking the\nmodel to re-declare dependencies adds a failure surface the DAG\nalready covers at the right layer."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Cross-turn speculation (ReWOO-style full plan-ahead)"
              },
              {
                "k": "text",
                "text": " — changes\nthe ReAct contract observable by authors; the spec's verb semantics\nstay untouched at v0.1."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-099",
      "file": "adr-099-durable-lite-run-resume.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Non-goals",
          "anchor": "non-goals"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Conformance sketch — 3 fixtures · `tests/runtime/resume/`",
          "anchor": "conformance-sketch--3-fixtures--testsruntimeresume"
        },
        {
          "title": "Evidence / Affected surfaces",
          "anchor": "evidence--affected-surfaces"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 2080,
      "sha256": "756fa69fcbbad3baac35bf157aa4a73a7b4e1a443628e76bc530e8111bd4f5b5",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-099 — Durable-lite run resume · the trace IS the checkpoint",
          "id": "adr-099--durable-lite-run-resume--the-trace-is-the-checkpoint"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Status"
              },
              {
                "k": "text",
                "text": ": Accepted (2026-07-05) — implemented by the engine\n"
              },
              {
                "k": "code",
                "text": "feat/resume-impl"
              },
              {
                "k": "text",
                "text": " arc: "
              },
              {
                "k": "code",
                "text": "nika-runtime"
              },
              {
                "k": "text",
                "text": " resume keys (JCS + blake3) +\nthe "
              },
              {
                "k": "code",
                "text": "--resume"
              },
              {
                "k": "text",
                "text": " skip fold + the pause rider · "
              },
              {
                "k": "code",
                "text": "nika-cli run --resume"
              },
              {
                "k": "text",
                "text": "\n/ "
              },
              {
                "k": "code",
                "text": "--from"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "--answer"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "EventKind::{TaskCacheHit, WorkflowPaused}"
              },
              {
                "k": "text",
                "text": "\n· exit code "
              },
              {
                "k": "code",
                "text": "4"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": ") · the 3 conformance fixtures as engine\ne2e tests ("
              },
              {
                "k": "code",
                "text": "crates/nika-cli/tests/resume_e2e.rs"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Surface"
              },
              {
                "k": "text",
                "text": ": CLI + trace/event vocabulary ONLY. Zero envelope change ·\nzero new YAML · the 4-verbs and "
              },
              {
                "k": "code",
                "text": "nika: v1"
              },
              {
                "k": "text",
                "text": " locks are untouched."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Home"
              },
              {
                "k": "text",
                "text": ": the CANONICAL prose lives in the "
              },
              {
                "k": "code",
                "text": "nika-spec"
              },
              {
                "k": "text",
                "text": " repo's "
              },
              {
                "k": "code",
                "text": "adr/"
              },
              {
                "k": "text",
                "text": "\n(language-surface decisions — the relative spec links below resolve\nTHERE); this copy registers the number in the shared Nika ADR series\n(the engine "
              },
              {
                "k": "code",
                "text": "docs/adr/"
              },
              {
                "k": "text",
                "text": " registry is the series' index)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Durability is the largest capability gap the spec names about itself.\n"
            },
            {
              "k": "link",
              "text": "08-out-of-scope",
              "href": "../spec/08-out-of-scope.md"
            },
            {
              "k": "text",
              "text": " holds four interlocking\ndeferrals: §Persistence defers workflow checkpointing/resumption (with a\n"
            },
            {
              "k": "code",
              "text": "checkpoint:"
            },
            {
              "k": "text",
              "text": "-block sketch), §Idempotency defers step dedup \"together\nwith the durable-execution waypoint\", H1 states plainly that "
            },
            {
              "k": "strong",
              "text": "crash =\nre-run from the top"
            },
            {
              "k": "text",
              "text": ", and H5 admits the human gate's pause-state is\n"
            },
            {
              "k": "strong",
              "text": "live"
            },
            {
              "k": "text",
              "text": " (\"the run keeps a process while blocked · durable pauses arrive\nwith H1\"). Across the 2026 workflow-runner landscape, crash-resume and\nthe durable human gate are the two most-asked-for capabilities this\nclass of tool gets measured on — Nextflow's "
            },
            {
              "k": "code",
              "text": "-resume"
            },
            {
              "k": "text",
              "text": " (content-addressed\ntask skip) is that ecosystem's single most-loved feature, and it taxes\nauthors with nothing."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Meanwhile the checkpoint artifact already exists. A conformant Runtime\nengine MUST emit the workflow event stream\n("
            },
            {
              "k": "link",
              "text": "07 §Runtime-4",
              "href": "../spec/07-conformance.md#level-2--runtime-conformance"
            },
            {
              "k": "text",
              "text": "):\n"
            },
            {
              "k": "code",
              "text": "task.completed"
            },
            {
              "k": "text",
              "text": " records carry per-task outcome, and the reference\nengine journals every run as an NDJSON trace ("
            },
            {
              "k": "code",
              "text": "crates/nika-event/src/kind.rs"
            },
            {
              "k": "text",
              "text": "\nserializes the kinds snake_case: "
            },
            {
              "k": "code",
              "text": "task_completed"
            },
            {
              "k": "text",
              "text": " · and pre-reserves\n"
            },
            {
              "k": "code",
              "text": "checkpoint_written"
            },
            {
              "k": "text",
              "text": ", \"a durable run state snapshot\"). What is missing\nis not a new runtime — it is a "
            },
            {
              "k": "strong",
              "text": "reader"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The trap to avoid is equally documented: Temporal-class durable\nexecution buys resume by imposing replay-determinism constraints on\nworkflow authors (code must be deterministic · side effects must be\nwrapped · histories version with the code). That tax is the single most\nhated property of the durable-execution class, and this spec's whole\nposture (audit-before-run · no hidden magic · the engine owns the how)\nforbids exporting it to authors."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "`nika run wf.nika.yaml --resume <trace>` re-executes a workflow,\nskipping every task whose identity matches a completed record in the\ngiven trace."
            },
            {
              "k": "text",
              "text": " The trace is the run's own NDJSON journal — no second\nstore, no new artifact, no daemon."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "1 · The skip rule — content-addressed · two hashes · both MUST match",
          "id": "1--the-skip-rule--content-addressed--two-hashes--both-must-match"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A task is skippable iff the trace holds a "
            },
            {
              "k": "code",
              "text": "task.completed"
            },
            {
              "k": "text",
              "text": " record whose"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "task-definition hash"
              },
              {
                "k": "text",
                "text": " — a canonical serialization of the task's\nbehavior-bearing fields (the verb body · "
              },
              {
                "k": "code",
                "text": "with:"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "output:"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "retry:"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "on_error:"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "on_finally:"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "when:"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "for_each:"
              },
              {
                "k": "text",
                "text": ") —\nmatches the task as now written, AND"
              }
            ],
            [
              {
                "k": "strong",
                "text": "resolved-input hash"
              },
              {
                "k": "text",
                "text": " — the values its "
              },
              {
                "k": "code",
                "text": "${{ }}"
              },
              {
                "k": "text",
                "text": " references resolved\nto at execution time (upstream outputs · "
              },
              {
                "k": "code",
                "text": "vars"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "with"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "env"
              },
              {
                "k": "text",
                "text": ") —\nmatches the values the resumed run resolves."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Edit a prompt → that task re-runs. Change a var an early task consumes →\nit re-runs, and the mismatch cascades through resolved-input hashes\nexactly as far as the data actually flows — untouched siblings still\nskip. "
            },
            {
              "k": "code",
              "text": "for_each"
            },
            {
              "k": "text",
              "text": " tasks record per-iteration (the item participates in\nthe input hash), so a partially-completed fan-out resumes at the\niteration level."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Secrets participate by declared reference identity (name · declared\nsource path), never by value"
            },
            {
              "k": "text",
              "text": " — masking is normative\n("
            },
            {
              "k": "link",
              "text": "01 §envelope MUSTs",
              "href": "../spec/01-envelope.md"
            },
            {
              "k": "text",
              "text": ") and a trace MUST NOT\ncarry secret-derived material, including value hashes (a hash of a\nlow-entropy secret is an oracle). Stated limit: a rotated secret does\nNOT invalidate the cache — force the affected node with "
            },
            {
              "k": "code",
              "text": "--from"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "2 · The skip is VISIBLE — `task.cache_hit` · never silent",
          "id": "2--the-skip-is-visible--taskcache_hit--never-silent"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Each skipped task emits a "
            },
            {
              "k": "strong",
              "text": "`task.cache_hit`"
            },
            {
              "k": "text",
              "text": " event (additive to the\n"
            },
            {
              "k": "link",
              "text": "07 §Runtime-4",
              "href": "../spec/07-conformance.md#level-2--runtime-conformance"
            },
            {
              "k": "text",
              "text": "\nevent set · reference-engine trace kind "
            },
            {
              "k": "code",
              "text": "task_cache_hit"
            },
            {
              "k": "text",
              "text": ") carrying\n"
            },
            {
              "k": "code",
              "text": "task_id"
            },
            {
              "k": "text",
              "text": " + a reference to the matched trace record. The task's output\nis rehydrated from the record; downstream tasks observe "
            },
            {
              "k": "code",
              "text": "status: success"
            },
            {
              "k": "text",
              "text": " and the bound output exactly as if it had run live. The task\nstate enum stays CLOSED ("
            },
            {
              "k": "link",
              "text": "03 §task states",
              "href": "../spec/03-dag.md#task-states"
            },
            {
              "k": "text",
              "text": "\n· no new state): the cache/live distinction rides the event stream and\nthe run report, never the "
            },
            {
              "k": "code",
              "text": "${{ }}"
            },
            {
              "k": "text",
              "text": " surface. Anti-hidden-magic is the\ndesign line — a resumed run's console and trace say "
            },
            {
              "k": "code",
              "text": "cache_hit"
            },
            {
              "k": "text",
              "text": " per\nskipped task, loudly."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "3 · `--from <task_id>` — the manual override",
          "id": "3----from-task_id--the-manual-override"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Forces re-execution from a node: the named task and its transitive\ndownstream re-run even on hash match (tasks upstream of it may still\ncache-hit). An unknown task id is refused pre-run (the same contract as\n"
            },
            {
              "k": "code",
              "text": "nika run --var"
            },
            {
              "k": "text",
              "text": " unknown-key refusal). This is the lever for \"the world\nchanged in a way the hashes cannot see\" (rotated secret · external\nstate · an "
            },
            {
              "k": "code",
              "text": "infer:"
            },
            {
              "k": "text",
              "text": " output you want re-rolled)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "4 · Non-deterministic tasks replay their recorded output",
          "id": "4--non-deterministic-tasks-replay-their-recorded-output"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "An "
            },
            {
              "k": "code",
              "text": "infer:"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "agent:"
            },
            {
              "k": "text",
              "text": " task that hash-matches replays the recorded output\n— that is the point (crash-resume without re-spending tokens).\nDeterminism is never the author's problem: there are no replay rules,\nno \"workflow versioning\", no side-effect wrappers. A task that does not\nhash-match simply re-runs live, side effects included (see Non-goals)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Rider — the durable human gate (`nika:prompt` pause)",
          "id": "rider--the-durable-human-gate-nikaprompt-pause"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Today's normative non-interactive contract\n("
            },
            {
              "k": "link",
              "text": "stdlib §nika:prompt",
              "href": "../stdlib/builtins-v0.1.md"
            },
            {
              "k": "text",
              "text": "): with "
            },
            {
              "k": "code",
              "text": "default:"
            },
            {
              "k": "text",
              "text": "\nthe engine MUST answer with it; without, it MUST fail\n"
            },
            {
              "k": "code",
              "text": "NIKA-BUILTIN-PROMPT-001"
            },
            {
              "k": "text",
              "text": " — never hang. The rider occupies "
            },
            {
              "k": "strong",
              "text": "exactly\nthat failure branch"
            },
            {
              "k": "text",
              "text": ", changing nothing else:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Under a non-interactive surface ("
              },
              {
                "k": "code",
                "text": "--json"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "nika serve"
              },
              {
                "k": "text",
                "text": "), a blocking\n"
              },
              {
                "k": "code",
                "text": "nika:prompt"
              },
              {
                "k": "text",
                "text": " with no usable "
              },
              {
                "k": "code",
                "text": "default:"
              },
              {
                "k": "text",
                "text": " journals a "
              },
              {
                "k": "strong",
                "text": "`workflow.paused`"
              },
              {
                "k": "text",
                "text": "\nevent (trace kind "
              },
              {
                "k": "code",
                "text": "workflow_paused"
              },
              {
                "k": "text",
                "text": ") carrying the prompt payload\n("
              },
              {
                "k": "code",
                "text": "task_id"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "mode"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "message"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "choices"
              },
              {
                "k": "text",
                "text": " · secret-masked like every\njournal surface) and "
              },
              {
                "k": "strong",
                "text": "exits cleanly"
              },
              {
                "k": "text",
                "text": " with run state "
              },
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": " instead\nof failing."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`--resume <trace>` re-arms the prompt"
              },
              {
                "k": "text",
                "text": ": completed upstream work\ncache-hits, the run continues AT the prompt — interactively it asks;\nresumed non-interactively with an answer supplied (CLI arg · serve\nwebhook payload — delivery ergonomics are the engine's) it binds it;\nresumed non-interactively with no answer it pauses again\n(idempotent · a paused trace can be resumed any number of times)."
              }
            ],
            [
              {
                "k": "text",
                "text": "This resolves H5's noted limitation (\"pause-state is live · durable\npauses arrive with H1\") with "
              },
              {
                "k": "strong",
                "text": "zero new syntax"
              },
              {
                "k": "text",
                "text": " — \"ONE construct · a\ntool under "
              },
              {
                "k": "code",
                "text": "invoke:"
              },
              {
                "k": "text",
                "text": " · forever\" stays exactly as locked."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The one spec-surface delta this rider needs: the run-report/event\nvocabulary gains "
            },
            {
              "k": "strong",
              "text": "`paused`"
            },
            {
              "k": "text",
              "text": " as an additive workflow-state value\n("
            },
            {
              "k": "link",
              "text": "05 §workflow-level semantics",
              "href": "../spec/05-errors.md#workflow-level-error-semantics"
            },
            {
              "k": "text",
              "text": ")\nplus the two events above. Like "
            },
            {
              "k": "code",
              "text": "pending"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "running"
            },
            {
              "k": "text",
              "text": ", "
            },
            {
              "k": "code",
              "text": "paused"
            },
            {
              "k": "text",
              "text": " exists in\nrun reports and events, never inside "
            },
            {
              "k": "code",
              "text": "${{ }}"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Non-goals",
          "id": "non-goals"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "No author-facing determinism constraints"
              },
              {
                "k": "text",
                "text": " (Temporal-style replay\nrules). Side-effectful tasks re-run whenever their hashes mismatch —\ndurability is the engine's problem, never the author's. An author who\nneeds a side effect to be exactly-once under resume marks the node\nwith "
              },
              {
                "k": "code",
                "text": "--from"
              },
              {
                "k": "text",
                "text": " discipline or makes the tool idempotent via its own\nargs (today's H1 guidance · unchanged)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Idempotency keys stay deferred"
              },
              {
                "k": "text",
                "text": " — the NEXT deferral in this ladder\n("
              },
              {
                "k": "link",
                "text": "08 §idempotency",
                "href": "../spec/08-out-of-scope.md#idempotency-keys--step-deduplication"
              },
              {
                "k": "text",
                "text": ").\nResume shrinks the re-run window (completed work never re-fires) but\na crash mid-side-effect can still double-fire on re-run; at-least-once\ndedup remains the full durable-execution waypoint's job."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No envelope/syntax change."
              },
              {
                "k": "text",
                "text": " The "
              },
              {
                "k": "code",
                "text": "checkpoint:"
              },
              {
                "k": "text",
                "text": "-block sketch in\n"
              },
              {
                "k": "link",
                "text": "08 §Persistence",
                "href": "../spec/08-out-of-scope.md#workflow-checkpointing--resumption"
              },
              {
                "k": "text",
                "text": "\nREMAINS deferred exactly as written: a workflow never declares\npersistence. This ADR is CLI flags + trace/event vocabulary."
              }
            ],
            [
              {
                "k": "strong",
                "text": "No daemon."
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "--resume"
              },
              {
                "k": "text",
                "text": " is a reader of a file. No supervisor · no\nrun-store service · no background process · no wake-on-event (durable\ntimers ride a later design)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Not general memoization."
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "task.cache_hit"
              },
              {
                "k": "text",
                "text": " fires only under\n"
              },
              {
                "k": "code",
                "text": "--resume"
              },
              {
                "k": "text",
                "text": "; a fresh "
              },
              {
                "k": "code",
                "text": "nika run"
              },
              {
                "k": "text",
                "text": " executes every task. The H17\n"
              },
              {
                "k": "code",
                "text": "cache_key:"
              },
              {
                "k": "text",
                "text": " field stays deferred on its own terms."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Crash-resume · re-run-from-a-node · no-token-re-spend land behind ONE\nflag, riding an artifact every Runtime-conformant engine already emits."
              }
            ],
            [
              {
                "k": "text",
                "text": "The durable human gate closes the biggest practitioner ask with zero\nnew syntax and zero change to answered-prompt behavior (the pause path\noccupies only what is a hard error today — strictly additive)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Sovereignty intact: the trace is a local NDJSON file · grep-able ·\nportable · no vendor run store, no cloud dependency."
              }
            ],
            [
              {
                "k": "text",
                "text": "The determinism tax is structurally refused, not just avoided: there\nis no replay engine to feed, so no replay rules can ever leak to\nauthors."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The "
              },
              {
                "k": "code",
                "text": "task.completed"
              },
              {
                "k": "text",
                "text": " record fields resume reads (definition hash ·\ninput hash · output) become a compatibility surface: they MUST evolve\nadditively, and an engine facing a trace it cannot read MUST say so\nand re-run fully — never guess a partial skip."
              }
            ],
            [
              {
                "k": "text",
                "text": "Secrets-by-name hashing means a rotated secret silently cache-hits;\nthe limit is documented and "
              },
              {
                "k": "code",
                "text": "--from"
              },
              {
                "k": "text",
                "text": " is the override, but it is a\nreal sharp edge to teach."
              }
            ],
            [
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": " is one more workflow-state value every run-report consumer\nmust render."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Resume conformance is behavioral-tier — it lands with the runtime\nfixtures, post-announce\n("
              },
              {
                "k": "link",
                "text": "07 §suite status",
                "href": "../spec/07-conformance.md#suite-status--v01-honest"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "text",
                "text": "Another engine MAY keep a different trace file layout: the\nconformance contract binds the EVENTS and the skip/pause semantics,\nnot the bytes of the journal."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Conformance sketch — 3 fixtures · `tests/runtime/resume/`",
          "id": "conformance-sketch--3-fixtures--testsruntimeresume"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Behavioral-tier shape per\n"
            },
            {
              "k": "link",
              "text": "conformance/tests/runtime/README.md",
              "href": "../conformance/tests/runtime/README.md"
            },
            {
              "k": "text",
              "text": "\n("
            },
            {
              "k": "code",
              "text": "input.nika.yaml"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "run.json"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "expected-run.json"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "mock/echo"
            },
            {
              "k": "text",
              "text": ") ·\nresume fixtures add the two-phase invocation (run → interrupt → resume)\nto the runner protocol:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "(a) `001-kill-midrun-resume-completes-remainder`"
              },
              {
                "k": "text",
                "text": " — first\ninvocation is interrupted after task "
              },
              {
                "k": "code",
                "text": "a"
              },
              {
                "k": "text",
                "text": " completes; the resumed run\nMUST cache-hit "
              },
              {
                "k": "code",
                "text": "a"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "events_include: [\"task.cache_hit:a\"]"
              },
              {
                "k": "text",
                "text": "), execute\nonly the remainder live, and end "
              },
              {
                "k": "code",
                "text": "workflow_state: success"
              },
              {
                "k": "text",
                "text": " with\noutputs identical to an uninterrupted run."
              }
            ],
            [
              {
                "k": "strong",
                "text": "(b) `002-input-change-rehashes-and-reruns`"
              },
              {
                "k": "text",
                "text": " — same trace, one\n"
              },
              {
                "k": "code",
                "text": "vars"
              },
              {
                "k": "text",
                "text": " value changed in "
              },
              {
                "k": "code",
                "text": "run.json"
              },
              {
                "k": "text",
                "text": ": the consuming task's\nresolved-input hash mismatches → it re-runs (no "
              },
              {
                "k": "code",
                "text": "task.cache_hit"
              },
              {
                "k": "text",
                "text": " for\nit) · an untouched sibling branch still cache-hits."
              }
            ],
            [
              {
                "k": "strong",
                "text": "(c) `003-paused-prompt-rearms`"
              },
              {
                "k": "text",
                "text": " — a non-interactive run hits a\ndefault-less "
              },
              {
                "k": "code",
                "text": "nika:prompt"
              },
              {
                "k": "text",
                "text": " → exits "
              },
              {
                "k": "code",
                "text": "workflow_state: paused"
              },
              {
                "k": "text",
                "text": ", trace\ncarries "
              },
              {
                "k": "code",
                "text": "workflow.paused"
              },
              {
                "k": "text",
                "text": " with the prompt payload; resumed with an\nanswer supplied → upstream cache-hits, the prompt binds, the run\ncompletes; resumed without one → it pauses again (idempotent)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected surfaces",
          "id": "evidence--affected-surfaces"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "link",
                "text": "`spec/08-out-of-scope.md`",
                "href": "../spec/08-out-of-scope.md"
              },
              {
                "k": "text",
                "text": " — §Persistence\ncheckpointing (the deferral this ADR lifts at the durable-lite tier) ·\n§Idempotency (the next deferral) · H1 · H5 · H17 (amended this arc)."
              }
            ],
            [
              {
                "k": "link",
                "text": "`stdlib/builtins-v0.1.md`",
                "href": "../stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " §"
              },
              {
                "k": "code",
                "text": "nika:prompt"
              },
              {
                "k": "text",
                "text": "\n— the "
              },
              {
                "k": "code",
                "text": "NIKA-BUILTIN-PROMPT-001"
              },
              {
                "k": "text",
                "text": " branch the rider occupies."
              }
            ],
            [
              {
                "k": "link",
                "text": "`spec/07-conformance.md`",
                "href": "../spec/07-conformance.md"
              },
              {
                "k": "text",
                "text": " §Runtime-4 —\nthe event vocabulary "
              },
              {
                "k": "code",
                "text": "task.cache_hit"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "workflow.paused"
              },
              {
                "k": "text",
                "text": " extend\nadditively."
              }
            ],
            [
              {
                "k": "text",
                "text": "Reference engine "
              },
              {
                "k": "code",
                "text": "crates/nika-event/src/kind.rs"
              },
              {
                "k": "text",
                "text": " — snake_case trace\nkinds ("
              },
              {
                "k": "code",
                "text": "task_completed"
              },
              {
                "k": "text",
                "text": ") + the pre-reserved "
              },
              {
                "k": "code",
                "text": "checkpoint_written"
              },
              {
                "k": "text",
                "text": "\n(\"durable run state snapshot\"): the seam was planted before the design."
              }
            ],
            [
              {
                "k": "text",
                "text": "External poles · Nextflow "
              },
              {
                "k": "code",
                "text": "-resume"
              },
              {
                "k": "text",
                "text": " (the adopted shape ·\ncontent-addressed task skip · zero author tax) · Temporal workflow\ndeterminism constraints (the rejected shape · the tax this ADR\nstructurally refuses)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — full durable-execution runtime (Temporal-class)",
          "id": "alt-a--full-durable-execution-runtime-temporal-class"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Event-sourced replay: re-execute workflow code against recorded\nhistories, requiring deterministic authorship, wrapped side effects,\nand code-version pinning. Rejected — it exports the runtime's problem\nto every author forever, contradicts the no-hidden-magic spine, and\nbuys nothing this design does not already deliver for a finite DAG\nwhose journal carries every task boundary."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — sqlite checkpoint store",
          "id": "alt-b--sqlite-checkpoint-store"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "A dedicated "
            },
            {
              "k": "code",
              "text": "checkpoints.db"
            },
            {
              "k": "text",
              "text": " beside the trace (the shape the old 08\nsketch reserved). Rejected for v1 — a second stateful artifact to\ncreate, locate, version, and explain, duplicating data the NDJSON trace\nalready journals. One artifact · plain text · portable. An engine MAY\nlater keep an internal index (sqlite or otherwise) as a pure\noptimization — invisible to this contract."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt C — do nothing",
          "id": "alt-c--do-nothing"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected — crash-resume and the durable human gate are the top two\npractitioner asks of this tool class, and H5's live-pause limitation is\nflagged in the spec's own text. Waiting for the full durable-execution\nwaypoint gates the 90% capability on the 10% problem (idempotent\ndelivery) that finite single-run DAGs mostly do not have."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "link",
                "text": "`spec/08-out-of-scope.md`",
                "href": "../spec/08-out-of-scope.md"
              },
              {
                "k": "text",
                "text": " §Persistence ·\n§Idempotency · H1 · H5 · H17"
              }
            ],
            [
              {
                "k": "link",
                "text": "`spec/07-conformance.md`",
                "href": "../spec/07-conformance.md"
              },
              {
                "k": "text",
                "text": " §Runtime\nconformance · §suite status"
              }
            ],
            [
              {
                "k": "link",
                "text": "`spec/03-dag.md`",
                "href": "../spec/03-dag.md"
              },
              {
                "k": "text",
                "text": " §task states (closed enum ·\nunchanged) · "
              },
              {
                "k": "link",
                "text": "`spec/05-errors.md`",
                "href": "../spec/05-errors.md"
              },
              {
                "k": "text",
                "text": "\n§workflow-level semantics ("
              },
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": " additive)"
              }
            ],
            [
              {
                "k": "link",
                "text": "`stdlib/builtins-v0.1.md`",
                "href": "../stdlib/builtins-v0.1.md"
              },
              {
                "k": "text",
                "text": " §"
              },
              {
                "k": "code",
                "text": "nika:prompt"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Registration"
              },
              {
                "k": "text",
                "text": " · DONE (this file · the "
              },
              {
                "k": "code",
                "text": "feat/resume-impl"
              },
              {
                "k": "text",
                "text": " arc) —\nADR-099 is registered in the engine "
              },
              {
                "k": "code",
                "text": "docs/adr/"
              },
              {
                "k": "text",
                "text": " index (the series\nregistry)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Status flip"
              },
              {
                "k": "text",
                "text": " · DONE · "
              },
              {
                "k": "code",
                "text": "proposed → accepted"
              },
              {
                "k": "text",
                "text": " with the engine\nimplementation; the behavioral fixtures of the Conformance sketch\nship as "
              },
              {
                "k": "code",
                "text": "crates/nika-cli/tests/resume_e2e.rs"
              },
              {
                "k": "text",
                "text": ". (The spec-repo copy's\nfrontmatter flip is a spec-repo commit — tracked there.)"
              }
            ],
            [
              {
                "k": "strong",
                "text": "Revisit trigger"
              },
              {
                "k": "text",
                "text": " · if implementation shows secrets-by-name hashing\nis too blunt (rotated-secret staleness bites in practice), a keyed\ncommitment (HMAC under an engine-local key · never a bare hash)\nre-opens HERE first — the trace-carries-no-secret-material invariant\nis not negotiable."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-100",
      "file": "adr-100-trace-retention.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Conformance sketch (ships with the implementation)",
          "anchor": "conformance-sketch-ships-with-the-implementation"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 849,
      "sha256": "75a205e9a8f447e9b9813ecdbaf8cb52920a49fcea564f8a5c2d7991550dff58",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-100 — Trace retention · bounded by default, never silently, never a pending gate",
          "id": "adr-100--trace-retention--bounded-by-default-never-silently-never-a-pending-gate"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "Status"
              },
              {
                "k": "text",
                "text": ": Accepted (2026-07-05) — implemented by the engine\n"
              },
              {
                "k": "code",
                "text": "feat/trace-retention"
              },
              {
                "k": "text",
                "text": " arc: the D1 policy (keep-10 per workflow ·\n30d age cap · 256MB budget · the paused + newest-per-workflow\nabsolute exemptions) · D2 opportunistic GC at "
              },
              {
                "k": "code",
                "text": "nika run"
              },
              {
                "k": "text",
                "text": " start\n(fail-open · exactly ONE stderr line · "
              },
              {
                "k": "code",
                "text": "--no-gc"
              },
              {
                "k": "text",
                "text": ") · D3\n"
              },
              {
                "k": "code",
                "text": "nika trace ls"
              },
              {
                "k": "text",
                "text": " (age · size · workflow · state · the ★\nresume-candidate marker) + "
              },
              {
                "k": "code",
                "text": "nika trace rm"
              },
              {
                "k": "text",
                "text": " (paused refuses without\n"
              },
              {
                "k": "code",
                "text": "--force"
              },
              {
                "k": "text",
                "text": " and names the task) · D4 knobs on the "
              },
              {
                "k": "code",
                "text": "NIKA_TRACE_*"
              },
              {
                "k": "text",
                "text": " env\nfamily, reported by "
              },
              {
                "k": "code",
                "text": "nika doctor"
              },
              {
                "k": "text",
                "text": " · the 4 conformance fixtures as\nengine tests ("
              },
              {
                "k": "code",
                "text": "crates/nika-cli/src/verbs/trace/{retention,manage}.rs"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Surface"
              },
              {
                "k": "text",
                "text": ": CLI + runtime housekeeping ONLY. Zero envelope change ·\nzero new YAML."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Home"
              },
              {
                "k": "text",
                "text": ": the CANONICAL prose lives in the "
              },
              {
                "k": "code",
                "text": "nika-spec"
              },
              {
                "k": "text",
                "text": " repo's "
              },
              {
                "k": "code",
                "text": "adr/"
              },
              {
                "k": "text",
                "text": "\n(language-surface decisions); this copy registers the number in the\nshared Nika ADR series (the engine "
              },
              {
                "k": "code",
                "text": "docs/adr/"
              },
              {
                "k": "text",
                "text": " registry is the\nseries' index)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": ".nika/traces/"
            },
            {
              "k": "text",
              "text": " grows without bound: every "
            },
            {
              "k": "code",
              "text": "nika run"
            },
            {
              "k": "text",
              "text": " appends one NDJSON\njournal and nothing ever removes one (verified 2026-07-05 · zero\nretention/GC language anywhere in spec v1). This was tolerable when a\ntrace was a debugging artifact. Since ADR-099 a trace is "
            },
            {
              "k": "strong",
              "text": "three things\nat once"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "text",
                "text": "an observability record ("
              },
              {
                "k": "code",
                "text": "nika trace show|replay"
              },
              {
                "k": "text",
                "text": " and the round-7\ncomprehension surfaces),"
              }
            ],
            [
              {
                "k": "strong",
                "text": "the checkpoint"
              },
              {
                "k": "text",
                "text": " ("
              },
              {
                "k": "code",
                "text": "--resume"
              },
              {
                "k": "text",
                "text": " folds it · deleting a trace deletes\nresumability),"
              }
            ],
            [
              {
                "k": "strong",
                "text": "a pending human gate"
              },
              {
                "k": "text",
                "text": " when its last state is "
              },
              {
                "k": "code",
                "text": "workflow_paused"
              },
              {
                "k": "text",
                "text": " —\ndeleting THAT trace silently destroys an unanswered approval."
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Unbounded growth is a real cost (traces carry full task outputs since\nADR-099 rehydration — multi-MB per run on payload-heavy workflows), but\nnaive GC would violate the resume and pause contracts. The legacy engine\nhad trace auto-GC; v1 must get the bounded-but-safe version."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D1 · Retention policy (defaults · engine-enforced)",
          "id": "d1--retention-policy-defaults--engine-enforced"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per workflow (keyed by workflow name within a project's "
            },
            {
              "k": "code",
              "text": ".nika/traces/"
            },
            {
              "k": "text",
              "text": "):"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Rule · Default · Rationale"
              }
            ],
            [
              {
                "k": "text",
                "text": "keep the last **N** completed-run traces · `10` · the observability window"
              }
            ],
            [
              {
                "k": "text",
                "text": "age cap · `30d` · stale traces exit even under N"
              }
            ],
            [
              {
                "k": "text",
                "text": "project total size budget · `256MB` · the hard stop"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "strong",
              "text": "Absolute exemptions — never collected regardless of any cap:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "a trace whose last workflow state is "
              },
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": " (a pending gate is an\nOBLIGATION, not garbage) — it ages out only once answered or explicitly\nremoved with "
              },
              {
                "k": "code",
                "text": "nika trace rm"
              },
              {
                "k": "text",
                "text": ","
              }
            ],
            [
              {
                "k": "text",
                "text": "the most recent trace of each workflow (the standing resume candidate)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D2 · When GC runs",
          "id": "d2--when-gc-runs"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Opportunistically at "
            },
            {
              "k": "code",
              "text": "nika run"
            },
            {
              "k": "text",
              "text": " start (maintenance rides usage — no\ndaemon, no cron, the Nextflow/cargo school). A collection that removes\nanything prints exactly ONE line to stderr:"
            }
          ]
        },
        {
          "k": "code",
          "lang": "text",
          "text": "trace gc · removed 3 (2 aged · 1 over-budget) · kept 12 · 41MB"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Silent deletion is forbidden (the anti-hidden-magic invariant). "
            },
            {
              "k": "code",
              "text": "--no-gc"
            },
            {
              "k": "text",
              "text": "\nskips collection for that invocation."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D3 · The explicit surface",
          "id": "d3--the-explicit-surface"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "nika trace ls"
              },
              {
                "k": "text",
                "text": " — traces with age · size · workflow · terminal state\n("
              },
              {
                "k": "code",
                "text": "completed"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "code",
                "text": "failed"
              },
              {
                "k": "text",
                "text": "/"
              },
              {
                "k": "strong",
                "text": "`paused`"
              },
              {
                "k": "text",
                "text": ") · the resume-candidate marker."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika trace rm <trace>|--older-than <dur>|--all"
              },
              {
                "k": "text",
                "text": " — explicit removal ·\nremoving a "
              },
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": " trace requires "
              },
              {
                "k": "code",
                "text": "--force"
              },
              {
                "k": "text",
                "text": " and says what it destroys\n("
              },
              {
                "k": "code",
                "text": "this trace carries an unanswered prompt for task <id>"
              },
              {
                "k": "text",
                "text": ")."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "D4 · Configuration",
          "id": "d4--configuration"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The three knobs ride the engine's existing config surface ("
            },
            {
              "k": "code",
              "text": "nika doctor"
            },
            {
              "k": "text",
              "text": "\nreports the active values). Config names and wiring are the\nimplementation arc's to fix; the DEFAULTS above are the spec contract."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "No GC (status quo)"
              },
              {
                "k": "text",
                "text": " — rejected: unbounded disk on every real user ·\nthe docs would have to teach manual cleanup of a dir the tool owns."
              }
            ],
            [
              {
                "k": "strong",
                "text": "A `nika trace gc` verb only (fully manual)"
              },
              {
                "k": "text",
                "text": " — rejected: nobody runs\nmaintenance verbs · bounded-by-default is the 2026 baseline · the\nexplicit surface exists IN ADDITION (D3)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "GC daemon / background thread"
              },
              {
                "k": "text",
                "text": " — rejected: no daemon is a v1\nidentity trait · opportunistic-at-run is sufficient and testable."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Content-addressed dedup of outputs before GC"
              },
              {
                "k": "text",
                "text": " — deferred: real\n(outputs repeat across runs) but it is a storage-format change ·\nretention must not wait for it."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Disk is bounded by default; resume and pause contracts survive GC by\nconstruction (the exemptions are the contract)."
              }
            ],
            [
              {
                "k": "code",
                "text": "--resume"
              },
              {
                "k": "text",
                "text": " against a collected trace keeps its ADR-099 behavior\n(readable error · nothing skips) — retention adds the "
              },
              {
                "k": "code",
                "text": "trace ls"
              },
              {
                "k": "text",
                "text": "\nsurface to SEE what is resumable."
              }
            ],
            [
              {
                "k": "text",
                "text": "Traces stop being an unbounded liability in CI (the size budget is the\ncap that matters there)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Conformance sketch (ships with the implementation)",
          "id": "conformance-sketch-ships-with-the-implementation"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "paused-survives"
              },
              {
                "k": "text",
                "text": " · a "
              },
              {
                "k": "code",
                "text": "workflow_paused"
              },
              {
                "k": "text",
                "text": " trace older than every cap\nsurvives GC · "
              },
              {
                "k": "code",
                "text": "trace ls"
              },
              {
                "k": "text",
                "text": " marks it "
              },
              {
                "k": "code",
                "text": "paused"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "strong",
                "text": "resume-candidate-survives"
              },
              {
                "k": "text",
                "text": " · the newest trace of a workflow\nsurvives even when over-budget alone."
              }
            ],
            [
              {
                "k": "strong",
                "text": "visible-collection"
              },
              {
                "k": "text",
                "text": " · an over-N history collects oldest-first ·\nexactly one stderr line reports it · "
              },
              {
                "k": "code",
                "text": "--no-gc"
              },
              {
                "k": "text",
                "text": " leaves it intact."
              }
            ],
            [
              {
                "k": "strong",
                "text": "forced-removal-speaks"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "trace rm"
              },
              {
                "k": "text",
                "text": " on a paused trace refuses\nwithout "
              },
              {
                "k": "code",
                "text": "--force"
              },
              {
                "k": "text",
                "text": " and names the unanswered task."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Interplay with idempotency keys (the §Persistence deferral) is\nunchanged — retention never re-runs anything by itself."
              }
            ],
            [
              {
                "k": "text",
                "text": "The engine index registration + "
              },
              {
                "k": "code",
                "text": "nika trace ls|rm"
              },
              {
                "k": "text",
                "text": " land with the\nimplementation arc."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-105",
      "file": "adr-105-image-generate-builtin.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        }
      ],
      "words": 1068,
      "sha256": "0e06d41bc3a1eaae684a166a01e648809d004abd50fb1fac672b8780cb9a2ca8",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-105: nika:image_generate — the Media builtin rides a dedicated image plane",
          "id": "adr-105-nikaimage_generate--the-media-builtin-rides-a-dedicated-image-plane"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Workflows increasingly need image ASSETS (OG covers, QR landing heroes, SEO\nbatches) as a step in a larger pipeline, not as a chat artifact. The stdlib\ndeferred the whole media class at the 42→22 consolidation; the spec kept a\n"
            },
            {
              "k": "code",
              "text": "§Media · DEFERRED"
            },
            {
              "k": "text",
              "text": " placeholder. Meanwhile the two providers that matter\nstabilized their image APIs (OpenAI "
            },
            {
              "k": "code",
              "text": "gpt-image-2"
            },
            {
              "k": "text",
              "text": " · Gemini\n"
            },
            {
              "k": "code",
              "text": "gemini-3.1-flash-image"
            },
            {
              "k": "text",
              "text": ", primary-source verified 2026-07-05), and the\nengine already owns every primitive the pipeline needs: the kernel http\nseam, the atomic-write fs seam, the "
            },
            {
              "k": "code",
              "text": "permits.fs"
            },
            {
              "k": "text",
              "text": " runtime boundary, base64 +\nsha256 helpers, and a builtin dispatcher with injected effect seams. Two\ntransport facts force a design decision: the FETCH http client carries a 30s\nidle-read guard that kills 60–120s image renders (the F1 timeout class), and\nimage endpoints are engine-fixed constants — never workflow data."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Ship "
            },
            {
              "k": "strong",
              "text": "one official builtin, `nika:image_generate`"
            },
            {
              "k": "text",
              "text": ", as a "
            },
            {
              "k": "strong",
              "text": "module family\ninside `nika-builtin`"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "src/image/"
            },
            {
              "k": "text",
              "text": " · collapse-default rule — no new crate,\nno 12-gate ceremony; feature-level gates still apply) and graduate the spec's\ndeferred Media class (23 → 24 canonical builtins)."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "strong",
                "text": "The image plane"
              },
              {
                "k": "text",
                "text": " · provider calls ride a DEDICATED dispatcher seam —\n"
              },
              {
                "k": "code",
                "text": "image_http: Option<Arc<H>>"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "with_image_plane(http, keys)"
              },
              {
                "k": "text",
                "text": ", wired at the\ncomposition root with the PROVIDER-plane client (SSRF "
              },
              {
                "k": "code",
                "text": "Disabled"
              },
              {
                "k": "text",
                "text": "\nsanctioned: endpoints are const studio-fixed strings, same reasoning as\n"
              },
              {
                "k": "code",
                "text": "infer:"
              },
              {
                "k": "text",
                "text": " · 600s transport ceiling). Unwired ⇒ real providers fail with the\nprecise "
              },
              {
                "k": "code",
                "text": "-002"
              },
              {
                "k": "text",
                "text": "; the mock provider works without it. "
              },
              {
                "k": "code",
                "text": "permits.net.http"
              },
              {
                "k": "text",
                "text": "\ndoes NOT govern the plane (like "
              },
              {
                "k": "code",
                "text": "infer:"
              },
              {
                "k": "text",
                "text": "); "
              },
              {
                "k": "code",
                "text": "permits.tools"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "permits.fs"
              },
              {
                "k": "text",
                "text": "\nDO."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Keys"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "ImageKeys { openai, gemini }"
              },
              {
                "k": "text",
                "text": " (zeroizing "
              },
              {
                "k": "code",
                "text": "Secret"
              },
              {
                "k": "text",
                "text": "s) resolved\nONLY at the composition root's sanctioned env boundary\n("
              },
              {
                "k": "code",
                "text": "NIKA_OPENAI_API_KEY → OPENAI_API_KEY"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "NIKA_GEMINI_API_KEY → GEMINI_API_KEY"
              },
              {
                "k": "text",
                "text": "). The builtin never reads env, never logs a credential."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Assets, not blobs"
              },
              {
                "k": "text",
                "text": " · V1 requires "
              },
              {
                "k": "code",
                "text": "save"
              },
              {
                "k": "text",
                "text": " (default true) — images land\nunder "
              },
              {
                "k": "code",
                "text": "output_dir:"
              },
              {
                "k": "text",
                "text": " (boundary-gated per FINAL path before any I/O ·\n"
              },
              {
                "k": "code",
                "text": "NIKA-SEC-004"
              },
              {
                "k": "text",
                "text": "), outputs and the "
              },
              {
                "k": "code",
                "text": "manifest_version: 1"
              },
              {
                "k": "text",
                "text": " provenance manifest\ncarry paths + dimensions + sha256. Base64 NEVER rides outputs/logs/traces;\n"
              },
              {
                "k": "code",
                "text": "debug: true"
              },
              {
                "k": "text",
                "text": " echoes a SANITIZED provider response (payloads stripped)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Validation is header-only"
              },
              {
                "k": "text",
                "text": " · magic bytes are the authority (PNG IHDR ·\nJPEG SOF · WebP VP8/VP8L/VP8X) — no pixel decode ⇒ no decompression-bomb\nsurface; dimension/byte bounds refuse absurd payloads ("
              },
              {
                "k": "code",
                "text": "-007"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Deterministic mock"
              },
              {
                "k": "text",
                "text": " · a hand-rolled stored-deflate PNG encoder\n(byte-stable forever · validated by the independent "
              },
              {
                "k": "code",
                "text": "png"
              },
              {
                "k": "text",
                "text": " dev-dep) makes\nthe whole pipeline offline-testable, golden-grade."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Static surface"
              },
              {
                "k": "text",
                "text": " · catalog row (21-arg vocabulary · required\n"
              },
              {
                "k": "code",
                "text": "prompt"
              },
              {
                "k": "text",
                "text": "+"
              },
              {
                "k": "code",
                "text": "output_dir"
              },
              {
                "k": "text",
                "text": ") · model-facing ToolDef (drift-guarded against the\ncatalog) · "
              },
              {
                "k": "code",
                "text": "builtin_shape"
              },
              {
                "k": "text",
                "text": " ladder (V1 reservations "
              },
              {
                "k": "code",
                "text": "mode: edit"
              },
              {
                "k": "text",
                "text": " /\n"
              },
              {
                "k": "code",
                "text": "reference_images"
              },
              {
                "k": "text",
                "text": " / "
              },
              {
                "k": "code",
                "text": "save: false"
              },
              {
                "k": "text",
                "text": " refused loudly · closed enums · ranges ·\nthe transparent×jpeg conflict) · "
              },
              {
                "k": "code",
                "text": "BuiltinEffect::Fs { path_arg: \"output_dir\", writes, recursive }"
              },
              {
                "k": "text",
                "text": " so escape-check AND "
              },
              {
                "k": "code",
                "text": "--infer-permits"
              },
              {
                "k": "text",
                "text": "\nunderstand the recursive write ("
              },
              {
                "k": "code",
                "text": "<dir>/**"
              },
              {
                "k": "text",
                "text": ")."
              }
            ],
            [
              {
                "k": "strong",
                "text": "V1 boundaries stated, not faked"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "mode: edit"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "reference_images:"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "save: false"
              },
              {
                "k": "text",
                "text": ", streaming, Imagen "
              },
              {
                "k": "code",
                "text": ":predict"
              },
              {
                "k": "text",
                "text": " are RESERVED with pointed\nerrors + roadmap notes."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The stdlib counts 24 (catalog · defs · schema enum · spec canon.yaml ·\nconformance fixtures 004/005 · showcase "
              },
              {
                "k": "code",
                "text": "t1-og-images"
              },
              {
                "k": "text",
                "text": " — cascade complete,\npack re-vendored)."
              }
            ],
            [
              {
                "k": "code",
                "text": "n:"
              },
              {
                "k": "text",
                "text": " on gemini = n sequential calls (no verified "
              },
              {
                "k": "code",
                "text": "candidateCount"
              },
              {
                "k": "text",
                "text": " for\nimages) — documented, usage accumulates across calls."
              }
            ],
            [
              {
                "k": "text",
                "text": "The 64 MiB provider-response cap bounds "
              },
              {
                "k": "code",
                "text": "n × png"
              },
              {
                "k": "text",
                "text": " batches — a clean "
              },
              {
                "k": "code",
                "text": "-003"
              },
              {
                "k": "text",
                "text": "\nhints jpeg/webp+compression."
              }
            ],
            [
              {
                "k": "code",
                "text": "image_generation.*"
              },
              {
                "k": "text",
                "text": " observability events ride the Emitter seam (stderr\ntoday; the run-stream integration follows the documented\n"
              },
              {
                "k": "code",
                "text": "EventKind::Extension"
              },
              {
                "k": "text",
                "text": " path — FCI-009, unchanged here)."
              }
            ],
            [
              {
                "k": "text",
                "text": "Run/workflow ids are honestly ABSENT from the manifest (no RunContext\nseam yet — roadmap, never empty-stringed)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "The 24th builtin crossed the agent router's activation threshold"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "RouterConfig::default().min_universe"
              },
              {
                "k": "text",
                "text": " was 24): a plain\n"
              },
              {
                "k": "code",
                "text": "tools: [\"nika:*\"]"
              },
              {
                "k": "text",
                "text": " agent would have silently flipped from\ndeterministic full-catalog passthrough to BM25 narrowing. The default\nis raised to 32 (routing pays for itself on MCP-heavy universes, never\non the stdlib alone) and a composition-root test pins\n"
              },
              {
                "k": "code",
                "text": "tool_defs().len() < min_universe"
              },
              {
                "k": "text",
                "text": " so the next builtin addition fails\nloudly instead of silently re-crossing."
              }
            ],
            [
              {
                "k": "strong",
                "text": "`provider_text` is a caption, not a payload channel"
              },
              {
                "k": "text",
                "text": ": gemini's\ninterleaved text is clamped (2 000 chars · stable\n"
              },
              {
                "k": "code",
                "text": "provider_text_clamped:"
              },
              {
                "k": "text",
                "text": " warning) and the "
              },
              {
                "k": "code",
                "text": "debug:"
              },
              {
                "k": "text",
                "text": " echo clamps ANY\noversized non-payload string — a multimodal response cannot carry\nmegabytes of text into "
              },
              {
                "k": "code",
                "text": "ToolResult.content"
              },
              {
                "k": "text",
                "text": "/the agent's next turn/the\nmanifest (review-swarm refutation, 2026-07-05)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "v1.1 amendment (2026-07-05 · same-day)"
              },
              {
                "k": "text",
                "text": " — the Socratic sovereignty\nreview found the V1 set violated supernovae-alignment Rule 3 (5 local\nLLM providers · zero sovereign image path). The set opens: "
              },
              {
                "k": "strong",
                "text": "`local`"
              },
              {
                "k": "text",
                "text": "\n(one OpenAI-images-compat wire now covers LocalAI · Ollama · sd.cpp\n"
              },
              {
                "k": "code",
                "text": "sd-server"
              },
              {
                "k": "text",
                "text": " · SGLang Diffusion · vLLM-Omni — landscape-verified; base\nURL = engine config "
              },
              {
                "k": "code",
                "text": "NIKA_IMAGE_LOCAL_URL"
              },
              {
                "k": "text",
                "text": ", default LocalAI "
              },
              {
                "k": "code",
                "text": ":8080"
              },
              {
                "k": "text",
                "text": ",\noptional Bearer; "
              },
              {
                "k": "code",
                "text": "response_format: b64_json"
              },
              {
                "k": "text",
                "text": " forced; url-only answers\nREFUSED — result URLs are never fetched; 300s default timeout; never\ninferred from "
              },
              {
                "k": "code",
                "text": "model:"
              },
              {
                "k": "text",
                "text": ") and "
              },
              {
                "k": "strong",
                "text": "`xai`"
              },
              {
                "k": "text",
                "text": " (Imagine API · "
              },
              {
                "k": "code",
                "text": "grok-imagine-image"
              },
              {
                "k": "text",
                "text": "\ndefault, "
              },
              {
                "k": "code",
                "text": "-quality"
              },
              {
                "k": "text",
                "text": " tier as the model knob · native aspect_ratio +\nresolution 1k/2k classes · usage billed in cost ticks → axes stay null).\nRiders: "
              },
              {
                "k": "code",
                "text": "endpoint_host"
              },
              {
                "k": "text",
                "text": " provenance in output+manifest · "
              },
              {
                "k": "code",
                "text": "count_shortfall:"
              },
              {
                "k": "text",
                "text": "\n· "
              },
              {
                "k": "code",
                "text": "revised_prompt_clamped:"
              },
              {
                "k": "text",
                "text": " warnings · "
              },
              {
                "k": "code",
                "text": "format_mismatch:"
              },
              {
                "k": "text",
                "text": " now fires only\non an EXPLICIT "
              },
              {
                "k": "code",
                "text": "format:"
              },
              {
                "k": "text",
                "text": " (xai returns jpeg by design). BFL's cloud FLUX\nAPI stays deferred (async submit→poll wire — a second adapter\narchitecture; the sovereign path to FLUX is "
              },
              {
                "k": "code",
                "text": "local"
              },
              {
                "k": "text",
                "text": "). Mistral has no\nimage-generation API (agent-tool only — verified negative)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "v1.2 polish (2026-07-05 · same-day)"
              },
              {
                "k": "text",
                "text": " — the top-of-art pass\n(competitive research vs n8n/Dify/LangGraph/CrewAI/Make/ComfyUI ·\nprimary sources · Nika held 7/7 differentiators). Three additions:\n(1) "
              },
              {
                "k": "strong",
                "text": "in-file provenance"
              },
              {
                "k": "text",
                "text": " — saved PNG files carry a "
              },
              {
                "k": "code",
                "text": "nika"
              },
              {
                "k": "text",
                "text": " tEXt chunk\n(deterministic core: tool · engine · provider · model · clamped prompt\n· seed — NO timestamp, so mock byte-determinism holds) inserted after\nIHDR before hashing, the ComfyUI/InvokeAI interchange practice no\nworkflow engine ships; (2) "
              },
              {
                "k": "strong",
                "text": "honest cost"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "ProviderBatch.cost_usd"
              },
              {
                "k": "text",
                "text": "\n(xai maps "
              },
              {
                "k": "code",
                "text": "cost_in_usd_ticks"
              },
              {
                "k": "text",
                "text": " exactly · 1¢ = 10⁸ ticks · others\nnull-honest) rides output + manifest + the completed event, and the\nruntime meters ANY invoke tool's top-level "
              },
              {
                "k": "code",
                "text": "cost_usd"
              },
              {
                "k": "text",
                "text": " into the run\nledger (the same channel infer rides); (3) "
              },
              {
                "k": "strong",
                "text": "doctor names the image\nplane"
              },
              {
                "k": "text",
                "text": " — one "
              },
              {
                "k": "code",
                "text": "image"
              },
              {
                "k": "text",
                "text": " finding (mock ready · which cloud keys present ·\nthe local URL). Identified-but-deferred: "
              },
              {
                "k": "code",
                "text": "mode: edit"
              },
              {
                "k": "text",
                "text": "/img2img (the one\nreal competitor gap · reserved args already refuse loudly) · async\nsubmit-poll wires (video-class · not stills)."
              }
            ],
            [
              {
                "k": "strong",
                "text": "The image plane shares the dispatcher's `H` type parameter"
              },
              {
                "k": "text",
                "text": "\n("
              },
              {
                "k": "code",
                "text": "Option<Arc<H>>"
              },
              {
                "k": "text",
                "text": ") rather than a "
              },
              {
                "k": "code",
                "text": "dyn"
              },
              {
                "k": "text",
                "text": " seam: "
              },
              {
                "k": "code",
                "text": "HttpPostDyn"
              },
              {
                "k": "text",
                "text": " is a\n"
              },
              {
                "k": "code",
                "text": "trait_variant"
              },
              {
                "k": "text",
                "text": " async trait (not dyn-compatible), and the house\ncomposes generics everywhere — both planes are "
              },
              {
                "k": "code",
                "text": "nika-http"
              },
              {
                "k": "text",
                "text": " clients\nwith different configs. Accepted trade-off; a distinct provider-plane\nTYPE would need a boxing adapter (additive, if ever needed)."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-107",
      "file": "adr-107-nika-check-wasm-admission.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Gate-by-gate readiness (evidence, not claims)",
          "anchor": "gate-by-gate-readiness-evidence-not-claims"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Gate 5 · mutation run (2026-07-28)",
          "anchor": "gate-5--mutation-run-2026-07-28"
        },
        {
          "title": "Gate 11 · swarm findings and resolutions",
          "anchor": "gate-11--swarm-findings-and-resolutions"
        }
      ],
      "words": 1951,
      "sha256": "601d84a53bd649ddf9ab1d9fee922dd89d159d58054c670554a9485824389b83",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-107: Admit `nika-check-wasm`",
          "id": "adr-107-admit-nika-check-wasm"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The spec reserved this seat before the crate existed — conformance Level 2\nnames « custom engines for specialized environments (embedded · "
            },
            {
              "k": "strong",
              "text": "WASM"
            },
            {
              "k": "text",
              "text": " ·\ncustom LLM gateway) » ("
            },
            {
              "k": "code",
              "text": "spec/07-conformance.md"
            },
            {
              "k": "text",
              "text": "). nika.sh's "
            },
            {
              "k": "code",
              "text": "/play"
            },
            {
              "k": "text",
              "text": " judged\nfiles with a 13-code TypeScript approximation of a 96-code checker, under a\nsite law that forbids hand-written verdicts; the site had even drawn the seam\nin advance ("
            },
            {
              "k": "code",
              "text": "window.NikaOracle"
            },
            {
              "k": "text",
              "text": ", « the day the engine ships its wasm check\nartifact… »). This crate is the seat taken: the same parser, the same\njudgment crates, the same error voice, for the legs a browser genuinely has —\nand its coverage stated in-band ("
            },
            {
              "k": "code",
              "text": "wasm: true"
            },
            {
              "k": "text",
              "text": " + closed "
            },
            {
              "k": "code",
              "text": "legs: []"
            },
            {
              "k": "text",
              "text": ") so the\nbrowser half can never claim the binary's reach."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The artifact already ships, honestly labelled, on nika.sh "
            },
            {
              "k": "code",
              "text": "/play"
            },
            {
              "k": "text",
              "text": "\n(vendored pkg + PROVENANCE.json naming this branch, pre-admission — a\nsite decision, taken because the alternative in production was strictly\nworse). Admission regularises the seat, it does not create the exposure."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Admit "
            },
            {
              "k": "code",
              "text": "nika-check-wasm"
            },
            {
              "k": "text",
              "text": " as an L4 transport surface (the "
            },
            {
              "k": "code",
              "text": "nika-lsp"
            },
            {
              "k": "text",
              "text": "/"
            },
            {
              "k": "code",
              "text": "nika-mcp"
            },
            {
              "k": "text",
              "text": "\nseat: it exposes, it never judges), moving it out of "
            },
            {
              "k": "code",
              "text": "wip = [...]"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate-by-gate readiness (evidence, not claims)",
          "id": "gate-by-gate-readiness-evidence-not-claims"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "# · Gate · State · Evidence"
              }
            ],
            [
              {
                "k": "text",
                "text": "1 · SPEC doc · ✅ · `docs/crate-specs/nika-check-wasm.md` (LOC live-anchored 234)"
              }
            ],
            [
              {
                "k": "text",
                "text": "2 · TDD red/green · ✅ with an honest note · the column test refuted its author twice before it held (wrong line, then under the anchor — three probes to find the flow-mapping shape); leg B went red on a wrong bin-target before green. Written test-first is not claimed for the v0 scaffold."
              }
            ],
            [
              {
                "k": "text",
                "text": "3 · IMPL compiles · ✅ · native + `wasm32-unknown-unknown` (`--profile wasm-release`), workspace green"
              }
            ],
            [
              {
                "k": "text",
                "text": "4 · Zero clippy · ✅ · `cargo clippy -p nika-check-wasm --all-targets` clean (pedantic workspace lints)"
              }
            ],
            [
              {
                "k": "text",
                "text": "5 · Mutation ≥90% · ✅ 95.8% · 24 mutants: 22 caught + 1 timeout (the `-=`→`/=` infinite loop — detected by hanging) + 1 EQUIVALENT (`>`→`>=` on `at > 0`: at 0, `is_char_boundary(0)` is always true, the `&&` exits identically — indistinguishable by construction). 100% of non-equivalent mutants killed."
              }
            ],
            [
              {
                "k": "text",
                "text": "6 · Property tests · ✅ · `line_col` under arbitrary unicode + arbitrary offsets vs an independent char-walking reference (mid-char, past-end, empty, newline-at-offset)"
              }
            ],
            [
              {
                "k": "text",
                "text": "7 · Benchmarks · n/a · the hot paths are `parse` + `analyze`, owned and measured by `nika-schema`/`nika-check`; this crate adds one O(offset) arithmetic per spanned finding"
              }
            ],
            [
              {
                "k": "text",
                "text": "8 · Zero doc warnings · ✅ · `RUSTDOCFLAGS=\"-D warnings\" cargo doc --no-deps -p nika-check-wasm` clean"
              }
            ],
            [
              {
                "k": "text",
                "text": "9 · Canary E2E · ✅ (consumer-side) · nika.sh `check-wasm-oracle.test.ts` loads the real artifact in node and re-judges the SERVED hero twins against the CLI-captured truth"
              }
            ],
            [
              {
                "k": "text",
                "text": "10 · Golden parity · ✅ · the differential pair: leg A (assembly vs library, 125-fixture corpus, always) · leg B (rows vs same-tree CLI, `NIKA_DIFF_CLI=1`) — both seen red before green"
              }
            ],
            [
              {
                "k": "text",
                "text": "11 · 3-agent swarm · ✅ · three legs returned, every finding resolved or declined-with-rationale below — including one BLOCKING engine bug (the jq nesting bomb) the corpus could never have found"
              }
            ],
            [
              {
                "k": "text",
                "text": "12 · Atomic commit · ✅ · this squash-merge — the ADR flipped to `accepted` on the operator's explicit word, 2026-07-28"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The one-voice law crosses the network: a finding on nika.sh IS a\n"
              },
              {
                "k": "code",
                "text": "SchemaError"
              },
              {
                "k": "text",
                "text": " rendered by the same "
              },
              {
                "k": "code",
                "text": "Display"
              },
              {
                "k": "text",
                "text": " and "
              },
              {
                "k": "code",
                "text": "spec_code()"
              },
              {
                "k": "text",
                "text": " as the\nbinary — no port, no paraphrase, no drift surface."
              }
            ],
            [
              {
                "k": "text",
                "text": "The remaining-legs seam becomes load-bearing debt: any future leg lands by\nEXTRACTION from "
              },
              {
                "k": "code",
                "text": "nika-cli"
              },
              {
                "k": "text",
                "text": ", never re-assembly here (the check↔run oracle's\ndivergence class; leg B is the standing tripwire)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The wasm diet (jaq/jsonschema linked into a parse+conform artifact) is\nnamed, owned, and deferred — 3.1M raw today, lazy-loaded on one route."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate 5 · mutation run (2026-07-28)",
          "id": "gate-5--mutation-run-2026-07-28"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "cargo mutants -p nika-check-wasm"
            },
            {
              "k": "text",
              "text": " · 24 mutants in 63s · "
            },
            {
              "k": "strong",
              "text": "22 caught · 1\ntimeout · 1 missed"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The timeout IS a detection: "
              },
              {
                "k": "code",
                "text": "at -= 1"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "at /= 1"
              },
              {
                "k": "text",
                "text": " never decreases the\ncursor, the boundary walk spins forever, the suite hangs — a mutant that\ncan only be observed by not terminating."
              }
            ],
            [
              {
                "k": "text",
                "text": "The miss is an "
              },
              {
                "k": "strong",
                "text": "equivalent mutant"
              },
              {
                "k": "text",
                "text": ": "
              },
              {
                "k": "code",
                "text": "at > 0"
              },
              {
                "k": "text",
                "text": " → "
              },
              {
                "k": "code",
                "text": "at >= 0"
              },
              {
                "k": "text",
                "text": ". At "
              },
              {
                "k": "code",
                "text": "at == 0"
              },
              {
                "k": "text",
                "text": ",\n"
              },
              {
                "k": "code",
                "text": "is_char_boundary(0)"
              },
              {
                "k": "text",
                "text": " is true for every string, so the "
              },
              {
                "k": "code",
                "text": "&&"
              },
              {
                "k": "text",
                "text": " short-circuit\nexits the loop under both spellings; no test can distinguish them because\nno behaviour does. Documented here rather than contorted around."
              }
            ],
            [
              {
                "k": "text",
                "text": "The first run also missed both "
              },
              {
                "k": "code",
                "text": "engine_version"
              },
              {
                "k": "text",
                "text": " mutants\n("
              },
              {
                "k": "code",
                "text": "String::new()"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "\"xyzzy\""
              },
              {
                "k": "text",
                "text": "); consumers pin that value against captured\nprovenance, so the kill test asserts it equals "
              },
              {
                "k": "code",
                "text": "CARGO_PKG_VERSION"
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Gate 11 · swarm findings and resolutions",
          "id": "gate-11--swarm-findings-and-resolutions"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Leg 1 · spn-nika reviewer (one-voice · zero-unwrap · I/O · layers)",
          "id": "leg-1--spn-nika-reviewer-one-voice--zero-unwrap--io--layers"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Verdict: hard rules ALL PASS (one-voice: only "
            },
            {
              "k": "code",
              "text": "spec_code()"
            },
            {
              "k": "text",
              "text": " + "
            },
            {
              "k": "code",
              "text": "Display"
            },
            {
              "k": "text",
              "text": " +\n"
            },
            {
              "k": "code",
              "text": "ERROR_DOCS_BASE"
            },
            {
              "k": "text",
              "text": " reach the wire, "
            },
            {
              "k": "code",
              "text": ".nika_code()"
            },
            {
              "k": "text",
              "text": " never called · zero unwrap\noutside "
            },
            {
              "k": "code",
              "text": "cfg(test)"
            },
            {
              "k": "text",
              "text": " · zero I/O so the kernel seam is correctly vacuous · L4\ndefensible under the registry's own sort test). Changes requested on\nPROCESS, and the reviewer was right:"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Finding · Severity · Resolution"
              }
            ],
            [
              {
                "k": "text",
                "text": "**No CI job ever built wasm32** — the crate's own reason to exist was verified only by hand · Critical · **FIXED at prep** · `scripts/ci/check-wasm.sh` + the `wasm` matrix leg in diamond-ci.yml: clippy `-D warnings` on the target, `--profile wasm-release` build, and differential Leg B"
              }
            ],
            [
              {
                "k": "text",
                "text": "Leg B (`NIKA_DIFF_CLI=1`) wired nowhere — silently no-ops unless a human remembers · Warning · **FIXED at prep** · the same CI leg exports it; the spec-fixtures checkout step now serves `tests` and `wasm` legs both"
              }
            ],
            [
              {
                "k": "text",
                "text": "No `docs/crate-specs/nika-check-wasm.md` · Warning · **already landed in parallel** during prep (the reviewer read the pre-edit tree) — live-anchored at 234 LOC"
              }
            ],
            [
              {
                "k": "text",
                "text": "`wasm-bindgen`/`getrandom`/`getrandom-02` pinned crate-side against the pin-once workspace rule · Warning · **FIXED at prep** · all three centralised in `[workspace.dependencies]` with attribution comments; the crate goes `.workspace = true`"
              }
            ],
            [
              {
                "k": "text",
                "text": "`crate-layer-registry.md` L4 rows not updated — and the two PRE-EXISTING near-duplicate L4 table rows (166/167, differing only by `nika-onboard` vs `nika-models`) · Warning · **FIXED at prep** · the duplicate rows merged into one honest row carrying both crates, `nika-check-wasm` seated in the row and the ASCII map (marked WIP · ADR-107)"
              }
            ],
            [
              {
                "k": "text",
                "text": "README owed-list numbering skipped an item · Suggestion · **FIXED at prep**"
              }
            ],
            [
              {
                "k": "text",
                "text": "The span arithmetic deserves a proptest · Suggestion · **already landed in parallel** — `line_col` vs an independent char-walker under arbitrary unicode (Gate 6)"
              }
            ],
            [
              {
                "k": "text",
                "text": "A typed verdict (`tsify`/hand `.d.ts`) at the TS boundary · Suggestion · **DECLINED with rationale** · the verdict's contract is CLI parity, held by the differential pair and the site's node gate; a second typed assembly would be a second thing able to drift — the exact divergence class Leg B exists to kill. The consumer types what it consumes (`oracle.ts` `WasmRow`), and the gate proves it against the artifact."
              }
            ],
            [
              {
                "k": "text",
                "text": "L4 carries no `layer-bans` entry · Note · **HELD for the ceremony** · what L4 may never depend on is a policy decision, the operator's; the CI wasm leg already provides the practical stop the finding wanted (a wasm32-incompatible dep now reddens every push)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Leg 2 · rust-security (hostile input · determinism · supply surface)",
          "id": "leg-2--rust-security-hostile-input--determinism--supply-surface"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Method: the reviewer built the real artifact and fed it 125 conformance\nfixtures plus ~50 crafted hostile inputs under V8 and a 1 MiB-stack native\nharness. PASSES earned, not assumed: the char walk survived every probe\n(mid-char, past-EOF, BOM, NUL, emoji — "
            },
            {
              "k": "code",
              "text": "badspan=0"
            },
            {
              "k": "text",
              "text": " across the corpus) ·\nbillion-laughs closed · determinism structural (BTreeMap throughout, 25\nprocesses → 1 output hash, zero entropy consumed on the check path) · SSRF\nclosed at the schema compiler ("
            },
            {
              "k": "code",
              "text": "default-features = false"
            },
            {
              "k": "text",
              "text": ", no fetch). Then\nthe verdict that mattered: "
            },
            {
              "k": "em",
              "text": "« nothing in the 12 gates proves panic-freedom\non hostile input — F1 was found in the first ten minutes of feeding it\ngarbage. »"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Finding · Severity · Resolution"
              }
            ],
            [
              {
                "k": "text",
                "text": "**F1 · a 1,047-byte paste stack-overflows the checker** — jaq's parser recurses per nesting level with no limit; a trapped wasm instance never recovers, and the installed CLI aborts (exit 134) on the same file · **Blocking** · **FIXED in `nika-check`** · pre-flight `jq_nesting_over` guard (string-aware O(n) scan · ceiling 128, the house's own cap, precedents `MAX_VALUE_DEPTH`/`nika-tmpl`) before `jq_compiles`, covering all three doors (`nika:jq` · `nika:fetch` jq · `on_finally`) · regression proves the bomb refuses AND the instance survives · fuzz corpus seeded with the 470-bracket shape"
              }
            ],
            [
              {
                "k": "text",
                "text": "**F2 · 28s of synchronous CPU inside the declared caps** — `did_you_mean` is O(n²·L²) across all task ids · High · **FIXED in `nika-check`** · a suggestion budget (256 candidates): past it the DAG-002 finding still fires, it just stops guessing · regression proves finding-without-suggestion at 300 ids · the Worker seat for the site consumer stays a named follow-up"
              }
            ],
            [
              {
                "k": "text",
                "text": "F3 · `<` `>` `&` U+2028/9 legal in JSON strings, hostile in every context a site inlines JSON into · Medium · **FIXED** · `escape_for_embedding` rewrites the five to `\\uXXXX` over the emitted text (only ever inside string literals → equivalent JSON) · regression proves zero raw occurrences and an intact round-trip"
              }
            ],
            [
              {
                "k": "text",
                "text": "F4 · `build-wasm.sh` could package a wasm it did not build (`CARGO_TARGET_DIR` + hardcoded path) · `--lib` skipped the differential · no `--locked` · Low · **FIXED** · artifact path derived from `cargo metadata` · full `--locked` test run"
              }
            ],
            [
              {
                "k": "text",
                "text": "F5 · no `--` terminator before the fixture path in the leg-B spawn · Low · **FIXED**"
              }
            ],
            [
              {
                "k": "text",
                "text": "F6 · emitted `span` not clamped (only the line/col walk is) · Low · **DECLINED with rationale** · the emitted span must stay byte-equal to the CLI's (leg A asserts it against the library accessor — clamping would manufacture divergence); `line`/`col` are the sanctioned consumer fields, and the reviewer could not produce an out-of-range span across the corpus"
              }
            ],
            [
              {
                "k": "text",
                "text": "F7 · `legs:` under-declares (the CONFORM analyzer also compile-checks jq + JSON Schema) · Info · **FIXED** · the nuance stated in the verdict doc comment; the legs list stays gate-named"
              }
            ],
            [
              {
                "k": "text",
                "text": "F8 · miette's terminal stack is pure payload in a browser · Info · already the named diet follow-up (feature-gating the judgment hub's heavy corners)"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Leg 3 · correctness (contract fit · docs honesty)",
          "id": "leg-3--correctness-contract-fit--docs-honesty"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "PASSES earned: the line/col arithmetic verified at every boundary the hand\ncan reach, and its property test judged « real coverage, not tautological »\n· CRLF self-consistency argued from the shared source string · the leg-B\nbin-target and fixture-path resolutions verified against the real\nfilesystem · the consumer's "
            },
            {
              "k": "code",
              "text": "line ?? 1"
            },
            {
              "k": "text",
              "text": " fallback matches the port's own\nconvention."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Finding · Confidence · Resolution"
              }
            ],
            [
              {
                "k": "text",
                "text": "README's Build section documented a WORSE recipe than `build-wasm.sh` (wrong profile · wrong path · no wasm-opt) — a contributor copying it from the crate dir gets a file-not-found, or an unstripped artifact · 88 · **FIXED** · the section is now a pointer at the script («a pointer cannot drift»), with the drift named"
              }
            ],
            [
              {
                "k": "text",
                "text": "`severity` · `kind` · the `docs_url` separator asserted NOWHERE — three plausible one-token mutants passed every test in the repo · 85 · **FIXED** · leg A asserts all three per row (exact-format `docs_url`); severity joins leg B's comparison tuple"
              }
            ],
            [
              {
                "k": "text",
                "text": "`PROVENANCE.json` « missing » beside pkg/ · no `estate.yaml` row for the vendored artifact · 80 · **HALF-STALE, HALF-PAID** · the file exists one level up (`src/lib/check-wasm/PROVENANCE.json`, beside the pkg it describes — the reviewer walked pkg/ only); the REAL half — the site's estate ledger had no row — is paid site-side with this resolution"
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-108",
      "file": "adr-108-nika-proof-split.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        }
      ],
      "words": 561,
      "sha256": "973496cd95b8b6c0079daa5a96e2caff178e42cd72d7053616d93023baaf0cb9",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-108: Split the spec-15 proof primitives into `nika-proof`",
          "id": "adr-108-split-the-spec-15-proof-primitives-into-nika-proof"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The 15k production-LOC invariant ("
            },
            {
              "k": "code",
              "text": "scripts/ci/check-crate-size.sh"
            },
            {
              "k": "text",
              "text": " ·\nCONSTELLATION_PLAN §7 criterion 3) is a hard pre-push gate, and nika-runtime\nsat at 14,952 of 15,000 on "
            },
            {
              "k": "code",
              "text": "main"
            },
            {
              "k": "text",
              "text": " ("
            },
            {
              "k": "code",
              "text": "bee82f9b1"
            },
            {
              "k": "text",
              "text": " · 99.7% full). The F-P6\npreview→commit lane adds ~460 LOC of new machinery (the canonical request,\nthe digest gate, the evidence plumbing) that no amount of documentation diet\ncould fit under the cap — measured with the gate's own counter\n("
            },
            {
              "k": "code",
              "text": "git ls-files"
            },
            {
              "k": "text",
              "text": " · src/ minus "
            },
            {
              "k": "code",
              "text": "#[cfg(test)]"
            },
            {
              "k": "text",
              "text": " minus "
            },
            {
              "k": "code",
              "text": "tests.rs"
            },
            {
              "k": "text",
              "text": ")."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The house's documented answer to a full crate is a descent split — the\nworkspace "
            },
            {
              "k": "code",
              "text": "Cargo.toml"
            },
            {
              "k": "text",
              "text": " layer comments name the precedents verbatim\n("
            },
            {
              "k": "code",
              "text": "nika-dap"
            },
            {
              "k": "text",
              "text": " split from nika-cli \"the crate-size cap · the nika-cap\nprecedent\", "
            },
            {
              "k": "code",
              "text": "nika-models"
            },
            {
              "k": "text",
              "text": " the same)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Create "
            },
            {
              "k": "strong",
              "text": "`nika-proof`"
            },
            {
              "k": "text",
              "text": " (L0, WIP) carrying the spec-15 proof layer's pure\nhash primitives — the seven "
            },
            {
              "k": "code",
              "text": "HashDomain"
            },
            {
              "k": "text",
              "text": "s, the JCS canonical bytes, the\ndomain-separated pre-image, the blake3 semantic hash, and the one receipt\n("
            },
            {
              "k": "code",
              "text": "build_receipt"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "build_run_receipt"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "verify"
            },
            {
              "k": "text",
              "text": ") — moved verbatim from\n"
            },
            {
              "k": "code",
              "text": "crates/nika-runtime/src/proof/{mod,receipt}.rs"
            },
            {
              "k": "text",
              "text": "."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The "
            },
            {
              "k": "code",
              "text": "ir"
            },
            {
              "k": "text",
              "text": " projection ("
            },
            {
              "k": "code",
              "text": "semantic_ir_hash"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "task_semantic_hash"
            },
            {
              "k": "text",
              "text": ") STAYS in\nnika-runtime: it reuses the resume family's task-definition projection\n("
            },
            {
              "k": "code",
              "text": "crate::resume::definition_value"
            },
            {
              "k": "text",
              "text": "), so it cannot descend without dragging\nthe family with it. nika-runtime re-exports the whole crate at the exact\nold path — "
            },
            {
              "k": "code",
              "text": "pub mod proof { pub use nika_proof::*; pub mod ir; }"
            },
            {
              "k": "text",
              "text": " — so\nevery consumer path ("
            },
            {
              "k": "code",
              "text": "nika_runtime::proof::{preimage, HashDomain, receipt::build_receipt, …}"
            },
            {
              "k": "text",
              "text": ") resolves unchanged and nika-dap needs no edit."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected: shrinking the F-P6 machinery to fit (the lane's honest minimum\nexceeds the 48 free lines by ~10×); moving the judge to nika-cap (covers\n~120 of ~460 · still red); an admitted-crate birth (the 12-gate ceremony is\nits own lane — the crate lands WIP, the "
            },
            {
              "k": "code",
              "text": "wip"
            },
            {
              "k": "text",
              "text": " array's own posture)."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "nika-runtime drops to ~14.4k prod LOC — the F-P6 lane fits, and the next\nlanes get ~600 lines of headroom back."
              }
            ],
            [
              {
                "k": "text",
                "text": "The receipt/hash primitives become consumable without the whole L3\nruntime (nika-dap already reads them through the re-export)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The split is history-clean ("
              },
              {
                "k": "code",
                "text": "git mv"
              },
              {
                "k": "text",
                "text": " — both files keep their blame)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The proof layer now lives in two places by necessity ("
              },
              {
                "k": "code",
                "text": "ir"
              },
              {
                "k": "text",
                "text": " in\nnika-runtime · the primitives in nika-proof) — the follow-up names the\nreunification condition (the resume projection descending too)."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-runtime/public-api.txt"
              },
              {
                "k": "text",
                "text": " re-renders the moved items as the glob\nre-export (paths preserved · additive under cargo-semver-checks)."
              }
            ],
            [
              {
                "k": "text",
                "text": "A new WIP crate to admit later (the 12-gate table)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The layer registry ("
              },
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " "
              },
              {
                "k": "code",
                "text": "[workspace.metadata.diamond]"
              },
              {
                "k": "text",
                "text": " +\n"
              },
              {
                "k": "code",
                "text": "docs/architecture/crate-layer-registry.md"
              },
              {
                "k": "text",
                "text": ") gains the L0 row; the\nstatus-claims projection ("
              },
              {
                "k": "code",
                "text": "scripts/refresh-status.sh"
              },
              {
                "k": "text",
                "text": " · vector 23) is\nregenerated (58 crates · 3 WIP · 19 L0)."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-proof/src/lib.rs"
              },
              {
                "k": "text",
                "text": " — ex "
              },
              {
                "k": "code",
                "text": "crates/nika-runtime/src/proof/mod.rs"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-proof/src/receipt.rs"
              },
              {
                "k": "text",
                "text": " — ex "
              },
              {
                "k": "code",
                "text": "crates/nika-runtime/src/proof/receipt.rs"
              },
              {
                "k": "text",
                "text": "."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-runtime/src/lib.rs"
              },
              {
                "k": "text",
                "text": " — the "
              },
              {
                "k": "code",
                "text": "pub mod proof { pub use nika_proof::*; pub mod ir; }"
              },
              {
                "k": "text",
                "text": " seam."
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-runtime/src/proof/ir.rs"
              },
              {
                "k": "text",
                "text": " — stays (the resume-projection coupling)."
              }
            ],
            [
              {
                "k": "code",
                "text": "Cargo.toml"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "members"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "wip"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "layers.nika-proof = \"L0\""
              },
              {
                "k": "text",
                "text": "."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "list",
          "ordered": true,
          "items": [
            [
              {
                "k": "strong",
                "text": "Trim the lane to the cap"
              },
              {
                "k": "text",
                "text": " — measured: the design-minimal machinery is\n~260 LOC against 48 free lines. Rejected as impossible without dropping\ndesign laws."
              }
            ],
            [
              {
                "k": "strong",
                "text": "nika-cap as the judge's home"
              },
              {
                "k": "text",
                "text": " — covers the pure judge (~120 LOC) but\nleaves the dispatch lanes + settle plumbing in a still-red crate.\nRejected as insufficient."
              }
            ],
            [
              {
                "k": "strong",
                "text": "Split `compose.rs`"
              },
              {
                "k": "text",
                "text": " — larger (1,326 LOC) but the public composer API\nis the workspace's hottest re-export surface. Rejected as the riskier\nmove; the proof primitives are the cleaner descent."
              }
            ]
          ]
        }
      ]
    },
    {
      "id": "ADR-109",
      "file": "adr-109-composition-proof-receipt.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Evidence / Affected code",
          "anchor": "evidence--affected-code"
        },
        {
          "title": "Alternatives considered",
          "anchor": "alternatives-considered"
        },
        {
          "title": "Related",
          "anchor": "related"
        },
        {
          "title": "Notes",
          "anchor": "notes"
        }
      ],
      "words": 2358,
      "sha256": "c88c7deebc755b7edab41aaa29670cf18b144b24700c133ef365b11c580d75ec",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-109: Publish the composition proof receipt — what this engine may claim on spec 14",
          "id": "adr-109-publish-the-composition-proof-receipt--what-this-engine-may-claim-on-spec-14"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Spec "
            },
            {
              "k": "code",
              "text": "14-composition.md"
            },
            {
              "k": "text",
              "text": " opens with a word of discipline: an engine may say a\ncomposition is "
            },
            {
              "k": "em",
              "text": "specified"
            },
            {
              "k": "text",
              "text": " the moment it parses and checks; it may only say\n"
            },
            {
              "k": "em",
              "text": "proven"
            },
            {
              "k": "text",
              "text": " once it has demonstrated, "
            },
            {
              "k": "strong",
              "text": "together, on real child execution"
            },
            {
              "k": "text",
              "text": ":\nstatic resolution · typed I/O · effect containment · inherited budgets ·\ncycle detection · the trace forest · nested receipts · semantic resume ·\nreference-model parity. The external audit of 2026-07-29/30 asked for the\nreceipt, condition by condition, partial allowed — because a claim without a\nreceipt is exactly the class of drift the contract forbids."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The evidence base is executable, not prose: "
            },
            {
              "k": "code",
              "text": "crates/nika-cli/tests/composition_e2e.rs"
            },
            {
              "k": "text",
              "text": "\n(9 tests against the REAL binary — real files, real subprocesses, an\nindependent sha256 chain re-walk that does not trust the engine's own\nverifier), the four composition cases in "
            },
            {
              "k": "code",
              "text": "crates/nika-cli/tests/resume_e2e.rs"
            },
            {
              "k": "text",
              "text": "\n(condition 8), plus a live run of the taught lesson\n("
            },
            {
              "k": "code",
              "text": "crates/nika-pack/pack/examples/10-compose-pipeline.nika.yaml"
            },
            {
              "k": "text",
              "text": " · offline on\n"
            },
            {
              "k": "code",
              "text": "mock/echo"
            },
            {
              "k": "text",
              "text": ") whose two journals pass "
            },
            {
              "k": "code",
              "text": "nika trace verify"
            },
            {
              "k": "text",
              "text": " (rc=0 each)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The 2026-07-30 pass added a discipline the earlier ones lacked: each row's\nclaim was probed BY HAND before any test was written, and each probe was\nbuilt to make the claim LIE. That is how the two defects below surfaced —\nboth had green suites over them."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Publish the per-condition receipt below and bind the claim vocabulary to it.\nThis engine claims composition is "
            },
            {
              "k": "strong",
              "text": "specified, and demonstrated on 9 of the 9\nconditions — at the tiers each row names"
            },
            {
              "k": "text",
              "text": ". It still does "
            },
            {
              "k": "strong",
              "text": "not"
            },
            {
              "k": "text",
              "text": " claim\n\"proven\" flatly: two rows are demonstrated at a NAMED LOWER TIER than spec 14's\nceiling (condition 8 at the "
            },
            {
              "k": "code",
              "text": "def_hash"
            },
            {
              "k": "text",
              "text": " tier, its semantic tier owed to W6;\ncondition 7 at the chain-commit tier, its spec-15 receipt ladder owed to\n"
            },
            {
              "k": "code",
              "text": "nika-proof"
            },
            {
              "k": "text",
              "text": "), and a tier is not a promotion — the word \"proven\" unlocks when\nthose two tiers close, and this ADR is re-issued again."
            }
          ]
        },
        {
          "k": "quote",
          "inline": [
            {
              "k": "strong",
              "text": "Amended 2026-07-30 — condition 8 CLOSES at the `def_hash` tier, and the\nsocratic probe that closed it found a WRONG SKIP."
            },
            {
              "k": "text",
              "text": " The row was "
            },
            {
              "k": "em",
              "text": "open"
            },
            {
              "k": "text",
              "text": " on\nthe assumption that resume across a composition needed the W6 semantic IR.\nProbed by hand first (parent + child · real files · "
            },
            {
              "k": "code",
              "text": "--resume"
            },
            {
              "k": "text",
              "text": " after an\nedit), the truth was worse than \"missing\": resume ALREADY crossed the\nboundary and cache-hit the call — while the CHILD FILE'S CONTENT was in no\nhash at all. An edited child served the OLD child's output as a hit\n(measured: "
            },
            {
              "k": "code",
              "text": "{\"echoed\":\"hello composition\"}"
            },
            {
              "k": "text",
              "text": " when a fresh run said\n"
            },
            {
              "k": "code",
              "text": "goodbye"
            },
            {
              "k": "text",
              "text": "). That is ADR-099 trap 6 — \"an edited task NEVER silently skips\"\n— leaking through the one edge the key recipe did not cover, the file\nboundary. Fixed by putting the child's transitive source-closure digest\ninto the calling task's DEFINITION identity (a Merkle fold, so a\ngrandchild edit re-keys the whole chain), with a missing digest making the\ntask non-eligible rather than skippable. Condition 8 now ships four e2e\ndemonstrations; the SEMANTIC tier (within-child granularity · law 10's own\nwords) is named as the residual, not claimed."
            },
            {
              "k": "strong",
              "text": "Amended 2026-07-30 (same day · the proof-ladder pass) — two verify\ndefects, both in the dangerous direction."
            },
            {
              "k": "text",
              "text": " The adversarial sweep over\n"
            },
            {
              "k": "code",
              "text": "nika trace verify"
            },
            {
              "k": "text",
              "text": "'s own claims (\"the HIGHEST honestly-attained tier\" ·\n\"a re-written journal now needs the key, not just write access\") broke two\nof them: (1) "
            },
            {
              "k": "strong",
              "text": "append-after-seal was invisible"
            },
            {
              "k": "text",
              "text": " — a line chained after a\nvalid "
            },
            {
              "k": "code",
              "text": "run_sealed"
            },
            {
              "k": "text",
              "text": " frame verified rc=0, the seal never mentioned, and the\njournal reported either \"the run was killed or crashed\" (a FALSE statement:\nthe journal carries a seal) or a plain \"OK · chain intact\"; re-chaining\nneeds only write access, so this is precisely the forgery the SEALED tier\nexists to catch. Now the "
            },
            {
              "k": "code",
              "text": "SEAL BURIED"
            },
            {
              "k": "text",
              "text": " class: exit FILE, the seal located\nand the trailing lines counted, and a torn tail after a seal still reads as\nthe crash it is. (2) "
            },
            {
              "k": "strong",
              "text": "an absent key was rendered as forgery"
            },
            {
              "k": "text",
              "text": " — an intact,\ngenuinely sealed journal verified without its public key said \"SEAL FORGED\"\nand exited 2, an accusation on no evidence (the third-party case the\nartifact exists for). Now "
            },
            {
              "k": "code",
              "text": "SEAL UNATTRIBUTABLE"
            },
            {
              "k": "text",
              "text": " · exit ENV: the signature\nis not judged, non-zero so a gate still fails closed. Neither defect\ntouched a condition row — they are the LADDER the rows are read through,\nso they are recorded here."
            },
            {
              "k": "strong",
              "text": "Amended twice on 2026-07-29"
            },
            {
              "k": "text",
              "text": " (this receipt is meant to move; each\namendment names what changed and why). Condition "
            },
            {
              "k": "strong",
              "text": "9"
            },
            {
              "k": "text",
              "text": " was "
            },
            {
              "k": "em",
              "text": "open"
            },
            {
              "k": "text",
              "text": " — a\nparity nobody had run, because the adapter the spec's protocol had\ndescribed for months did not exist. Written, run, and the composition\nfamily reads 9/9 across two independent oracles; the suite-wide residue\nis classified rather than rounded away, and the run found one real\nengine defect (a templated "
            },
            {
              "k": "code",
              "text": "model:"
            },
            {
              "k": "text",
              "text": " refused as a bare id). Condition\n"
            },
            {
              "k": "strong",
              "text": "8"
            },
            {
              "k": "text",
              "text": " stays the single open one."
            },
            {
              "k": "strong",
              "text": "Amended 2026-07-29 (same day)"
            },
            {
              "k": "text",
              "text": " — condition 4 was published "
            },
            {
              "k": "em",
              "text": "partial"
            },
            {
              "k": "text",
              "text": "\n(\"the cost half is implemented, the run-level test is owed\"), and the\nowed test found something better than itself: the cost half is enforced\n"
            },
            {
              "k": "strong",
              "text": "statically"
            },
            {
              "k": "text",
              "text": ", at the parent's pre-flight, by law 5's own summation —\na child's floor refuses the parent's run before a single token. The\nrun-level metered cut survives as a named, non-hermetic residual rather\nthan a gap. The original wording stays above this line in git; the table\nbelow carries the current claim."
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "# · condition (spec 14) · status · evidence"
              }
            ],
            [
              {
                "k": "text",
                "text": "1 · static resolution · **demonstrated** · `templated_target_is_refused_at_check` (`NIKA-COMP-001` · law 1); real relative-path resolution in every green test; the released 0.106.1 prints a dedicated `COMPOSITION` check line (\"static, typed, contained and acyclic\")"
              }
            ],
            [
              {
                "k": "text",
                "text": "2 · typed I/O · **demonstrated** · `child_runs_for_real_and_typed_outputs_remount` (law 2, both halves, on a real `echo` subprocess: child typed output → parent task value → parent output); `missing_required_child_input_is_refused_at_check` (`NIKA-COMP-004` names the missing input)"
              }
            ],
            [
              {
                "k": "text",
                "text": "3 · effect containment · **demonstrated** · `child_effect_outside_the_parent_boundary_is_refused` (`NIKA-COMP-002` · child `exec` under a net-only parent · laws 3/4)"
              }
            ],
            [
              {
                "k": "text",
                "text": "4 · inherited budgets · **demonstrated (static tier) · one residual named** · time half: `parent_timeout_bounds_the_real_child` (the child future is dropped at the parent task's deadline). Cost half: `the_child_floor_bounds_the_parent_budget_before_any_token` — a parent with NO priced task of its own is refused BEFORE IT STARTS because the child's floor exceeds `--max-cost-usd` (rc 2 · no trace written · no provider touched · hermetic at $0), and the child alone under the same budget reports the SAME floor to the cent, so that floor is the child's own summed into the parent (law 5 at work). An unpriced child under the same tiny budget runs green — the gate reads the floor, never the mere presence of a budget. **Residual**: the run-level metered cut (`remaining_budget_usd` handed to the child runtime · `child_runner.rs:210`) is the backstop for spend the static floor cannot bound (an uncapped `infer:`), and demonstrating it requires a METERED provider — it cannot be shown hermetically, and is not claimed here"
              }
            ],
            [
              {
                "k": "text",
                "text": "5 · cycle detection · **demonstrated** · `static_cycle_is_refused_at_check` (a two-file cycle · `NIKA-COMP-003` at check AND `run` refusing through the same gate · law 7); `acyclic_chain_beyond_the_depth_bound_fails_closed_at_run` (`NIKA-SEC-003` backstop at real 10-deep nesting — the case static acyclicity cannot cover)"
              }
            ],
            [
              {
                "k": "text",
                "text": "6 · trace forest · **demonstrated** · `trace_forest_two_chains_and_the_parent_commits_to_the_child`: two journals on disk, each hash chain intact under an INDEPENDENT re-walk (the test's own sha256, genesis `nika-trace-v1`); live lesson run: 2 traces, `nika trace verify` rc=0 on both"
              }
            ],
            [
              {
                "k": "text",
                "text": "7 · nested receipts · **demonstrated (chain-commit tier) · one gap named** · the parent's hash-chained terminal frame embeds the child's chain head at commit time plus the child's `def_hash` — tamper with any earlier child line and the committed head no longer matches (law 9's Merkle commitment at the journal tier; the spec-15 receipt ladder above it is `nika-proof` territory). **Gap (probed 2026-07-30)**: the commitment is CHECKABLE but no shipped verb CHECKS it — `nika trace verify <parent>` walks the parent file only and says nothing about the child (correctly: it never overclaims — measured, a falsified child journal leaves the parent's own verdict rc=0 with zero mention of the child). The demonstration is `trace_forest_two_chains_and_the_parent_commits_to_the_child`'s own independent sha256 re-walk; an operator has no command for it (`--forest` is the follow-up)"
              }
            ],
            [
              {
                "k": "text",
                "text": "8 · semantic resume · **demonstrated (`def_hash` tier) · the semantic tier named** · four e2e in `resume_e2e.rs` on REAL nested runs: unchanged files cache-hit ACROSS the boundary (`resume_across_a_composition_cache_hits_the_call`); an EDITED child re-runs the call instead of serving the stale output (`an_edited_child_reruns_the_call_instead_of_serving_stale_output` — the wrong skip this row's probe FOUND and fixed); a GRANDCHILD edit re-keys transitively through the Merkle closure fold (`an_edited_grandchild_reruns_the_call_transitively`); a run torn mid-composition re-runs the child WHOLE (`a_composition_torn_mid_child_reruns_the_child_whole`). The identity is the child's transitive SOURCE-closure digest, composer-resolved (`child_runner::closure_digests`) and folded into the calling task's definition hash (`nika-runtime/src/resume.rs`); a target the composer cannot resolve yields NO stamp (the task re-runs · never a wrong skip). **Named residual**: law 10's own tier is the W6 SEMANTIC IR — within-child granularity (resuming INSIDE a child, skipping only the child tasks whose semantics are unchanged) is not claimed; today a changed child re-runs whole, and two children differing only in formatting re-run rather than hit"
              }
            ],
            [
              {
                "k": "text",
                "text": "9 · reference-model parity · **demonstrated (composition family)** · rendered 2026-07-29 through the adapter the protocol had only described (`spec conformance/adapters/nika-engine.py` · the Bowtie pattern): the nine `tests/deep/composition` fixtures are judged by BOTH oracles and agree **9/9**, and the whole `deep` tier is 37/37. Corpus-wide the two-oracle differential reads 49/49 · 0 unexplained · ledger **0** (`spec scripts/oracle-differential.py`). Suite-wide parity is 200/215 with the fifteen divergences classified in the protocol doc — **none of them composition** (they are: a category-only expectation the adapter cannot match · the codeless MODELS rung · tier-scope, a full engine judging more layers than a tier-scoped fixture binds · and one documented spec-vs-engine doctrinal disagreement on open-schema soundness). The measurement also FOUND and fixed an engine defect: the MODELS rung refused a templated `model:`, i.e. refused the parameterization pattern spec 08 §H8 recommends by name"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Positive",
          "id": "positive"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "\"Proven\" stays a load-bearing word: the public claim is now exactly as\nstrong as the executable evidence, per the spec's own discipline note —\nand each row that sits below spec 14's ceiling says WHICH tier it holds\nat, so a tier can never be read as a promotion."
              }
            ],
            [
              {
                "k": "text",
                "text": "Every gap is a named, greppable follow-up ("
              },
              {
                "k": "code",
                "text": "follow_ups:"
              },
              {
                "k": "text",
                "text": " above) — the\nnext session picks them up without re-deriving this audit."
              }
            ],
            [
              {
                "k": "text",
                "text": "The receipt is replayable: every row cites tests that run in ~2s\n("
              },
              {
                "k": "code",
                "text": "cargo test -p nika-cli --test composition_e2e --test resume_e2e"
              },
              {
                "k": "text",
                "text": " ·\n9/9 + 12/12 green 2026-07-30) or a command ("
              },
              {
                "k": "code",
                "text": "nika trace verify"
              },
              {
                "k": "text",
                "text": ") — no\ntrust in this document required."
              }
            ],
            [
              {
                "k": "text",
                "text": "Two verify defects are closed with the probes that found them kept as\ntests ("
              },
              {
                "k": "code",
                "text": "a_buried_seal_reports_tampered_and_never_blames_a_crash"
              },
              {
                "k": "text",
                "text": " ·\n"
              },
              {
                "k": "code",
                "text": "a_key_id_mismatch_across_all_candidates_is_unattributable_never_forged"
              },
              {
                "k": "text",
                "text": "),\neach mutation-proven: reverting the production code kills them."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Negative",
          "id": "negative"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "The engine's public posture is deliberately weaker than \"it all works\"\nfeels from the green suite. That is the cost of the vocabulary; we accept\nit."
              }
            ],
            [
              {
                "k": "text",
                "text": "The receipt is dated evidence and will rot if not re-issued when the\ncomposition surface moves — re-issue triggers listed in Notes."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Neutral",
          "id": "neutral"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Condition 7 is demonstrated at the journal chain-commit tier; the full\nspec-15 receipt composition (SEALED/ANCHORED tiers over nested runs) rides\nthe "
              },
              {
                "k": "code",
                "text": "nika-proof"
              },
              {
                "k": "text",
                "text": " ladder and is not claimed here."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Evidence / Affected code",
          "id": "evidence--affected-code"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "crates/nika-cli/tests/composition_e2e.rs"
              },
              {
                "k": "text",
                "text": " — the 9-test demonstration suite"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-cli/tests/resume_e2e.rs"
              },
              {
                "k": "text",
                "text": " — the 4 composition cases (condition 8)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-runtime/src/resume.rs"
              },
              {
                "k": "text",
                "text": " — the child-closure digest in the calling\ntask's definition identity ("
              },
              {
                "k": "code",
                "text": "workflow_targets"
              },
              {
                "k": "text",
                "text": " · the "
              },
              {
                "k": "code",
                "text": "child_closure"
              },
              {
                "k": "text",
                "text": " payload)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-cli/src/verbs/run/child_runner.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "closure_digests"
              },
              {
                "k": "text",
                "text": " (the\ncomposer's transitive Merkle fold over the child sources)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-dap/src/anchor/tier.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "SealTier::Buried"
              },
              {
                "k": "text",
                "text": " (the\nappend-after-seal class) + "
              },
              {
                "k": "code",
                "text": "SealTier::Unattributable"
              },
              {
                "k": "text",
                "text": " (an absent key is a\nmissing input, never forgery)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-cli/src/verbs/trace_verify.rs"
              },
              {
                "k": "text",
                "text": " — the TAMPERED headline that\nreplaces the walk's crash reading when a seal is buried"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-runtime/src/child.rs"
              },
              {
                "k": "text",
                "text": " — child-workflow execution (the composition seam)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-runtime/src/dispatch.rs"
              },
              {
                "k": "text",
                "text": " — "
              },
              {
                "k": "code",
                "text": "child_budget"
              },
              {
                "k": "text",
                "text": " law-6 doc + the workflow-call dispatch"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-runtime/src/workflow_call.rs"
              },
              {
                "k": "text",
                "text": " — min(parent remaining, child declared)"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-check/src/composition.rs"
              },
              {
                "k": "text",
                "text": " — the static half ("
              },
              {
                "k": "code",
                "text": "NIKA-COMP-001..004"
              },
              {
                "k": "text",
                "text": ")"
              }
            ],
            [
              {
                "k": "code",
                "text": "crates/nika-pack/pack/examples/10-compose-pipeline.nika.yaml"
              },
              {
                "k": "text",
                "text": " + "
              },
              {
                "k": "code",
                "text": "10-compose-child.nika.yaml"
              },
              {
                "k": "text",
                "text": " — the taught lesson (spec "
              },
              {
                "k": "code",
                "text": "examples/"
              },
              {
                "k": "text",
                "text": ", vendored)"
              }
            ],
            [
              {
                "k": "text",
                "text": "Live run 2026-07-29: parent+child journals under "
              },
              {
                "k": "code",
                "text": ".nika/traces/"
              },
              {
                "k": "text",
                "text": ", "
              },
              {
                "k": "code",
                "text": "nika trace verify"
              },
              {
                "k": "text",
                "text": " rc=0 each, typed output "
              },
              {
                "k": "code",
                "text": "{\"brief\":{\"chars\":76,…}}"
              },
              {
                "k": "text",
                "text": " flowed child→parent"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives considered",
          "id": "alternatives-considered"
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt A — claim \"proven\" now (9/9 demonstrated)",
          "id": "alt-a--claim-proven-now-99-demonstrated"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected AGAIN on 2026-07-30, for a new reason. The original rejection was\n\"two conditions have zero demonstration\"; that is no longer true. But two\nrows hold at a tier BELOW the one spec 14 names (condition 8 at "
            },
            {
              "k": "code",
              "text": "def_hash"
            },
            {
              "k": "text",
              "text": "\nrather than the semantic IR · condition 7 at chain-commit rather than the\nspec-15 receipt ladder), and \"9 of 9 demonstrated\" would read as the\nceiling. Counting rows is not the same as meeting them."
            }
          ]
        },
        {
          "k": "h",
          "depth": 3,
          "text": "Alt B — put the receipt in the spec repo",
          "id": "alt-b--put-the-receipt-in-the-spec-repo"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Rejected: the receipt is an ENGINE artifact about ONE implementation. The\nspec defines the conditions and stays engine-agnostic; each implementation\npublishes its own receipt."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Related",
          "id": "related"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "ADR-099 (durable-lite resume — the surface law 10 will compose with)"
              }
            ],
            [
              {
                "k": "text",
                "text": "ADR-108 (nika-proof split — the receipt ladder above condition 7)"
              }
            ],
            [
              {
                "k": "code",
                "text": "spec 14-composition.md"
              },
              {
                "k": "text",
                "text": " — the contract this receipt answers"
              }
            ],
            [
              {
                "k": "code",
                "text": "spec scripts/oracle-differential.py"
              },
              {
                "k": "text",
                "text": " — the two-oracle harness for condition 9"
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Notes",
          "id": "notes"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Re-issue this receipt (same id, new date, updated table) when any of: the\nrun-level metered cut lands (condition 4's residual) · W6's semantic IR\nreaches resume (condition 8 climbs from the "
            },
            {
              "k": "code",
              "text": "def_hash"
            },
            {
              "k": "text",
              "text": " tier) · a verb\nverifies a trace FOREST (condition 7's gap) · the spec-15 receipt ladder\ncomposes over nested runs (condition 7's tier) · any "
            },
            {
              "k": "code",
              "text": "NIKA-COMP"
            },
            {
              "k": "text",
              "text": " semantics\nchange · any change to the verify ladder's classes (the rows are read\nthrough it)."
            }
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The word \"proven\" unlocks when conditions 7 and 8 reach spec 14's own\ntiers — not when the row count reaches nine."
            }
          ]
        }
      ]
    },
    {
      "id": "ADR-110",
      "file": "adr-110-nika-cli-host-member-split.md",
      "sections": [
        {
          "title": "Context",
          "anchor": "context"
        },
        {
          "title": "Decision",
          "anchor": "decision"
        },
        {
          "title": "Alternatives rejected",
          "anchor": "alternatives-rejected"
        },
        {
          "title": "Consequences",
          "anchor": "consequences"
        },
        {
          "title": "Security boundary",
          "anchor": "security-boundary"
        },
        {
          "title": "Rollback",
          "anchor": "rollback"
        }
      ],
      "words": 404,
      "sha256": "50afb4923818699ceeb1fb7a2b34331c26300509851e236d84bdbfb272ca9fba",
      "tokens": [
        {
          "k": "h",
          "depth": 1,
          "text": "ADR-110: nika-cli size-cap member split — nika-cli-host",
          "id": "adr-110-nika-cli-size-cap-member-split--nika-cli-host"
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Context",
          "id": "context"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "The trust-experience arc (2026-07-31 · 22 P0 closures) grew "
            },
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": "\npast the 15,000 prod-LOC Diamond invariant: vector 24 measured 17,789\nat the pre-push gate. The cap is a locked maintainability budget\n(nika-invariants.md), not advisory. Two descents already set the\npattern: the display surface to "
            },
            {
              "k": "code",
              "text": "nika-display"
            },
            {
              "k": "text",
              "text": " (2026-07-10) and the\nrun composer to "
            },
            {
              "k": "code",
              "text": "nika_runtime::compose"
            },
            {
              "k": "text",
              "text": " (2026-07-22) — \"compute\ndescends, render stays.\""
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Decision",
          "id": "decision"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Per D-2026-07-09-N1 (a size-cap split is ONE architectural unit in TWO\nworkspace members), the host-integration plane descends to a new L4\nmember crate "
            },
            {
              "k": "code",
              "text": "nika-cli-host"
            },
            {
              "k": "text",
              "text": ":"
            }
          ]
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "probe"
              },
              {
                "k": "text",
                "text": " (client/kit probes · capability levels · receipts)"
              }
            ],
            [
              {
                "k": "code",
                "text": "wire"
              },
              {
                "k": "text",
                "text": " (writers · preview/detected · the clap "
              },
              {
                "k": "code",
                "text": "WireTarget"
              },
              {
                "k": "text",
                "text": " seam)"
              }
            ],
            [
              {
                "k": "code",
                "text": "doctor"
              },
              {
                "k": "text",
                "text": " (the receipt surface)"
              }
            ],
            [
              {
                "k": "code",
                "text": "welcome"
              },
              {
                "k": "text",
                "text": " (the front-door glance + SAMPLE)"
              }
            ],
            [
              {
                "k": "code",
                "text": "clients_registry"
              },
              {
                "k": "text",
                "text": " + the vendored "
              },
              {
                "k": "code",
                "text": "data/clients.registry.yaml"
              }
            ],
            [
              {
                "k": "code",
                "text": "context_envelope"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "retention"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "metrics"
              },
              {
                "k": "text",
                "text": " · "
              },
              {
                "k": "code",
                "text": "text"
              }
            ],
            [
              {
                "k": "code",
                "text": "output"
              },
              {
                "k": "text",
                "text": " (spec §4 exit codes · "
              },
              {
                "k": "code",
                "text": "VerbOutput"
              },
              {
                "k": "text",
                "text": " · the OSC-8 link seam)"
              }
            ]
          ]
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": " re-exports every public item at its historical path\n("
            },
            {
              "k": "code",
              "text": "verbs::{doctor,probe,welcome,wire}"
            },
            {
              "k": "text",
              "text": " · "
            },
            {
              "k": "code",
              "text": "verbs::{VerbOutput, exit}"
            },
            {
              "k": "text",
              "text": " ·\n"
            },
            {
              "k": "code",
              "text": "metrics"
            },
            {
              "k": "text",
              "text": " · …), so call sites and the bin dispatch stay untouched. The\n"
            },
            {
              "k": "code",
              "text": "local-infer"
            },
            {
              "k": "text",
              "text": " feature forwards member-to-member. Tests coupled to\ncli-side fixtures (retention×trace-store · welcome-SAMPLE×check)\nrelocate cli-side and exercise the re-exported paths."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Alternatives rejected",
          "id": "alternatives-rejected"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "code",
                "text": "LOC-EXEMPT"
              },
              {
                "k": "text",
                "text": " markers: the whitelist (codegen · lookup-table ·\nenum-mega) does not cover organic verb growth; exempting would erode\nthe ratchet."
              }
            ],
            [
              {
                "k": "text",
                "text": "A logic/verb split inside one crate: does not move prod LOC out of\nthe crate; churns every verb body for no budget gain."
              }
            ],
            [
              {
                "k": "text",
                "text": "Blocking the arc on a redesign: the cap exists to force exactly this\nmechanical descent at the moment of overflow."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Consequences",
          "id": "consequences"
        },
        {
          "k": "list",
          "ordered": false,
          "items": [
            [
              {
                "k": "text",
                "text": "Vector 24 returns GREEN ("
              },
              {
                "k": "code",
                "text": "nika-cli"
              },
              {
                "k": "text",
                "text": " ≈ 13.5k prod incl. the #771\nmerge; "
              },
              {
                "k": "code",
                "text": "nika-cli-host"
              },
              {
                "k": "text",
                "text": " ≈ 4.5k) with real headroom."
              }
            ],
            [
              {
                "k": "code",
                "text": "nika-cli-host"
              },
              {
                "k": "text",
                "text": " is a member of the "
              },
              {
                "k": "code",
                "text": "nika-cli"
              },
              {
                "k": "text",
                "text": " unit, not a new\narchitectural unit: same L4 row, "
              },
              {
                "k": "code",
                "text": "publish = false"
              },
              {
                "k": "text",
                "text": ", one public\nsurface re-exported by the operator crate."
              }
            ],
            [
              {
                "k": "text",
                "text": "New "
              },
              {
                "k": "code",
                "text": "crates/nika-cli-host/public-api.txt"
              },
              {
                "k": "text",
                "text": " baseline joins the diff\ngate; "
              },
              {
                "k": "code",
                "text": "crates/nika-cli/public-api.txt"
              },
              {
                "k": "text",
                "text": " re-baselines (items become\nre-exports)."
              }
            ],
            [
              {
                "k": "text",
                "text": "The next growth inside "
              },
              {
                "k": "code",
                "text": "nika-cli"
              },
              {
                "k": "text",
                "text": " should descend "
              },
              {
                "k": "code",
                "text": "welcome"
              },
              {
                "k": "text",
                "text": "'s glance\ndata walks before the wall bites again."
              }
            ]
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Security boundary",
          "id": "security-boundary"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "None moved: "
            },
            {
              "k": "code",
              "text": "nika guard"
            },
            {
              "k": "text",
              "text": " and the run path stay in "
            },
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": "; the\nmember carries probes/writers/render-adjacent surfaces only. The\nengine's authority model (permits · cost · secrets · trace) is\nuntouched."
            }
          ]
        },
        {
          "k": "h",
          "depth": 2,
          "text": "Rollback",
          "id": "rollback"
        },
        {
          "k": "p",
          "inline": [
            {
              "k": "text",
              "text": "Reverse "
            },
            {
              "k": "code",
              "text": "git mv"
            },
            {
              "k": "text",
              "text": " + drop the member from workspace "
            },
            {
              "k": "code",
              "text": "members"
            },
            {
              "k": "text",
              "text": " and\n"
            },
            {
              "k": "code",
              "text": "nika-cli"
            },
            {
              "k": "text",
              "text": " deps; the re-export shim keeps the surface stable in both\ndirections."
            }
          ]
        }
      ]
    }
  ]
}
