Write rules
Use semantic() to tell EDcheck what a “good value” means.
A rule is a sentence that describes what you want. It is not validation code. It is an instruction for the AI.
The short form (yes or no)
If you pass a string, EDcheck asks a yes/no question. This is called a Noul rule.
fullName: semantic("A plausible full name for a real person")That is enough to start. Write in your users’ language. Be concrete.
The full form
If you want more control, pass an object.
bio: semantic({ intent: "A meaningful professional biography", valid: "Describes the person's work or background", invalid: "Meaningless text or something clearly unrelated", severity: "warning", message: "The bio is unclear or does not talk about the person", id: "bio_quality",})| Field | What it is for |
|---|---|
intent | The question. Required. |
valid | An example of “this is good”. It helps the AI. |
invalid | An example of “this is bad”. |
severity | error (flips success), warning, or info. Default is error. |
message | The text your app will see on the issue. If you skip it, EDcheck uses a generic one. |
id | A stable name for this rule. If you skip it, the field path is used (fullName). |
When it is not just yes or no
Sometimes you want a scale: bad, okay, good. Then use kind: "score" and a list of levels.
description: semantic({ kind: "score", intent: "How well does the description explain the software project?", levels: [ { label: "meaningless", description: "Random, spam-like, or unrelated text", outcome: "fail", }, { label: "vague", description: "On topic, but too vague to act on", outcome: "warning", }, { label: "clear", description: "You can tell what to build or which problem it solves", outcome: "pass", }, ],})The winning level is the one with the highest probability. If there is a tie, the first one in the list wins.
If the AI is not sure enough (minConfidence, default 0.6), the outcome becomes warning even if the winning level was a pass or a fail.
Use Noul (yes/no) for “is this a name?”. Use Score when the degree matters: vague vs clear.
Where the rules go
In define, each key in rules is a field name from the schema. That field must exist on the Zod object.
const UserSemantic = edcheck.define(User, { rules: { fullName: semantic("A plausible full name for a real person"), bio: semantic("A coherent professional biography"), },});You do not need a rule on every field. Zod already owns the shape. EDcheck only steps in where meaning matters.