Skip to content

Architecture

Michael Kinney edited this page Oct 15, 2026 · 1 revision

Architecture

A Rust workspace (edition 2024, MSRV 1.90) of eleven crates under crates/, organized in planes where dependency arrows only ever point toward core.

Related pages: Engine (the core crate's behavior) · Decisions (the IR) · Interfaces (the five interface crates) · Contributing (the gates below as a workflow).

Crate table

Crate Plane Owns
opencodifier-core decision core canonical IR: validating constructors, Distribution, ConfidenceReport + outcome routing, stable ir.* error codes, trace_version, #[non_exhaustive] enums. Pure — no local deps
opencodifier-schema adapters the only place wire formats are named: native (identity) / OpenAI structured outputs / Anthropic tool schemas / Jev/System One, plus the DecisionDefinition registry. Byte-locked fixtures under fixtures/
opencodifier-engine deterministic engine DAG executor, rule engine, metadata + hand-written BM25 narrowing, exact-decision cache (CacheKeyBuilder, D6), relational solver, Calibration + LadderPolicy seams, cross-rung escalation, focused extraction, optimizer, EngineHandle facade, the transport-neutral wire module
opencodifier-runtime inference runtime InferenceBackend / EmbeddingBackend traits (sync, object-safe, logits-only), validating DenseTensor, mock backends; ort only behind the onnx feature (Kai + Julia ONNX transports live here)
opencodifier-model decision model candidate-conditioned serving contract, EmbeddingClassifier, SHA-256 ModelManifest (D14), the Kai (D33) and Julia (D34) contracts with committed parity fixtures, the NLI verbalization verifier contract, the llama.cpp decision rung (D26, llamacpp feature), shared answer-key rendering
opencodifier-itemgen corpus tooling D37 relational/contrastive item generator: sample → render through the engine's extraction grammar → solver-verify. Corpus emission only — never a serving dependency
opencodifier-http interface axum 0.8 /v1 API (D12 amended), loopback-default bind, error envelope, x-opencodifier-format selection (D30)
opencodifier-cli interface clap v4 binary opencodifier: decide / graph / serve / mcp / models / ladder / recipe, D13 exit codes
opencodifier-mcp interface rmcp 2.2 stdio server, six codify_* tools (D17). Stdio only — local by construction
opencodifier-wasm interface WasmEngine over the zero-ML stack for wasm32-unknown-unknown (D23); dual glue (web + nodejs) in releases
opencodifier-ffi interface C-ABI staticlib over the same wire module (D24); hand-written include/ocffi.h that integration tests re-declare so header and implementation cannot drift

Dependency direction (binding)

cli ──► http ──┐
               ├──► engine ──► core ◄── schema
model ──► runtime ──► core
        └────────► engine
itemgen ──► engine ──► core   (itemgen also declares the direct core edge)
mcp / wasm / ffi take the same engine-facade edge as cli/http

Binding rules (PLANNING.md §39):

  • opencodifier-core must never depend on HTTP, MCP, CLI, Tokio, or any ML runtime.
  • Each heavyweight dependency is confined to exactly one crate (matrix below); no crate may enable another crate's heavyweight feature transitively.
  • The default build of the whole workspace is ML-free and runtime-free.

Sync core, async edges

The core and engine are synchronous — deterministic, testable, trivially embeddable. I/O shapes are traits (Clock, Deadline, CancellationToken, InferenceBackend); async wrappers (Tokio, wasm-bindgen) live in the interface crates. The HTTP/MCP servers own any threading; the engine stays single-threaded per request. Deliberate asymmetry, not inconsistency: a concurrent HTTP server must protect unrelated connections (spawn_blocking off the async workers), while a stdio MCP session is strictly sequential and calls the engine inline.

Wire formats live in schema only

Native IR is the truth; adapters are total functions in both directions with fixture-locked round-trips: native ⇄ OpenAI structured outputs, native ⇄ Anthropic tool schemas, native ⇄ Jev/System One (state/model/questions; score answer = probability-weighted mean index; noul = probability of yes). Where a format cannot express an IR feature, the adapter refuses or discloses (e.g. unsupported_generation_field) — it never pretends.

Feature matrix (D11)

Feature Crate Pulls in
(default) core, engine, schema serde, thiserror, sha2 — nothing heavy
onnx opencodifier-runtime ort =2.0.0-rc.13 (D2 pin; reinstated for the Kai/Julia rungs under D33/D34)
runtime-onnx-dynamic opencodifier-runtime ort load-dynamic (system dylib via ORT_DYLIB_PATH)
llamacpp opencodifier-model ureq (sync HTTP client only, D26)
tokenizers opencodifier-model tokenizers 0.22 + fancy-regex (Kai reference encoder, D33)
http opencodifier-http axum 0.8, tokio
mcp opencodifier-mcp rmcp 2.2 (D1)
cli opencodifier-cli clap v4
wasm opencodifier-wasm wasm-bindgen, getrandom js

Floating-point policy

  • All decision math (softmax, calibration, margins, entropy, weighted score means) is Rust f64 — never inside an ONNX graph — so CPU/GPU/quantization cannot change a decision (D7).
  • ONNX returns logits; Rust converts.
  • Comparisons use total_cmp or explicit tolerances; the float_cmp lint is allowed only in test modules.
  • Cross-implementation float equality is never assumed (measured: ORT vs Rust softmax agree to ~5 ULP, never bit-identical — D2).

Failure posture

  • Malformed or hostile input → typed error with a stable code, never a panic.
  • Unknown wire fields are ignored forward-compatibly — except the native IR, which denies unknown fields.
  • Model artifacts are SHA-256-verified against a manifest before load; models/ is never committed.
  • Default posture is local: bind 127.0.0.1, no outbound calls. See Security.

Workspace lints

unsafe_code forbidden workspace-wide (one documented exception in the FFI crate, where pointer handling is the boundary, pinned by a drift-guard test); missing_docs warned and CI-enforced via -D warnings; clippy all + pedantic with unwrap_used, expect_used, panic, todo!, unimplemented!, dbg_macro, print_stdout denied. Test modules may re-allow unwrap_used, expect_used, panic, float_cmp at module level only. No placeholders anywhere: no TODO/FIXME/stub lands on main. Every public item is rustdoc-documented; anyhow is banned from public APIs; one thiserror error enum per crate with a stable code().

Quality gates (the CI contract)

just ci runs exactly this, and .gitforge.yml mirrors it line-for-line:

cargo fmt --all -- --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace
cargo test -p opencodifier-model --features llamacpp,tokenizers   # feature lane
cargo test --doc
RUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps
cargo deny check          # licenses / advisories / bans / sources
cargo audit               # known-vulnerability audit
aegis --format json scan --file . --baseline .aegis/baseline.json   # 0 new findings

Plus the coverage lane: cargo llvm-cov --workspace --lcov with a floor assertion at 97.5 % (lcov DA basis, measured inside the pinned CI image — the number of record; only an in-image measurement may move the floor, D36). The 298-line residual above the floor is proven class-infeasible and recorded line-by-line in docs/COVERAGE.md.

Not part of just ci (run when the relevant boundary changes): just check-wasm (the wasm32 compile + Node smoke over the real artifact) and just check-macjev (renderer/prep byte parity).

CI platform

GitForge is the CI platform of record (.gitforge.yml); the GitHub Actions config is a mirror, and a red GitHub run is not a code-failure signal. All jobs run on the runner-local opencodifier-ci-rust:2 image (toolchain, clippy/rustfmt, cargo-deny, cargo-audit, aegis at a pinned rev, llvm-cov, warm registry — zero runtime tool installs). The job graph fans out (lint → {test, doc, supply-chain} → e2e) and is safe only because the runner serializes a run's jobs; the file documents the collapse condition. The e2e lane builds the release binary and drives serve + MCP over the real wire.

Clone this wiki locally