<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hriday Vig</title>
    <description>The latest articles on DEV Community by Hriday Vig (@vighriday).</description>
    <link>https://dev.to/vighriday</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3938816%2F12b7188d-172b-4306-80f7-3df6ac764729.png</url>
      <title>DEV Community: Hriday Vig</title>
      <link>https://dev.to/vighriday</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vighriday"/>
    <language>en</language>
    <item>
      <title>I built a workflow-aware verification layer for AI coding agents — open source, MCP-native</title>
      <dc:creator>Hriday Vig</dc:creator>
      <pubDate>Tue, 19 May 2026 13:30:00 +0000</pubDate>
      <link>https://dev.to/vighriday/i-built-a-workflow-aware-verification-layer-for-ai-coding-agents-open-source-mcp-native-10df</link>
      <guid>https://dev.to/vighriday/i-built-a-workflow-aware-verification-layer-for-ai-coding-agents-open-source-mcp-native-10df</guid>
      <description>&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;Autonomous coding agents are good at writing code. They are bad at knowing &lt;strong&gt;what's actually risky&lt;/strong&gt; about the code they just wrote.&lt;/p&gt;

&lt;p&gt;I built &lt;strong&gt;&lt;a href="https://github.com/vighriday/Veris" rel="noopener noreferrer"&gt;Veris&lt;/a&gt;&lt;/strong&gt; -- an MCP-native verification intelligence layer. You point it at a repo, it builds a behavioral graph, groups functions into semantic workflows (Authentication, Payments, Webhooks, Caching, Queue, etc.), and emits &lt;em&gt;concrete adversarial probes&lt;/em&gt; per workflow.&lt;/p&gt;

&lt;p&gt;Install + run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx veris-core analyze
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Open the HTML dashboard, see what could break.&lt;/p&gt;

&lt;p&gt;MIT. Sponsor-funded. No telemetry. Local SQLite. &lt;a href="https://github.com/vighriday/Veris" rel="noopener noreferrer"&gt;Repo here.&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;Every coding agent I use -- Claude Code, Cursor, Aider, you name it -- has the same blindspot.&lt;/p&gt;

&lt;p&gt;I ask it to "add a new Stripe charge flow." It writes the code, runs the unit tests, says it's done. Tests pass. The PR merges. Three days later, prod has duplicate charges because there's no idempotency key.&lt;/p&gt;

&lt;p&gt;The agent didn't miss a &lt;em&gt;bug&lt;/em&gt;. It missed an entire &lt;em&gt;category&lt;/em&gt; of failure that doesn't show up in unit tests: idempotency under retry. There is no test for "the same request hits us twice in 500ms because of network retry." Until prod surfaces it.&lt;/p&gt;

&lt;p&gt;Veris exists to make that category visible &lt;strong&gt;before&lt;/strong&gt; the PR merges.&lt;/p&gt;




&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Behavioral graph
&lt;/h3&gt;

&lt;p&gt;Veris parses every TS/JS/JSX/TSX/MJS/CJS file in your repo via &lt;code&gt;ts-morph&lt;/code&gt;. For each, it extracts symbols (classes, functions, methods, top-level arrow assignments, &lt;code&gt;module.exports.X = function&lt;/code&gt;, &lt;code&gt;Foo.prototype.method = function&lt;/code&gt;) and resolves cross-module imports + invocations into edges.&lt;/p&gt;

&lt;p&gt;On Express (141 files) it produces 93 nodes and 71 edges in under a second. On Next.js (2,444 files) it builds the graph in the same timeframe via basename pre-indexing and local-import filtering.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;nodes  = symbols (Class / Method / Function)
edges  = DependsOn (file import) | Invokes (call)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Semantic workflow grouping
&lt;/h3&gt;

&lt;p&gt;This is the moat. Raw graphs are noise. Veris classifies each node into one of 25 workflow domains using a weighted vote across three signals:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Path tokens&lt;/strong&gt; -- &lt;code&gt;src/payments/charge.ts&lt;/code&gt; becomes strongly Payments.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Import tokens&lt;/strong&gt; -- &lt;code&gt;import stripe from 'stripe'&lt;/code&gt; becomes Payments-adjacent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Symbol tokens&lt;/strong&gt; -- &lt;code&gt;processPayment&lt;/code&gt;, &lt;code&gt;refund&lt;/code&gt; match via word-boundary + camelCase.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exact-segment path matches outrank import-token matches, which outrank symbol matches. Test/sample/fixture dirs get a 30% multiplier so a real &lt;code&gt;src/auth/login.ts&lt;/code&gt; always beats &lt;code&gt;tests/auth.spec.ts&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rules live in &lt;code&gt;data/workflow-rules.json&lt;/code&gt;. Override per repo at &lt;code&gt;.veris/data/workflow-rules.json&lt;/code&gt;. Add new domains via &lt;code&gt;.veris/plugins/*.js&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Adversarial probe templates
&lt;/h3&gt;

&lt;p&gt;Each workflow has a deck of concrete probes. Examples:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Payments:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Submit charge twice with the same idempotency key inside a 500ms window. &lt;em&gt;Expected: exactly one ledger entry; second call returns the first result.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;Capture succeeds at gateway, response times out before reaching us. &lt;em&gt;Expected: reconciliation eventually marks order paid; no orphan charge.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Webhooks:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replay a 24-hour-old signed payload with the original signature. &lt;em&gt;Expected: replay rejected by timestamp window even though signature is valid.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Caching:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mass cache expiry triggers thundering herd on origin. &lt;em&gt;Expected: single-flight or jittered refresh; origin not overwhelmed.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Veris never &lt;em&gt;runs&lt;/em&gt; the probe. It emits the directive. Your agent (or human) runs it and calls &lt;code&gt;report_execution&lt;/code&gt; via MCP to feed results back into the confidence model.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Behavioral drift detection
&lt;/h3&gt;

&lt;p&gt;Veris fingerprints each workflow (SHA-256 of sorted edges + members + key signals) and stores fingerprints in &lt;code&gt;.veris/state.db&lt;/code&gt;. Run again later -- drift is the diff of fingerprints. A workflow that silently changed (member set identical, edges shifted) is the most dangerous kind because nobody's looking.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Confidence model
&lt;/h3&gt;

&lt;p&gt;Per-workflow risk score = weighted blast radius + runtime criticality + dependency fragility. Math weights live in &lt;code&gt;data/risk-config.json&lt;/code&gt;. Every number visible and explainable. Confidence decays with a 14-day half-life; execution feedback restores it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you actually see
&lt;/h2&gt;

&lt;p&gt;I ran Veris on a self-contained synthetic app with 17 planted bugs across 11 workflows. (&lt;a href="https://github.com/vighriday/Veris/tree/main/examples/demo-app" rel="noopener noreferrer"&gt;Demo app + ground truth here.&lt;/a&gt;)&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Planted bug&lt;/th&gt;
&lt;th&gt;Workflow detected&lt;/th&gt;
&lt;th&gt;Probe fired&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;JWT expiry check uses &lt;code&gt;&amp;lt;&lt;/code&gt; not &lt;code&gt;&amp;lt;=&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Authentication&lt;/td&gt;
&lt;td&gt;"Refresh token at the exact expiry boundary while two requests in flight"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Stripe charge has no idempotency key&lt;/td&gt;
&lt;td&gt;Payments&lt;/td&gt;
&lt;td&gt;"Submit charge twice with the same idempotency key inside a 500ms window"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Webhook handler not idempotent&lt;/td&gt;
&lt;td&gt;Webhooks&lt;/td&gt;
&lt;td&gt;"Sender delivers 50 retries of the same event id within 1 minute"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;updateProduct&lt;/code&gt; doesn't invalidate cache&lt;/td&gt;
&lt;td&gt;Caching&lt;/td&gt;
&lt;td&gt;"Invalidation event arrives out of order with the write"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Worker side-effects not idempotent&lt;/td&gt;
&lt;td&gt;Queue&lt;/td&gt;
&lt;td&gt;"Worker crashes after side effect but before ack"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;N+1 in &lt;code&gt;getOrdersWithItems&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Persistence&lt;/td&gt;
&lt;td&gt;"Two transactions update the same row; commit order non-deterministic"&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;/admin/users&lt;/code&gt; has no auth middleware&lt;/td&gt;
&lt;td&gt;Routing&lt;/td&gt;
&lt;td&gt;"Middleware order changes -- unauthenticated request reaches handler"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every planted bug got a matching probe. Veris doesn't read function bodies to find &lt;code&gt;&amp;lt;&lt;/code&gt; vs &lt;code&gt;&amp;lt;=&lt;/code&gt; -- it surfaces the workflow and the probe directive. The agent (or human) runs the probe.&lt;/p&gt;




&lt;h2&gt;
  
  
  Validated on real OSS repos
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repo&lt;/th&gt;
&lt;th&gt;Nodes&lt;/th&gt;
&lt;th&gt;Edges&lt;/th&gt;
&lt;th&gt;Workflows&lt;/th&gt;
&lt;th&gt;Probes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Express&lt;/td&gt;
&lt;td&gt;93&lt;/td&gt;
&lt;td&gt;71&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Next.js&lt;/td&gt;
&lt;td&gt;2,400+&lt;/td&gt;
&lt;td&gt;~30k&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Prisma&lt;/td&gt;
&lt;td&gt;3,696&lt;/td&gt;
&lt;td&gt;25,046&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NestJS&lt;/td&gt;
&lt;td&gt;3,712&lt;/td&gt;
&lt;td&gt;31,890&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Strapi&lt;/td&gt;
&lt;td&gt;6,982&lt;/td&gt;
&lt;td&gt;40,027&lt;/td&gt;
&lt;td&gt;21&lt;/td&gt;
&lt;td&gt;25&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Each one surfaced real bugs in Veris itself that I then fixed. The shakedown is the dev loop -- running Veris on Express revealed missing CommonJS extraction; Next.js revealed an O(N^2) edge explosion; Prisma revealed a Windows MAX_PATH crash; NestJS revealed AI false positives from CLI prompt scaffolding. All shipped fixes are in the CHANGELOG.&lt;/p&gt;




&lt;h2&gt;
  
  
  MCP integration
&lt;/h2&gt;

&lt;p&gt;17 tools exposed via stdio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;analyze_repository        export_behavioral_graph    analyze_pr_behavior
generate_verification_plan identify_unverified_behaviors
list_workflows            analyze_workflow           detect_drift
generate_adversarial_probes allocate_budget          what_if_revert
report_execution          confidence_history         node_history
export_onboarding         cross_repo_snapshot        register_repo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wire into any MCP-compatible client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"veris"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"veris-core"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mcp"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask the agent: &lt;em&gt;"List the workflows in this repo affected by my current PR. For the highest-risk one, give me the adversarial probes I should run before merging."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Also discoverable via the official &lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP Registry&lt;/a&gt; as &lt;code&gt;io.github.vighriday/veris&lt;/code&gt;, and via &lt;code&gt;npx skills add vighriday/Veris&lt;/code&gt; for the skills.sh ecosystem.&lt;/p&gt;




&lt;h2&gt;
  
  
  Privacy + posture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;MIT.&lt;/strong&gt; No paid tier. No license gating. No telemetry endpoints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;VERIS_STATE_DISABLED=1&lt;/code&gt;&lt;/strong&gt; for zero-retention mode (skips all SQLite writes).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Local-first.&lt;/strong&gt; No network calls. State lives at &lt;code&gt;&amp;lt;projectRoot&amp;gt;/.veris/state.db&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No analytics.&lt;/strong&gt; No phone-home.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Funding: GitHub Sponsors when I get them. Until then, my own time.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;More language adapters (Python next, then Go).&lt;/li&gt;
&lt;li&gt;More workflow domains via community plugins (&lt;code&gt;.veris/plugins/*.js&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Tighter Cursor integration.&lt;/li&gt;
&lt;li&gt;Public registry of community probe libraries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this resonates: star the &lt;a href="https://github.com/vighriday/Veris" rel="noopener noreferrer"&gt;repo&lt;/a&gt;, file issues with your false positives, or contribute a workflow rule.&lt;/p&gt;

&lt;p&gt;If you find a planted bug in the demo app that Veris missed -- open an issue with the workflow + missing probe. That's the loop.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/vighriday/Veris" rel="noopener noreferrer"&gt;github.com/vighriday/Veris&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NPM:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/veris-core" rel="noopener noreferrer"&gt;npmjs.com/package/veris-core&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MCP Registry:&lt;/strong&gt; &lt;code&gt;io.github.vighriday/veris&lt;/code&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>opensource</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
