Repository navigation
Decisions
Everything OpenCodifier decides is expressed in one canonical intermediate representation (IR), owned by opencodifier-core. Wire formats (OpenAI, Anthropic, Jev/System One) are adapters that normalize into the IR — never the internal representation, and never named outside opencodifier-schema.
Related pages: Confidence (how an answer becomes a routed outcome) · Engine (what produces it) · Interfaces (the transports).
- The three decision kinds
- State, questions, answers
- Policy
- Distributions
- Stable error codes
- Traces
- Resource limits
Every question is one of three internally-tagged ("type") kinds, validated at construction (candidate/level uniqueness and non-emptiness are constructor errors, not runtime surprises):
| Kind | Answer shape | Notes |
|---|---|---|
Choice |
one candidate from a runtime-defined candidate list | candidates may be static in the request or dynamically supplied by the caller |
Boolean |
true/false with probability | the Jev adapter names this noul = probability of yes |
Score |
ordered levels | retains the full distribution over levels plus the expected value — never a bare number |
A single request may carry several questions (the repo's own fixture does: choice + score + boolean together). An empty questions list is a constructor error (ir.empty_questions).
| IR type | Shape | Contract notes |
|---|---|---|
State |
text + BTreeMap<String, FactValue> facts |
canonical (sorted) order is part of the cache-key contract — see Engine |
DecisionQuestion |
Choice / Boolean / Score
|
question text plus candidates or levels |
DecisionAnswer |
full Distribution
|
keys = candidate ids / level ids, normalized within SUM_TOLERANCE = 1e-6
|
DecisionRequest |
state + questions + metadata + policy | the canonical unit every transport decodes into |
DecisionResponse |
answers + confidence + outcome + trace + metrics | machine-readable; no generated prose anywhere |
Facts are tagged enums, not free strings — structural facts ride every view unchanged even when focused extraction shrinks the text (D18).
DecisionPolicy rides on the request, not on the server. There is no server-wide policy override by design — serve --policy validates the file and then reports cli.policy_inapplicable, because pretending a server-wide override existed would be a lie.
Defaults (frozen v1 contract):
| Field | Default |
|---|---|
min_confidence |
0.80 |
verify_below |
0.65 |
abstain_below |
0.50 |
risk |
RiskLevel (Low / Medium / High / Critical) |
Ordering is validated: abstain_below ≤ verify_below ≤ min_confidence.
Policy extensions (all default-absent and byte-identical when unset):
-
boolean_threshold: Option<f64>— answer true whenp_true ≥ threshold; strictly inside(0, 1); the distribution is never rewritten (D25 amendment, 2026-10-08). -
abstain_candidate: Option<AbstainCandidate>— a "none of these" candidate appended to choice questions; electing it is a terminal abstention regardless of confidence (D25 amendment). - Uncertainty gates (§19): entropy ceiling, minimum margin, OOD ceiling — disabled by default.
-
ood_mode—answer_entropy(default) orlexical_band(D28); see Engine.
- Every answer carries the full distribution — a winner alone is a typed refusal, never a synthesized probability.
- Normalization tolerance is
1e-6; anything else isir.distribution_not_normalized. - All decision math (softmax, calibration, margins, entropy, weighted score means) happens in Rust f64 — never inside a model graph — so CPU/GPU/quantization cannot change a decision (D7).
- The lexical classifier's BM25 scores pass through a spread bound before softmax: lexical overlap may never certify better than 99:1 odds (top probability capped below 1.0, D35) — saturated lexical answers escalate instead of self-accepting.
One error enum per crate, each with a code() returning a stable string. Wire surfaces map codes to payloads verbatim; codes are additive-only after v1.
Core (ir.*) codes include:
ir.empty_field ir.empty_candidates ir.empty_questions
ir.duplicate_candidate ir.duplicate_level ir.too_few_levels
ir.invalid_id ir.invalid_policy ir.invalid_probability
ir.distribution_not_normalized
Other prefixes follow the owning crate: schema.*, engine.*, graph.*, cli.*, mcp.*, ffi.*. Malformed or hostile input produces a typed error with a stable code — never a panic.
DecisionTrace { trace_version: u32 = 1, entries } — the explainability story is a deterministic execution trace, never chain-of-thought:
-
trace_versionis frozen at1(D8) and every public enum is#[non_exhaustive], so no consumer can be silently broken. - Entries carry node + sorted detail facts; serialization is byte-stable (
BTreeMapeverywhere). - Gates are never hidden: ladder overrides record
policy_source, rung calibrations recordcalibration_source, escalation recordsrungs_fired+rung_chain, boolean boundaries recordboolean_threshold/boolean_flipped, elicited abstention recordsabstain_candidate/abstain_elicited— each absent when the feature is unconfigured, so unconfigured runs are byte-identical.
Print a trace with decide --trace; the same JSON projection serves MCP codify_explain.
The IR carries a per-request ceiling set (Limits) that the schema validates against: max graph nodes (128), max execution time (120 s — raised from 10 s in D32 after the model rung's measured ~22 s CPU tail killed every long-document escalation), max batch (16), and a 1 MiB HTTP body cap at the socket. Parsers enforce the limits and never panic on malformed input.
OpenCodifier · local-first, deterministic-first decision runtime · Apache-2.0
Releases (current: v0.6.1) · Weights on HuggingFace (aliasfox org) · Live site + playground · Measured benchmark board · Benchmarks
OpenCodifier
Learn
Build
Participate
External