Skip to content

feat: verification — inline guardrails + async judge (Phase 5)#7

Merged
MANVENDRA-github merged 1 commit into
mainfrom
feat/phase-5-verification
Jun 27, 2026
Merged

feat: verification — inline guardrails + async judge (Phase 5)#7
MANVENDRA-github merged 1 commit into
mainfrom
feat/phase-5-verification

Conversation

@MANVENDRA-github

Copy link
Copy Markdown
Owner

Phase 5 — Verification: Guardrails + Judge (C6)

Adds the in-path correctness layer that is Sentinel's wedge vs LiteLLM/Helicone/Langfuse: it can flag or block a bad response before it returns, and score sampled responses out of band.

What's new

  • Inline guardrails (deterministic, fail-closed) — on non-streaming responses: JSON validity + a minimal JSON-Schema match (when the request asked for JSON), plus a policy/PII engine (verify/policy.ts): email, Luhn-checked credit cards, SSN, phone, IPv4 (octet-validated), API-key-like tokens, a configurable content blocklist, and refusal detection. Aggregated to pass | flag | block. A check that throws fails closed (blocks), never passes. Traces store violation category codes only (e.g. pii.email) — never the matched value.
  • Async LLM judge (sampled)verify/judge.ts calls a local Ollama model; the response is wrapped behind delimiters as untrusted DATA so it can't talk the judge into a pass, and the {score 1–5, reason} verdict is parsed defensively. It runs out of band (never adds response latency); a judge failure is recorded as "unscored", never a pass.
  • Regression tracking — every request carries a model-independent prompt fingerprint; GET /regression groups judge scores by (prompt, model) so you can compare a prompt's quality across models/versions.
  • Verdicts on the traceguardrailStatus, guardrailViolations, judgeScore, judgeReason, judgeError, promptFingerprint, queryable via GET /traces?guardrailStatus=block / ?judgeScoreMax=2 / ?promptFingerprint=…. The async judge attaches via a new store.attachVerdict(spanId, …) after the span is exported.

Decisions (locked with maintainer)

  • Full policy/PII engine now (not just format/schema).
  • Flag by default, GUARDRAILS_BLOCK=true to return 422; guardrail execution errors always fail closed.
  • Build the comparison capability now (/regression computed from the trace store — no second persistence layer).

Architecture notes

  • Inline guardrails are non-streaming only (a stream is committed before its body is known — same boundary as Phase 4's pre-hijack rule); streamed responses are judged from their buffered output after completion.
  • The judge runs after the span ends, relying on the synchronous SimpleSpanProcessor export, then updates the row via attachVerdict.
  • No verifier configured ⇒ unchanged Phase 0–4 pass-through (gated by VERIFY_ENABLED / JUDGE_ENABLED, both off by default). New modules live under verify/; the ProviderRegistry/handler contracts are untouched.

Security — SR-005 (area 2)

Ticks prompt-injection / judge-integrity boxes 2.1–2.3: content treated as data not instructions; judge prompt isolates the response and degrades to "unscored"; guardrail policy comes from env/config only (request content can't disable it); traces never persist the matched PII/secret.

Tests / verification

pnpm verify green. 190 tests, 98% coverage (≥90% gate). New unit suites for policy, schema, guardrails, judge (prompt isolation + defensive parse), verifier (sampling, attach, fail-open, drain) and regression; server integration tests cover guardrail flag (trace) / block (422), judge verdict + "unscored" on failure, streamed-response judging, and the /regression endpoint.

Add the in-path correctness layer: deterministic guardrails that flag (or
block) a bad response before it returns, plus an async, sampled local-Ollama
judge whose 1-5 verdict is attached to the trace out of band.

- verify/policy.ts: PII (email, Luhn-checked cards, SSN, phone, IPv4 with
  octet validation, API-key tokens) + a configurable content blocklist +
  refusal detection. Returns category codes only, never the matched value.
- verify/schema.ts: a small deterministic JSON-Schema subset validator.
- verify/guardrails.ts: JSON-validity + schema match (when JSON is requested)
  + policy/PII, aggregated to pass/flag/block. Fails closed — a check that
  throws blocks, never passes.
- verify/judge.ts: local-Ollama judge; the response is wrapped as delimited,
  untrusted DATA so it can't talk the judge into a pass; verdict parsed
  defensively (clamped 1-5).
- verify/verifier.ts: orchestrates sync inspect() + fire-and-forget sampled
  scheduleJudge() (attaches via store.attachVerdict) + drain() for shutdown.
- verify/regression.ts + GET /regression: group judge scores by
  (promptFingerprint, model) to compare a prompt's quality across models.
- Traces gain guardrailStatus/guardrailViolations/judgeScore/judgeReason/
  judgeError/promptFingerprint (trace.ts, both stores + attachVerdict, query
  API). New GuardrailBlockedError (422). Config: VERIFY_ENABLED/
  GUARDRAILS_BLOCK/JUDGE_ENABLED/JUDGE_MODEL/JUDGE_SAMPLE_RATE + a guardrails
  block in sentinel.config.json. Inline guardrails are non-streaming only;
  streams are judged from buffered output. No verifier configured = unchanged.

Docs: README "Verification", .env.example, config example, SR-005 (ticks
prompt-injection/judge-integrity boxes 2.1-2.3). 190 tests, 98% coverage;
pnpm verify green.
@MANVENDRA-github
MANVENDRA-github merged commit c0fca40 into main Jun 27, 2026
1 check passed
@MANVENDRA-github
MANVENDRA-github deleted the feat/phase-5-verification branch June 27, 2026 15:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant