<?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: Faisal Mehmood</title>
    <description>The latest articles on DEV Community by Faisal Mehmood (@faisal77666).</description>
    <link>https://dev.to/faisal77666</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%2F4068444%2F60629d68-1ac8-4d59-98c0-e8fc71f06572.jpg</url>
      <title>DEV Community: Faisal Mehmood</title>
      <link>https://dev.to/faisal77666</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/faisal77666"/>
    <language>en</language>
    <item>
      <title>One Prompt Template, Three Engines That Disagree</title>
      <dc:creator>Faisal Mehmood</dc:creator>
      <pubDate>Sat, 08 Aug 2026 07:34:16 +0000</pubDate>
      <link>https://dev.to/faisal77666/one-prompt-templatethree-engines-that-disagree-ifi</link>
      <guid>https://dev.to/faisal77666/one-prompt-templatethree-engines-that-disagree-ifi</guid>
      <description>&lt;p&gt;&lt;em&gt;Written against &lt;code&gt;main&lt;/code&gt; at the time of publishing. Every regex and line number is cited so you can re-check with &lt;code&gt;grep -n&lt;/code&gt;  and there's a runnable reproduction at the end that needs no build.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;PROMPT_TEMPLATE&lt;/code&gt; artifact in Apicurio Registry is rendered by &lt;strong&gt;three&lt;/strong&gt;&lt;br&gt;
different pieces of code that don't share an engine and don't agree on what a template means beyond a plain &lt;code&gt;{{name}}&lt;/code&gt;. Add a space, a dot, or an &lt;code&gt;{{#if}}&lt;/code&gt; block and the same stored template renders three different ways. If you're going to build anything on top of prompt rendering  a UI playground,a new client you have to know which of the three you're actually rendering against.&lt;/p&gt;


&lt;h2&gt;
  
  
  Storing a prompt template looks simple
&lt;/h2&gt;

&lt;p&gt;You save some text with &lt;code&gt;{{variable}}&lt;/code&gt; holes in it, hand it a set of arguments, and get a rendered string back. One artifact, one render.&lt;/p&gt;

&lt;p&gt;Except the registry doesn't render it in one place. It renders it in &lt;strong&gt;three&lt;/strong&gt;,and those three don't share an engine. A template that renders correctly over the&lt;br&gt;
REST API can come back untouched over the MCP server, and be flagged as having novariables at all by the validator that gated it on the way in.&lt;/p&gt;

&lt;p&gt;A renderer that returns &lt;em&gt;a&lt;/em&gt; result is not the same as a renderer that returns a &lt;em&gt;consistent&lt;/em&gt; one. This post walks through exactly where the three surfaces diverge, using the real classes and the real regexes.&lt;/p&gt;
&lt;h2&gt;
  
  
  The three-surface model
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;PROMPT_TEMPLATE&lt;/code&gt; artifact is consumed by three independent pieces of code:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Surface&lt;/th&gt;
&lt;th&gt;Class&lt;/th&gt;
&lt;th&gt;Job&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;REST&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PromptRenderingService&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders a template to a string for HTTP clients&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PromptTemplateConverter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders a template for Model Context Protocol clients, including &lt;code&gt;{{#if}}&lt;/code&gt; blocks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Validator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PromptTemplateContentValidator&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;On write, decides which variables the template "declares"&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They live in three different modules (&lt;code&gt;app&lt;/code&gt;, &lt;code&gt;mcp&lt;/code&gt;, &lt;code&gt;schema-util&lt;/code&gt;). None of them calls the others. Each one carries its own idea of what a template &lt;em&gt;is&lt;/em&gt; encoded in its own regular expression.&lt;/p&gt;
&lt;h2&gt;
  
  
  The objects involved
&lt;/h2&gt;

&lt;p&gt;There is exactly one source of truth: the stored artifact content. Everything downstream is a consumer reading the same bytes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌──────────────────────────────┐
                    │  Stored PROMPT_TEMPLATE       │
                    │  content:  "Hi {{ name }}"    │   ← one source of truth
                    └───────────────┬──────────────┘
                                    │  same bytes, three consumers
           ┌────────────────────────┼────────────────────────┐
           ▼                        ▼                        ▼
   ┌───────────────┐       ┌────────────────┐       ┌──────────────────┐
   │ REST          │       │ MCP            │       │ Validator        │
   │ PromptRender- │       │ PromptTemplate-│       │ PromptTemplate-  │
   │ ingService    │       │ Converter      │       │ ContentValidator │
   ├───────────────┤       ├────────────────┤       ├──────────────────┤
   │ \{\{([^}]+)\}\}│      │ key spliced    │       │ \{\{(\w+)\}\}    │
   │ + .trim()     │       │ unescaped      │       │  (strict)        │
   └───────┬───────┘       └───────┬────────┘       └────────┬─────────┘
           ▼                       ▼                         ▼
       "Hi Ada"            "Hi {{ name }}"            declares: (nothing)
        renders            whitespace breaks it       name not recognized
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single picture is the whole story. The rest of this post is just proving&lt;br&gt;
each of those three boxes really behaves that way.&lt;/p&gt;
&lt;h2&gt;
  
  
  A small template example
&lt;/h2&gt;

&lt;p&gt;Take the simplest possible template and one argument:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;template:  Hi {{name}}
args:      { "name": "Ada" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three surfaces agree here. REST renders &lt;code&gt;Hi Ada&lt;/code&gt;, MCP renders &lt;code&gt;Hi Ada&lt;/code&gt;, and&lt;br&gt;
the validator sees one declared variable, &lt;code&gt;name&lt;/code&gt;. Good. This is the case everyone&lt;br&gt;
tests, and it's why the divergence hides so well.&lt;/p&gt;

&lt;p&gt;Now change one thing at a time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two grammars, not one
&lt;/h2&gt;

&lt;p&gt;The REST renderer matches variables with this pattern, and then trims the capture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PromptRenderingService.java:27&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;VARIABLE_PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\{\\{([^}]+)\\}\\}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// PromptRenderingService.java:361&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;varName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;[^}]+&lt;/code&gt; means "one or more characters that aren't a closing brace." That is a&lt;br&gt;
&lt;em&gt;generous&lt;/em&gt; grammar. It matches whitespace. It matches dots. It matches &lt;code&gt;#&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;/&lt;/code&gt;. And because of the &lt;code&gt;.trim()&lt;/code&gt;, &lt;code&gt;{{ name }}&lt;/code&gt; and &lt;code&gt;{{name}}&lt;/code&gt; are treated as the&lt;br&gt;
same variable.&lt;/p&gt;

&lt;p&gt;The validator — the code that decides, on write, which variables a template&lt;br&gt;
declares — uses a &lt;em&gt;different, stricter&lt;/em&gt; pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PromptTemplateContentValidator.java:30&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;TEMPLATE_VARIABLE_PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\{\\{(\\w+)\\}\\}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;\w+&lt;/code&gt; is "word characters only." No spaces, no dots, no symbols. So the set of&lt;br&gt;
variables the server &lt;em&gt;accepts as declared&lt;/em&gt; and the set of variables the server&lt;br&gt;
&lt;em&gt;will actually substitute&lt;/em&gt; are defined by two different regexes in two different&lt;br&gt;
modules. The write path and the render path do not speak the same language.&lt;/p&gt;
&lt;h2&gt;
  
  
  What happens with whitespace
&lt;/h2&gt;

&lt;p&gt;Add a space:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;template:  Hi {{ name }}
args:      { "name": "Ada" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;REST&lt;/strong&gt; → &lt;code&gt;Hi Ada&lt;/code&gt;. &lt;code&gt;[^}]+&lt;/code&gt; captures &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;.trim()&lt;/code&gt; makes it &lt;code&gt;name&lt;/code&gt;,
substituted.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP&lt;/strong&gt; → &lt;code&gt;Hi {{ name }}&lt;/code&gt;. Unchanged. (Why, in the next section.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Validator&lt;/strong&gt; → declares &lt;em&gt;nothing&lt;/em&gt;. &lt;code&gt;\w+&lt;/code&gt; never matched &lt;code&gt;name&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Same template. One surface renders it, one leaves it raw, and the gatekeeper&lt;br&gt;
thinks it has no variables at all.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why MCP leaves it raw — the substitution nobody escapes
&lt;/h2&gt;

&lt;p&gt;MCP declares a &lt;code&gt;VARIABLE_PATTERN&lt;/code&gt; at the top of the file, exactly like the others:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PromptTemplateConverter.java:25  — declared but never referenced&lt;/span&gt;
&lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="nc"&gt;Pattern&lt;/span&gt; &lt;span class="no"&gt;VARIABLE_PATTERN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
        &lt;span class="nc"&gt;Pattern&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;compile&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"\\{\\{(\\w+)\\}\\}"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's never used. The real substitution doesn't compile a pattern for the whole&lt;br&gt;
template. It loops over the &lt;em&gt;arguments&lt;/em&gt; and builds a regex per key, on the fly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PromptTemplateConverter.java:441&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;placeholder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"\\{\\{"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;entry&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getKey&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s"&gt;"\\}\\}"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// :443&lt;/span&gt;
&lt;span class="n"&gt;rendered&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rendered&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;replaceAll&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Matcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;quoteReplacement&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things fall out of this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It builds a regex that matches &lt;code&gt;{{name}}&lt;/code&gt; &lt;strong&gt;exactly&lt;/strong&gt; — no &lt;code&gt;[^}]+&lt;/code&gt;, no &lt;code&gt;.trim()&lt;/code&gt;.
So &lt;code&gt;{{ name }}&lt;/code&gt; with spaces never matches, and the placeholder survives into
the output. That's the whitespace result above.&lt;/li&gt;
&lt;li&gt;The replacement &lt;em&gt;value&lt;/em&gt; is escaped with &lt;code&gt;Matcher.quoteReplacement&lt;/code&gt;, but the
&lt;em&gt;key&lt;/em&gt; is spliced straight into the pattern with no &lt;code&gt;Pattern.quote&lt;/code&gt;. The key
becomes regex source. For an ordinary name that's harmless; for a key with
regex metacharacters it is not the same string match REST would do.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The triple-brace and the dotted path
&lt;/h2&gt;

&lt;p&gt;Two more probes, to show it isn't only whitespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;template:  Raw: {{{name}}}     args: { "name": "Ada" }
  REST → Raw: {{{name}}}       (the greedy match lands on {name, no such arg → left literal)
  MCP  → Raw: {Ada}            ({{name}} is found *inside* the braces and replaced)

template:  Hi {{user.email}}   args: { "user.email": "a@x" }
  REST → Hi a@x                ([^}]+ matches the dot)
  MCP  → Hi a@x                (the unescaped '.' happens to match the literal dot)
  Validator → declares nothing (\w+ rejects the dot)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dotted-path row is the sneaky one: REST and MCP &lt;em&gt;agree&lt;/em&gt; on the output, and&lt;br&gt;
it's the &lt;strong&gt;validator&lt;/strong&gt; that's the odd one out — it never considered &lt;code&gt;user.email&lt;/code&gt;&lt;br&gt;
a declared variable in the first place. So "the surfaces disagree" isn't even a&lt;br&gt;
clean two-against-one; which surface is the outlier depends on the construct.&lt;/p&gt;
&lt;h2&gt;
  
  
  The full divergence table
&lt;/h2&gt;

&lt;p&gt;This is the output of the reproduction script (&lt;code&gt;verify_divergence.py&lt;/code&gt;), which&lt;br&gt;
re-implements each surface using its exact on-&lt;code&gt;main&lt;/code&gt; regex:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;probe          REST                          MCP              validator sees
--------------------------------------------------------------------------------
plain          Hi Ada                        Hi Ada           name
whitespace     Hi Ada                        Hi {{ name }}    (none)
triple-stache  Raw: {{{name}}}               Raw: {Ada}       name
if-block       {{#if premium}}VIP Ada{{/if}} VIP Ada          name
dotted-path    Hi a@x                        Hi a@x           (none)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the first row — plain &lt;code&gt;{{name}}&lt;/code&gt; — is unanimous. Every other construct&lt;br&gt;
produces at least one disagreement. And &lt;code&gt;{{#if}}&lt;/code&gt; blocks only exist on MCP at&lt;br&gt;
all: REST has no concept of them, so it emits the raw &lt;code&gt;{{#if}}&lt;/code&gt; and &lt;code&gt;{{/if}}&lt;/code&gt;&lt;br&gt;
tags into the output.&lt;/p&gt;
&lt;h2&gt;
  
  
  So  is this a bug?
&lt;/h2&gt;

&lt;p&gt;Some of these divergences may be intentional; &lt;code&gt;{{#if}}&lt;/code&gt; on MCP but not REST could&lt;br&gt;
be a deliberate feature-gap, not an oversight. I'm not calling the code broken.&lt;br&gt;
The point that survives either way is narrower and more useful:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The same stored template does not have a single, well-defined rendering. What&lt;br&gt;
it means depends on which surface serves it.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's a fact about the architecture, and it's the fact anyone building on top of prompt rendering needs to design around.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why I care: building a UI Playground on top
&lt;/h2&gt;

&lt;p&gt;I'm proposing a Prompt Template Playground in the Registry UI a panel where you edit a template, fill in variables, and see the rendered result before you save.&lt;/p&gt;

&lt;p&gt;The table above is the reason this feature is harder than it looks, and the reason it's worth doing carefully. A playground has to render the preview &lt;em&gt;somehow&lt;/em&gt;. If it quietly reimplements a fourth regex in TypeScript, it becomes a &lt;strong&gt;fourth&lt;/strong&gt;&lt;br&gt;
dialect  now there are four answers to "what does this template mean." The correct design is the opposite: the playground must render against the surface the&lt;br&gt;
user is actually going to call, and be explicit about which one. Anything else is a preview that lies.&lt;br&gt;
That's also why the first useful contributions here are backend-shaped, not just&lt;br&gt;
UI polish: before a playground can tell the truth, the surfaces have to agree on what the truth is.&lt;/p&gt;
&lt;h2&gt;
  
  
  What to remember
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;code&gt;PROMPT_TEMPLATE&lt;/code&gt; is rendered by &lt;strong&gt;three independent surfaces&lt;/strong&gt; (REST, MCP,
validator) that do &lt;strong&gt;not&lt;/strong&gt; share an engine.&lt;/li&gt;
&lt;li&gt;They encode &lt;strong&gt;three different grammars&lt;/strong&gt;: &lt;code&gt;[^}]+&lt;/code&gt;+trim (REST), exact-key splice
(MCP), strict &lt;code&gt;\w+&lt;/code&gt; (validator). Only plain &lt;code&gt;{{name}}&lt;/code&gt; renders the same everywhere.&lt;/li&gt;
&lt;li&gt;Before you build anything &lt;em&gt;on top&lt;/em&gt; of prompt rendering, pin down &lt;strong&gt;which surface&lt;/strong&gt; you're rendering against  or you'll ship a fourth dialect.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Reproduce it yourself
&lt;/h2&gt;

&lt;p&gt;No build required  the repo re-implements each surface with its exact on-&lt;code&gt;main&lt;/code&gt; regex and prints the table above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Faisal77666/prompt-template-render-divergence
&lt;span class="nb"&gt;cd &lt;/span&gt;prompt-template-render-divergence
python3 verify_divergence.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source it's checking against:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PromptRenderingService.java&lt;/code&gt;  REST renderer (&lt;code&gt;:27&lt;/code&gt;, &lt;code&gt;:361&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PromptTemplateConverter.java&lt;/code&gt;  MCP renderer (&lt;code&gt;:25&lt;/code&gt;, &lt;code&gt;:441&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PromptTemplateContentValidator.java&lt;/code&gt;  write-time validator (&lt;code&gt;:30&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Line numbers reflect &lt;code&gt;main&lt;/code&gt; at the time of writing. If they've drifted, a quick&lt;br&gt;
&lt;code&gt;grep -n&lt;/code&gt; on the patterns above will find them.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>llm</category>
      <category>cncf</category>
      <category>apicurioregistry</category>
    </item>
  </channel>
</rss>
