Skip to content

Your First Step

Runnable example

Try the Prompt Diff Quickstart — a single file you can run to see replay + diff in action.

Wrap one function, get replay and diff. No workflows, no queues, no complexity.

Install

bash
bun add verist zod
bash
npm install verist zod

Define a step

A step is a function with typed input/output and audit events.

ts
import { z } from "zod";
import { defineStep, run } from "verist";

const verifyDocument = defineStep({
  name: "verify-document",
  input: z.object({ docId: z.string(), text: z.string() }),
  output: z.object({
    verdict: z.enum(["accept", "reject"]),
    confidence: z.number(),
  }),
  run: async (input, ctx) => {
    const verdict = await ctx.adapters.llm.verify(input.text);
    return {
      output: { verdict, confidence: 0.84 },
      events: [{ type: "document_verified", payload: { docId: input.docId } }],
    };
  },
});

Run it

ts
const result = await run(
  verifyDocument,
  { docId: "doc-1", text: "Invoice #1042 for ACME Corp." },
  {
    adapters: {
      llm: {
        verify: async (text) => (text.includes("fraud") ? "reject" : "accept"),
      },
    },
  },
);

if (result.ok) {
  console.log(result.value.output);
  // { verdict: "accept", confidence: 0.84 }
}

Add replay + diff

Capture the output as a snapshot, then recompute with a new model to see what changed:

ts
import { createSnapshotFromResult, recompute, formatDiff } from "verist";

if (!result.ok) throw new Error(result.error.message);

const snapshot = await createSnapshotFromResult(result.value);

// Recompute with a different adapter
const recomputeResult = await recompute(snapshot, verifyDocument, {
  adapters: { llm: { verify: async () => "reject" } }, 
});

if (recomputeResult.ok) {
  const { status, outputDiff } = recomputeResult.value;
  console.log("Status:", status); // "clean" | "value_changed" | "schema_violation"
  if (outputDiff && !outputDiff.equal) {
    console.log(formatDiff(outputDiff));
    // Shows exactly which fields changed
  }
}

What you get

FeatureDescription
Typed I/OZod schemas validate input and output
Audit eventsStructured records for every execution
ReplayReproduce past runs from stored artifacts
DiffSee exactly what changes with new models/prompts

When to add explicit identity

run() uses defaults for workflow identity (workflowId = step name, version = "0.0.0"). Pass explicit values when you need:

  • Stable workflow IDs across deployments
  • Version tracking across prompt/model changes
  • Multi-step workflows with typed commands
  • State persistence with @verist/storage-pg
ts
const result = await run(verifyDocument, input, {
  adapters,
  workflowId: "verify-document",
  workflowVersion: "1.0.0",
  runId: crypto.randomUUID(),
});

Most teams never need overlays or contradiction handling. Verist is useful even if you stop at replay + diff.

LLM context: llms.txt · llms-full.txt
Released under the Apache 2.0 License.