<?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: AYON ARYAN</title>
    <description>The latest articles on DEV Community by AYON ARYAN (@ayonaryan).</description>
    <link>https://dev.to/ayonaryan</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4026649%2Fe6ad11bf-e602-4432-b2da-b0a5d266b3d1.jpg</url>
      <title>DEV Community: AYON ARYAN</title>
      <link>https://dev.to/ayonaryan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ayonaryan"/>
    <language>en</language>
    <item>
      <title>Executable Contracts as Guardrails for AI-Generated Code</title>
      <dc:creator>AYON ARYAN</dc:creator>
      <pubDate>Mon, 13 Jul 2026 05:23:37 +0000</pubDate>
      <link>https://dev.to/ayonaryan/executable-contracts-as-guardrails-for-ai-generated-code-iip</link>
      <guid>https://dev.to/ayonaryan/executable-contracts-as-guardrails-for-ai-generated-code-iip</guid>
      <description>&lt;h3&gt;
  
  
  Integrating Specmatic into my NL-to-SQL engine (DATABASE-MANAGER)
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Submitted for the Specmatic Full Stack AI Engineering Intern challenge.&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The problem nobody warns you about with AI coding agents
&lt;/h2&gt;

&lt;p&gt;I build with AI coding agents every day — Claude Code, Cursor. They're incredible at velocity. But they have one quiet, expensive failure mode: &lt;strong&gt;they drift.&lt;/strong&gt; You ask for a small change to an endpoint, and somewhere in the diff the agent renames a field, flips a status code, or drops a required parameter. The code still runs. The tests (if you have them) still pass. And then three layers downstream — in the frontend, in a consumer service — something breaks, and you spend an afternoon debugging an integration bug that was never a logic bug at all. It was a &lt;strong&gt;contract&lt;/strong&gt; bug.&lt;/p&gt;

&lt;p&gt;That's the exact problem Specmatic's challenge asked me to explore: &lt;em&gt;can Spec-Driven Development and executable contracts improve AI-assisted software development?&lt;/em&gt; After integrating Specmatic into one of my projects, my answer is an emphatic yes — and the reason is that &lt;strong&gt;an executable contract is a guardrail for AI-generated code.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The project: DATABASE-MANAGER
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/AYON-ARYAN/DATABASE-MANAGER" rel="noopener noreferrer"&gt;DATABASE-MANAGER&lt;/a&gt; is a natural-language-to-SQL engine I built that works across 8 database engines (SQLite, MySQL, PostgreSQL, MSSQL, Oracle, MongoDB, Cassandra, Redis). It has a Flask &lt;code&gt;/api&lt;/code&gt; blueprint consumed by a React frontend, and — importantly — a &lt;strong&gt;human-in-the-loop write-safety model&lt;/strong&gt;: any write/schema command doesn't execute directly. The API returns &lt;code&gt;{ "needs_review": true, ... }&lt;/code&gt;, a human approves, and only then does &lt;code&gt;/api/execute&lt;/code&gt; run it, with snapshot rollback via &lt;code&gt;/api/undo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the project already had a philosophy: &lt;em&gt;don't let the model run unchecked.&lt;/em&gt; Specmatic let me extend that exact philosophy from &lt;strong&gt;database writes&lt;/strong&gt; to the &lt;strong&gt;API contract itself.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1 — The contract becomes the source of truth
&lt;/h2&gt;

&lt;p&gt;I wrote an OpenAPI contract (&lt;code&gt;api_contract.yaml&lt;/code&gt;) describing the real API. The interesting part is the &lt;code&gt;/api/command&lt;/code&gt; response, which is a &lt;em&gt;union&lt;/em&gt; of three shapes — a READ result, a write staged for review, or an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;/api/command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;oneOf&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/ReadResult"&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/NeedsReview"&lt;/span&gt;   &lt;span class="c1"&gt;# { needs_review: true, sql, explanation, task }&lt;/span&gt;
                &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/Error"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This contract is now the single thing both the backend and the frontend agree on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — Executable contract: testing the real API
&lt;/h2&gt;

&lt;p&gt;With Specmatic, the contract isn't documentation that rots — it's &lt;em&gt;executable&lt;/em&gt;. Specmatic auto-generates positive and negative requests from the spec and verifies the live API conforms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/specs"&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /specs &lt;span class="se"&gt;\&lt;/span&gt;
  specmatic/specmatic:latest &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--host&lt;/span&gt; localhost &lt;span class="nt"&gt;--port&lt;/span&gt; 5001
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No hand-written test cases. The spec &lt;em&gt;is&lt;/em&gt; the test suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — Service virtualization: the frontend stops waiting
&lt;/h2&gt;

&lt;p&gt;Specmatic can also &lt;em&gt;be&lt;/em&gt; the API — a spec-conformant stub — so my React frontend can develop with no backend, no database, and no LLM keys running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="nt"&gt;--network&lt;/span&gt; host &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;:/specs"&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; /specs &lt;span class="se"&gt;\&lt;/span&gt;
  specmatic/specmatic:latest stub &lt;span class="nt"&gt;--port&lt;/span&gt; 9000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same contract, two uses: it tests the provider &lt;em&gt;and&lt;/em&gt; mocks it for the consumer. That's contract-driven development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — The payoff: catching AI drift in seconds
&lt;/h2&gt;

&lt;p&gt;Here's the demo that made it click for me. I asked an AI agent to "add a confidence score and rename &lt;code&gt;needs_review&lt;/code&gt; to &lt;code&gt;requiresReview&lt;/code&gt;." It did — silently changing the response shape. To a human reviewer skimming the diff, it looks harmless. To the React frontend, it's a broken integration.&lt;/p&gt;

&lt;p&gt;Then I re-ran the contract test:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Specmatic caught it instantly&lt;/strong&gt; and told me &lt;em&gt;exactly&lt;/em&gt; what drifted. A bug that would have surfaced as a confusing frontend error became a precise, one-line test failure — before it ever left my machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;Building with LLMs taught me a mantra: &lt;em&gt;it's 20% prompting and 80% guardrails.&lt;/em&gt; The interesting engineering is everything you put &lt;strong&gt;around&lt;/strong&gt; the model to make it safe. Specmatic is that idea applied to AI-assisted development itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;For humans:&lt;/strong&gt; the contract is unambiguous truth; no more "wait, what does this endpoint return?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;For AI agents:&lt;/strong&gt; the contract is a fence. The agent can move fast &lt;em&gt;inside&lt;/em&gt; it; the moment it drifts outside, the executable contract fails loudly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a tool like DATABASE-MANAGER — where I already gate database writes behind human review — adding a contract guardrail at the API layer felt like the natural second half of the same idea. Two guardrails, one principle: don't let generated actions run unchecked.&lt;/p&gt;

&lt;p&gt;That's how Spec-Driven Development improves AI-assisted software engineering. It turns the AI's biggest weakness — confident, silent drift — into the cheapest possible signal: a failing test.&lt;/p&gt;

&lt;h2&gt;
  
  
  Going deeper (round 2): examples, resiliency, and virtualizing the LLM
&lt;/h2&gt;

&lt;p&gt;After the first pass I pushed three Specmatic techniques further:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Inline + external examples.&lt;/strong&gt; Instead of letting Specmatic only check &lt;em&gt;shapes&lt;/em&gt;, I added examples that pin &lt;em&gt;values&lt;/em&gt; — so the contract test now verifies real cases: &lt;code&gt;admin1&lt;/code&gt;→&lt;code&gt;ADMIN&lt;/code&gt;, bad creds→&lt;code&gt;Invalid credentials&lt;/code&gt;, and &lt;code&gt;editor1&lt;/code&gt;→&lt;code&gt;EDITOR&lt;/code&gt; (the last loaded as an external example file). One generic test became three value-checked ones, and the stub now returns lifelike data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Schema-resiliency testing found a real bug.&lt;/strong&gt; Turning on generative negative tests, Specmatic mutated the login request — empty body, &lt;code&gt;username&lt;/code&gt; as null / number / boolean — and expected a &lt;code&gt;4xx&lt;/code&gt;. My API returned &lt;code&gt;200&lt;/code&gt; for all of them: it had &lt;strong&gt;no input validation&lt;/strong&gt;, silently treating a malformed body as a failed login. 14 negative tests failed. I added body validation (&lt;code&gt;400&lt;/code&gt; on a non-object body or non-string fields), documented the &lt;code&gt;400&lt;/code&gt;, and re-ran: &lt;strong&gt;24/24 green&lt;/strong&gt;. The resiliency suite turned an invisible robustness gap into a fixed, tested guarantee.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Virtualizing the LLM itself.&lt;/strong&gt; Meridian's NL-to-SQL calls an LLM (Groq, which is OpenAI-compatible). Real tests would burn tokens on every CI run and tolerate non-determinism. So I wrote a contract for the LLM's &lt;code&gt;/v1/chat/completions&lt;/code&gt;, ran &lt;code&gt;specmatic stub&lt;/code&gt;, and pointed the app at it with a single env var.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How it's wired:&lt;/em&gt; &lt;code&gt;core/llm_manager.py&lt;/code&gt; reads &lt;code&gt;GROQ_API_URL&lt;/code&gt; (unset in prod → real Groq); in CI a step boots &lt;code&gt;specmatic stub llm_contract.yaml&lt;/code&gt;, sets &lt;code&gt;GROQ_API_URL&lt;/code&gt; to it, and runs the real NL-to-SQL path against the stub — so the AI path is &lt;strong&gt;offline, deterministic, and zero-token&lt;/strong&gt;. It's a &lt;em&gt;separate&lt;/em&gt; CI step, after the API contract tests, because it virtualizes an upstream &lt;em&gt;dependency&lt;/em&gt; the app &lt;strong&gt;consumes&lt;/strong&gt;, not the app's own API. The stub spec is a deliberately reduced version of the real provider API — every deviation (and why) is documented in &lt;code&gt;LLM_CONTRACT_NOTES.md&lt;/code&gt;. The same tool that guards &lt;em&gt;my&lt;/em&gt; API also virtualizes the &lt;em&gt;dependency&lt;/em&gt; my API consumes — exactly the kind of guardrail an AI-native codebase needs.&lt;/p&gt;

&lt;p&gt;All three run in CI on every push, and the contract test reports &lt;strong&gt;100% coverage&lt;/strong&gt; (generative negative tests cover the &lt;code&gt;400&lt;/code&gt; that examples can't). Spec-Driven Development didn't just document my API — it found a bug, made my AI tests free, and became the project's source of truth.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing the loop: testing the LLM-calling endpoints (and the bugs that fell out)
&lt;/h2&gt;

&lt;p&gt;The real test of "mock the LLM" is to run the contract suite against &lt;strong&gt;the endpoints that actually call the LLM&lt;/strong&gt; (&lt;code&gt;/api/command&lt;/code&gt;) with the provider stubbed — so the AI path is exercised, deterministically, for &lt;strong&gt;zero tokens&lt;/strong&gt;. Those endpoints are behind a session cookie, which Specmatic's test mode can't drive, so I added a &lt;strong&gt;gated test-auth&lt;/strong&gt; (a Bearer path enabled only when &lt;code&gt;SPECMATIC_TEST&lt;/code&gt; is set, never in production) and pointed the app's LLM calls at the stub.&lt;/p&gt;

&lt;p&gt;Running the &lt;strong&gt;full &lt;code&gt;api_contract.yaml&lt;/code&gt;&lt;/strong&gt; this way immediately earned its keep — it surfaced five real issues I'd never have found by reading the code:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api/connections&lt;/code&gt; leaked an undocumented &lt;code&gt;config&lt;/code&gt; object&lt;/strong&gt; (DB path/host) the contract didn't model — caught as an "unknown property."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The &lt;code&gt;/api/command&lt;/code&gt; error response was ambiguous&lt;/strong&gt; in its &lt;code&gt;oneOf&lt;/code&gt; (it carries &lt;code&gt;sql&lt;/code&gt;/&lt;code&gt;task&lt;/code&gt; alongside &lt;code&gt;error&lt;/code&gt;, so it half-looked like a &lt;code&gt;ReadResult&lt;/code&gt;). I gave errors their own &lt;code&gt;CommandError&lt;/code&gt; schema.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api/command&lt;/code&gt; returned HTTP 500 on a non-string &lt;code&gt;command&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;None.strip()&lt;/code&gt; crashed) — a resiliency negative test caught it; I fixed it to a clean &lt;code&gt;400&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api/execute&lt;/code&gt; mishandled malformed &lt;code&gt;sql&lt;/code&gt;&lt;/strong&gt; the same way — fixed to &lt;code&gt;400&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/api/execute&lt;/code&gt; had its body marked required&lt;/strong&gt;, but the human-in-the-loop flow calls it with no body (run the last-reviewed SQL) — the contract was lying about the design; I corrected it.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Two of those (3, 4) were genuine &lt;strong&gt;server crashes&lt;/strong&gt; on bad input. The whole point landed: &lt;em&gt;mocking the LLM let the contract suite exercise the AI endpoints for free, and the resiliency tests turned "we'll find out in production" into "we found out in CI."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Making the pipeline legible: one job per concern, real HTML reports
&lt;/h2&gt;

&lt;p&gt;The last round was about &lt;em&gt;signal quality&lt;/em&gt;. Bundling "does it conform?" and "does it survive garbage input?" into a single green check hides which one broke when it does. So I &lt;strong&gt;split the CI by concern&lt;/strong&gt;: for each app spec (&lt;code&gt;contract_public.yaml&lt;/code&gt;, &lt;code&gt;api_contract.yaml&lt;/code&gt;) there are now two independent jobs — a &lt;strong&gt;contract&lt;/strong&gt; job (value-checked conformance against inline + external examples) and a &lt;strong&gt;resiliency&lt;/strong&gt; job (generative negative/boundary tests). Two specs × two modes = &lt;strong&gt;four test jobs&lt;/strong&gt;, plus a fifth that smoke-tests the LLM virtualization. A conformance regression and a robustness regression now light up as &lt;em&gt;different&lt;/em&gt; red checks.&lt;/p&gt;

&lt;p&gt;I also stopped hand-writing text summaries and let Specmatic emit the real thing: &lt;code&gt;specmatic.yaml&lt;/code&gt; configures &lt;code&gt;report.formatters: [text, html]&lt;/code&gt;, so every job produces a proper &lt;strong&gt;HTML coverage report&lt;/strong&gt; (&lt;code&gt;build/reports/specmatic/test/html&lt;/code&gt;) — committed under &lt;code&gt;reports/&lt;/code&gt; and uploaded as a CI artifact. The numbers are honest per concern: &lt;code&gt;contract_public&lt;/code&gt; contract-mode covers the 200s (50%) while its resiliency job reaches &lt;strong&gt;100%&lt;/strong&gt; by exercising the &lt;code&gt;400&lt;/code&gt;; &lt;code&gt;api_contract&lt;/code&gt; sits at 43% → 64% because the authenticated-only paths and 401s aren't auto-driven by bearer test-mode — the report &lt;em&gt;shows&lt;/em&gt; that gap rather than papering over it.&lt;/p&gt;

&lt;p&gt;Finally, I broadened the &lt;strong&gt;external examples&lt;/strong&gt; — the login contract now pins all three roles and the rejection path (&lt;code&gt;admin1&lt;/code&gt;→&lt;code&gt;ADMIN&lt;/code&gt;, &lt;code&gt;editor1&lt;/code&gt;→&lt;code&gt;EDITOR&lt;/code&gt;, &lt;code&gt;viewer1&lt;/code&gt;→&lt;code&gt;VIEWER&lt;/code&gt;, bad creds→&lt;code&gt;Invalid credentials&lt;/code&gt;) as separate example files, so the contract job checks real values across the whole auth surface, not just one case.&lt;/p&gt;

&lt;p&gt;The throughline across every round: &lt;em&gt;the contract isn't documentation you write once — it's an executable artifact you keep sharpening, and each time you sharpen it, it hands you back a bug or a blind spot you didn't know you had.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Driving coverage to 100% — and what the gap was hiding
&lt;/h2&gt;

&lt;p&gt;The per-concern reports were honest, and they exposed a real hole: my protected endpoints were only ~43–64% covered. Digging into Specmatic's coverage table, the pattern was clear — every endpoint's &lt;code&gt;200&lt;/code&gt; was tested, but the &lt;code&gt;400&lt;/code&gt;s and especially the &lt;code&gt;401&lt;/code&gt;s were "not tested." The &lt;code&gt;401&lt;/code&gt;s were the interesting ones: my CI test-auth accepted &lt;strong&gt;any&lt;/strong&gt; bearer token, and Specmatic always sends one in test mode, so the unauthenticated path was structurally unreachable. My own test shortcut was hiding a whole column of behavior.&lt;/p&gt;

&lt;p&gt;The fix was to make auth &lt;strong&gt;example-driven&lt;/strong&gt; instead of blanket: the test-auth gate now accepts exactly one token (the value of &lt;code&gt;SPECMATIC_TEST&lt;/code&gt;). Then external examples do the rest — they carry the right token to exercise the authenticated &lt;code&gt;200&lt;/code&gt;/&lt;code&gt;400&lt;/code&gt; responses, and a &lt;em&gt;wrong&lt;/em&gt; token (or none) to exercise the real &lt;code&gt;401&lt;/code&gt;. One authenticated run now covers both the authorized and the unauthorized response of every secured endpoint. With examples added for every &lt;code&gt;400&lt;/code&gt; and &lt;code&gt;401&lt;/code&gt;, all four jobs — contract and resiliency, on both specs — report &lt;strong&gt;100% coverage&lt;/strong&gt;. The lesson that stuck: &lt;em&gt;a convenient test backdoor can quietly suppress coverage; tightening it to be precise is what let the contract test the thing that actually matters (is auth enforced?).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating the config to v3: wiring that matches the architecture
&lt;/h2&gt;

&lt;p&gt;Finally I migrated &lt;code&gt;specmatic.yaml&lt;/code&gt; from v2 to &lt;strong&gt;v3&lt;/strong&gt;, which replaces v2's implicit &lt;code&gt;provides&lt;/code&gt;/&lt;code&gt;consumes&lt;/code&gt; with &lt;strong&gt;explicit service wiring&lt;/strong&gt;. That turned out to describe this system almost perfectly: the &lt;code&gt;systemUnderTest&lt;/code&gt; is the Meridian Data API (run &lt;code&gt;type: test&lt;/code&gt;), and its one external &lt;code&gt;dependency&lt;/code&gt; — the LLM provider — is declared as a service run in &lt;code&gt;type: mock&lt;/code&gt;. The config now reads like the architecture diagram: &lt;em&gt;test my API; virtualize the LLM it depends on.&lt;/em&gt; Reports moved under &lt;code&gt;specmatic.governance.report&lt;/code&gt; (&lt;code&gt;formats: [html, ctrf]&lt;/code&gt;). Six rounds in, the config isn't just settings — it's an honest, executable description of what this service is and what it leans on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actual coverage: making the app tell Specmatic what it really exposes
&lt;/h2&gt;

&lt;p&gt;There's a difference between "every operation in the contract is tested" and "every operation is tested &lt;em&gt;and actually exists in the running app&lt;/em&gt;." Specmatic can prove the second — but only if the app exposes its route table. Spring Boot apps get this free via &lt;code&gt;/actuator/mappings&lt;/code&gt;; this is a Flask app, so I built the equivalent: a &lt;strong&gt;test-only &lt;code&gt;/actuator/mappings&lt;/code&gt; endpoint&lt;/strong&gt; that generates the Spring-Actuator JSON from Flask's own &lt;code&gt;url_map&lt;/code&gt;. Point Specmatic at it (it auto-discovers &lt;code&gt;/actuator&lt;/code&gt;), and the "cannot calculate actual coverage" warning disappears — the reports now show &lt;strong&gt;100% Absolute Coverage&lt;/strong&gt;, meaning each contracted endpoint is confirmed live in the app &lt;em&gt;and&lt;/em&gt; exercised, not merely matched against a document. It also draws a clean line between the API the contract governs and the app's broader surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency hygiene: install that just works, 3.11 through 3.14
&lt;/h2&gt;

&lt;p&gt;A reviewer on Python 3.14 hit a wall: &lt;code&gt;pymssql==2.3.4&lt;/code&gt; has no wheel for 3.14 and fails to build. The fix taught a nice lesson about &lt;em&gt;optional&lt;/em&gt; dependencies. Meridian speaks to eight database engines, but every driver is &lt;strong&gt;lazy-imported&lt;/strong&gt; — the app and the entire test suite run on the bundled SQLite databases with none of them installed. So the heavy external drivers didn't belong in the core &lt;code&gt;requirements.txt&lt;/code&gt; at all. I split them into &lt;code&gt;requirements-optional.txt&lt;/code&gt; (loosely pinned so newer Pythons resolve a compatible wheel) and left the core install lean. Verified by importing the app with every driver blocked: 91 routes, zero errors. A dependency you only need for one optional path shouldn't be able to break everyone's install.&lt;/p&gt;

&lt;h2&gt;
  
  
  The last mile: getting the details exactly right
&lt;/h2&gt;

&lt;p&gt;Three small things, each a good reminder that "works" and "configured correctly" aren't the same:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The actuator was &lt;em&gt;read&lt;/em&gt; but not &lt;em&gt;registered&lt;/em&gt;.&lt;/strong&gt; My report kept showing the actuator as disabled even though actual coverage was computing. The tell was in the access log: Specmatic was hitting &lt;code&gt;/actuator&lt;/code&gt; (my Spring-style root, which returns &lt;code&gt;_links&lt;/code&gt;) but never following to &lt;code&gt;/actuator/mappings&lt;/code&gt;. Pointing &lt;code&gt;actuatorUrl&lt;/code&gt; &lt;strong&gt;directly at the mappings endpoint&lt;/strong&gt; fixed it — the report now flips the actuator flag to enabled. A 200 was enough to silence the warning but not enough to actually ingest the routes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Auth belongs in the config, not scattered across examples.&lt;/strong&gt; Following Specmatic's security-schemes pattern, the bearer token is now declared once in &lt;code&gt;specmatic.yaml&lt;/code&gt; under &lt;code&gt;securitySchemes&lt;/code&gt; (overridable via &lt;code&gt;SPECMATIC_BEARER_TOKEN&lt;/code&gt;), and the one-command test runner starts the app with the matching test-auth token. So both the authenticated paths and the &lt;code&gt;401&lt;/code&gt;s are exercised, and there's a single place that owns the credential — no more hunting through fixtures to see how auth works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;python -m pip&lt;/code&gt;, always.&lt;/strong&gt; A reviewer on Python 3.14 hit an install-vs-run mismatch that traced back to bare &lt;code&gt;pip&lt;/code&gt; resolving to a different interpreter than the app runs on. The dependency split already made 3.14 install cleanly (verified on 3.11–3.14); documenting &lt;code&gt;python -m pip&lt;/code&gt; inside the venv closes the last gap. Small, but it's the difference between "it works on my machine" and "it works on yours."&lt;/p&gt;

&lt;h2&gt;
  
  
  Don't test through a backdoor
&lt;/h2&gt;

&lt;p&gt;The sharpest feedback I got was also the most obvious in hindsight: &lt;em&gt;your code shouldn't have a separate path for the test tool.&lt;/em&gt; I'd been authenticating Specmatic through a &lt;code&gt;SPECMATIC_TEST&lt;/code&gt;-gated bypass — a branch that only existed for contract testing. That's a smell: you're no longer testing the thing you ship. So I removed it and made bearer-token auth a &lt;strong&gt;real, first-class feature&lt;/strong&gt; of the API: alongside the web UI's session-cookie login, the API accepts a &lt;code&gt;Bearer&lt;/code&gt; token (configured via &lt;code&gt;API_BEARER_TOKEN&lt;/code&gt;, off by default) that any programmatic client can use — curl, a gateway, CI, or Specmatic. Specmatic just declares that token in &lt;code&gt;securitySchemes&lt;/code&gt; and sends it like any other client. The auth path under test is now the &lt;em&gt;same&lt;/em&gt; path real callers use, and the &lt;code&gt;401&lt;/code&gt;s are exercised by simply sending a wrong token. No backdoor, no drift between "tested" and "deployed."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scope, documented.&lt;/strong&gt; With the actuator now honestly reporting the app's 52 routes against a 6-endpoint contract, the report shows 46 "Missing in Spec." Rather than paper over that, I wrote &lt;a href="//CONTRACT_SCOPE.md"&gt;&lt;code&gt;CONTRACT_SCOPE.md&lt;/code&gt;&lt;/a&gt;: the six are the trust boundary external consumers depend on (contract-tested at 100%); the rest are the SPA's own feature endpoints, shipped in the same unit, with an explicit promotion path. A contract's job is to guard boundaries, and the gap is now a visible, justified backlog rather than a silent omission.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it should just run.&lt;/strong&gt; A reviewer hit port conflicts on the fixed test ports, and later found the setup easier to trust as plain, explicit commands than as a wrapper script. The README now documents the exact per-job commands directly, with every environment variable spelled out, so nothing about running the tests is hidden behind tooling. "Clone and run" shouldn't depend on nothing else living on port 5001, or on trusting a script you haven't read.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Code + integration: github.com/AYON-ARYAN/DATABASE-MANAGER (branches: main + react_build; see &lt;code&gt;api_contract.yaml&lt;/code&gt;, &lt;code&gt;SPECMATIC_INTEGRATION.md&lt;/code&gt;). Specademy course completed — certificate attached.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;— Ayon Aryan&lt;/p&gt;

</description>
      <category>specmatic</category>
      <category>contracttesting</category>
      <category>api</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
