Skip to main content
Reference

Verdix developer reference

Install the package, evaluate a subject, and read a verdict. Every page example here runs the exact evaluate() the test suite passes against.

Install

Verdix is a small TypeScript package with no dependencies. It runs anywhere JavaScript runs: Node, the browser, or the edge. It never needs a network call or a model to give you an answer.

npm install verdix
The engine ships with two example packs so you can evaluate something on the first line. Real deployments author their own packs, so see Author a pack for how.

Quick start

You give it a subject, which is just a bag of attributes about a person, and a pack, which holds the rules. Back comes a verdict: a status, a confidence, and the evidence behind every rule.

import { evaluate, governmentJobPack } from "verdix";

const verdict = evaluate(
  { age: 28, degreeClass: "2nd", discipline: "economics", quota: "general" },
  governmentJobPack
);

verdict.status;             // "review"
verdict.confidence;         // 0.6
verdict.summary;            // "Needs review: Discipline."
for (const r of verdict.results) {
  console.log(r.status, r.label, r.reason, r.clause, r.confidence);
}

Core concepts

TypeWhat it is
SubjectA typed record of applicant attributes. Missing keys yield unknown, never a guessed pass.
RuleA declarative, JSON-serializable requirement carrying its provenance (source, clause, confidence).
PackAn id, a label, an optional field schema, and a list of rules. This is the domain.
VerdictThe composite result. It carries a status, a confidence, per-rule CheckResults, and a summary.
The engine contains no domain vocabulary. "Government job" vs "scholarship" is entirely a difference of packs data, not code.

The verdict

A verdict is never a bare boolean. It is one of four states, each with reasons attached:

StatusMeaning
passAll requirements met with sufficient confidence.
failA hard requirement is not met. The blocking clause is named.
unknownA required attribute is missing. There is insufficient information to decide.
reviewA rule that can't be honestly formalized, or a low-confidence read. Routed to a human.

Precedence

When results disagree, they fold in a fixed order. The ordering is forced, not stylistic:

FAIL (hard) > UNKNOWN (required) > REVIEW > PASS

Confidence gate

Every rule carries an extraction confidence. A check's confidence is the minimum of its rule and any applied relaxations; the verdict's confidence is the minimum across all checks only as confident as its least-confident input.

A hard rule that passes but was read below the threshold (default 0.7) does not turn the verdict green. It downgrades it to review. This is the mechanism that stops a bad extraction from producing a confident wrong "eligible". Rules already held for review are excluded from the fold: their confidence is a placeholder for a judgement no one has made yet.

evaluate(subject, pack, { confidenceThreshold: 0.85 });

Rule kinds

numeric

A threshold check with optional guarded relaxations. The threshold is either a constant (threshold) or another subject attribute (thresholdAttribute) — exactly one of the two. Each relaxation shifts the effective threshold and carries its own clause and confidence, applying only when its condition matches. A condition is either membership ({ attribute, in }) or a numeric comparison ({ attribute, operator, value }).

{
  kind: "numeric",
  id: "age",
  label: "Maximum age",
  attribute: "age",
  operator: "<=",
  threshold: 30,                    // a constant, OR —
  // thresholdAttribute: "maxAge",  // compare against another subject attribute
  relaxations: [
    {
      adjust: 5,                    // raises the effective threshold to 35
      when: { attribute: "service", operator: ">", value: 10 },
      provenance: { clause: "7(b)", confidence: 0.85 }
    }
  ]
};

See the rule kinds section on the home page for a worked example of each.

set

Membership. The attribute must be in or notIn a set (case-insensitive for strings).

{
  kind: "set",
  id: "field",
  label: "Field",
  attribute: "discipline",
  membership: "in",               // or "notIn"
  values: ["engineering", "sciences", "economics", "statistics"],
  provenance: { clause: "3.4", confidence: 0.8 }
};
// subject: { discipline: "Economics" } → pass (case-insensitive)
// missing value → unknown

date

A recency check: the attribute, read as a date (ISO string or epoch millis), must fall within the last years years of the evaluation clock. Missing or unparseable values resolve to unknown — never a guessed pass.

{
  kind: "date",
  id: "degree-age",
  label: "Degree recency",
  attribute: "degreeDate",
  operator: "withinLast",
  years: 8,
  provenance: { clause: "3.6", confidence: 0.9 }
};
// subject: { degreeDate: "2021-09-01" }  — ISO string or epoch millis
// compared against the evaluation clock (injectable via options.now)

manual

A requirement that can't be reduced to a predicate ("relevant experience"). It always resolves to review, because the engine admits what it can't decide instead of guessing.

{
  kind: "manual",
  id: "relevant-experience",
  label: "Relevant experience",
  note: "relevance of prior experience can't be evaluated automatically",
  provenance: { clause: "3.5", confidence: 0.5 }
};
// no attribute, no predicate — the result is always status: "review"
// with the note carried into the reason for the reviewer

group

A composite: nested rules folded with strong Kleene logic (and / or). An or group satisfied by one branch passes without being blocked by a sibling that wants review; an and group escalates to review when the fold is undecided and any child demanded it. Each child keeps its own full evidence on the group result's children array.

{
  kind: "group",
  id: "merit-route",
  label: "Merit route",
  combinator: "or",                 // or "and"
  rules: [
    { kind: "set", id: "first-division", label: "First division",
      attribute: "degreeClass", membership: "in", values: ["1st"],
      provenance: { clause: "3.3a", confidence: 0.85 } },
    { kind: "numeric", id: "cgpa", label: "CGPA",
      attribute: "cgpa", operator: ">=", threshold: 3.0, unit: "/4",
      provenance: { clause: "3.3b", confidence: 0.9 } }
  ],
  provenance: { clause: "3.3", confidence: 0.85 }
};
// either branch satisfies the route on its own; each child keeps its
// own evidence on the group result's "children" array

Author a pack

A pack is plain data. Keep it in your repo as a versioned, reviewable artifact. That way a diff to eligibility logic is a diff a reviewer can read.

The playground ships with two complete packs you can use as references — the scholarship pack demonstrates every rule kind, including a group merit route and a date recency check. Start by copying one, then rename the fields and rules to match your domain.

Ready to integrate? Verdix is designed to be dropped into any TypeScript project. Start with the example packs, then author your own domain-specific rules.