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 verdixQuick 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
| Type | What it is |
|---|---|
Subject | A typed record of applicant attributes. Missing keys yield unknown, never a guessed pass. |
Rule | A declarative, JSON-serializable requirement carrying its provenance (source, clause, confidence). |
Pack | An id, a label, an optional field schema, and a list of rules. This is the domain. |
Verdict | The composite result. It carries a status, a confidence, per-rule CheckResults, and a summary. |
The verdict
A verdict is never a bare boolean. It is one of four states, each with reasons attached:
| Status | Meaning |
|---|---|
pass | All requirements met with sufficient confidence. |
fail | A hard requirement is not met. The blocking clause is named. |
unknown | A required attribute is missing. There is insufficient information to decide. |
review | A 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:
- A hard failure settles the question whatever else is missing. You can say "not eligible" without complete data.
- Absent any disqualification, a missing required input blocks green rather than being assumed.
- Only a
hardrule can producefail; a failingsoftrule is a preference, not a disqualification.
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 → unknowndate
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 reviewergroup
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" arrayAuthor 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.