<?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: Lavie</title>
    <description>The latest articles on DEV Community by Lavie (@vibestackdev).</description>
    <link>https://dev.to/vibestackdev</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%2F3851208%2F98ba92e8-fdde-4e61-879d-6680a9ae3b3e.jpg</url>
      <title>DEV Community: Lavie</title>
      <link>https://dev.to/vibestackdev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/vibestackdev"/>
    <language>en</language>
    <item>
      <title>React 19 migration traps your AI keeps falling into (and how to fix them with .mdc rules)</title>
      <dc:creator>Lavie</dc:creator>
      <pubDate>Wed, 01 Apr 2026 20:17:59 +0000</pubDate>
      <link>https://dev.to/vibestackdev/react-19-migration-traps-your-ai-keeps-falling-into-and-how-to-fix-them-with-mdc-rules-2aoe</link>
      <guid>https://dev.to/vibestackdev/react-19-migration-traps-your-ai-keeps-falling-into-and-how-to-fix-them-with-mdc-rules-2aoe</guid>
      <description>&lt;p&gt;Every AI coding assistant was trained on React 18 patterns. When you're building with React 19 + Next.js 15, the AI generates code that compiles perfectly but fails at runtime or behaves unexpectedly.&lt;/p&gt;

&lt;p&gt;Here are the 4 most common React 19 traps I've documented, with &lt;code&gt;.mdc&lt;/code&gt; rules to prevent each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. useFormStatus() returns &lt;code&gt;{ pending: false }&lt;/code&gt; forever
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The trap:&lt;/strong&gt; The AI places &lt;code&gt;useFormStatus()&lt;/code&gt; in the same component that renders the &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;. This will NEVER work — &lt;code&gt;useFormStatus&lt;/code&gt; only reads status from a parent &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ AI generates this — pending is ALWAYS false&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;use client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFormStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createItem&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Submit&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Extract the button to a child component&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;SubmitButton&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useFormStatus&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Saving...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Submit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyForm&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createItem&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;SubmitButton&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. useFormState is deprecated — AI still imports it
&lt;/h2&gt;

&lt;p&gt;React 19 renamed &lt;code&gt;useFormState&lt;/code&gt; to &lt;code&gt;useActionState&lt;/code&gt; and moved it from &lt;code&gt;react-dom&lt;/code&gt; to &lt;code&gt;react&lt;/code&gt;. The AI generates the old import because its training data is 99% React 18.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Deprecated — AI generates this&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useFormState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ React 19&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useActionState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;formAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isPending&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useActionState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serverAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. forwardRef is no longer needed
&lt;/h2&gt;

&lt;p&gt;In React 19, &lt;code&gt;ref&lt;/code&gt; is a regular prop. The AI wraps every component in &lt;code&gt;forwardRef()&lt;/code&gt; because that's what it learned from thousands of React 18 examples.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Unnecessary boilerplate in React 19&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MyInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;forwardRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ React 19 — ref is a regular prop&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;MyInput&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;Props&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Ref&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;HTMLInputElement&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. params are Promises in Next.js 15
&lt;/h2&gt;

&lt;p&gt;This is the #1 runtime crash. In Next.js 15, &lt;code&gt;params&lt;/code&gt; and &lt;code&gt;searchParams&lt;/code&gt; are &lt;code&gt;Promise&lt;/code&gt; objects. The AI generates synchronous destructuring because Next.js 14 used synchronous params.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ Compiles in dev, crashes in production&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ Next.js 15 — params are async&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The .mdc solution
&lt;/h2&gt;

&lt;p&gt;Each of these traps has a corresponding &lt;code&gt;.mdc&lt;/code&gt; rule file that activates based on file globs. When the AI opens a &lt;code&gt;.tsx&lt;/code&gt; file, the rules inject the correct patterns into the context window, overriding the stale training data.&lt;/p&gt;

&lt;p&gt;I've documented 29 of these patterns (React 19, Next.js 15, Supabase SSR, Stripe) as open source &lt;code&gt;.mdc&lt;/code&gt; rule files:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/vibestackdev/vibe-stack" rel="noopener noreferrer"&gt;github.com/vibestackdev/vibe-stack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick install (5 free rules):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx vibe-stack-rules init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;What React 19 traps have you hit with AI code generation? I'd love to add them to the rule set.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>nextjs</category>
      <category>cursor</category>
      <category>ai</category>
    </item>
    <item>
      <title>Why your Cursor rules are being silently ignored (and how to fix it)</title>
      <dc:creator>Lavie</dc:creator>
      <pubDate>Wed, 01 Apr 2026 17:56:56 +0000</pubDate>
      <link>https://dev.to/vibestackdev/why-your-cursor-rules-are-being-silently-ignored-and-how-to-fix-it-4123</link>
      <guid>https://dev.to/vibestackdev/why-your-cursor-rules-are-being-silently-ignored-and-how-to-fix-it-4123</guid>
      <description>&lt;h2&gt;
  
  
  The most frustrating thing about Cursor rules
&lt;/h2&gt;

&lt;p&gt;You write a rule. You are confident it is correct. You open a chat. The AI ignores it completely and generates the exact pattern you told it not to.&lt;/p&gt;

&lt;p&gt;No error. No warning. Just silence.&lt;/p&gt;

&lt;p&gt;This happens to almost every developer who adopts .mdc rules, and it almost always comes down to four root causes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cause 1: Malformed YAML frontmatter (the silent killer)
&lt;/h2&gt;

&lt;p&gt;This is the number 1 reason rules are ignored. If your frontmatter has any syntax error, Cursor silently skips the file. No warning, no log, nothing.&lt;/p&gt;

&lt;p&gt;Wrong patterns that silently fail:&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My rule&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Missing the closing ---. Rule is never loaded.&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My rule&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;src/**/*.ts&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Glob must be an array. Rule is never loaded.&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;My rule&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;True&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;YAML is case-sensitive. True is not true. Rule is never loaded.&lt;/p&gt;

&lt;p&gt;Correct format:&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="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;What this rule prevents&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/**/*.ts"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;src/**/*.tsx"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="na"&gt;alwaysApply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Debug step: Open Cursor Settings and navigate to Rules. You should see your rule listed there. If it is missing, your frontmatter is broken.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cause 2: You are using the old single-file format
&lt;/h2&gt;

&lt;p&gt;The .cursorrules single file at the project root still works, but it conflicts unpredictably with the newer .cursor/rules/ directory format. If you have both, behavior is undefined.&lt;/p&gt;

&lt;p&gt;Migrate fully to .cursor/rules/:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.cursor/
  rules/
    supabase-auth.mdc
    nextjs15-params.mdc
    project-context.mdc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Delete your old .cursorrules file entirely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cause 3: Too many rules with alwaysApply: true
&lt;/h2&gt;

&lt;p&gt;Rules with alwaysApply: true load into every session and consume context window tokens whether the task needs them or not.&lt;/p&gt;

&lt;p&gt;If you have 10+ rules all set to alwaysApply: true, you are burning a large portion of the context window before the conversation even starts. The model satisfies all of them simultaneously and produces average output that partially violates most of them.&lt;/p&gt;

&lt;p&gt;The fix: only 1-2 rules should have alwaysApply: true. Everything else should be glob-targeted.&lt;/p&gt;

&lt;p&gt;A well-structured system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;project-context.mdc      alwaysApply: true  (project identity only -- keep it tiny)
supabase-auth.mdc        globs: ["**/lib/supabase/**"]
nextjs15-params.mdc      globs: ["**/app/**/*.tsx"]
stripe-payments.mdc      globs: ["**/api/webhooks/**"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now only the relevant rules load for each file. Context is clean. Compliance improves.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cause 4: Negative instructions vs. positive instructions
&lt;/h2&gt;

&lt;p&gt;LLMs are trained to predict the next token. Negative instructions require the model to first imagine the bad pattern and then suppress it. This is harder than positive instructions.&lt;/p&gt;

&lt;p&gt;Weak: "Never use getSession() on the server"&lt;/p&gt;

&lt;p&gt;Strong: "Always use supabase.auth.getUser() for server-side auth. getUser() is the only method that verifies the JWT with the auth server."&lt;/p&gt;

&lt;p&gt;For security-critical rules, use both: tell the model what to do AND explicitly ban the alternative.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cause 5: The session is too long
&lt;/h2&gt;

&lt;p&gt;LLMs lose track of early context in long chat sessions. A rule loaded at the start may be effectively forgotten after 10-15 turns.&lt;/p&gt;

&lt;p&gt;Two fixes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Start fresh sessions for new tasks. Do not continue a 20-turn session for a new feature.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add rules as explicit references in your prompt: "Implement this following the auth patterns in supabase-auth-security.mdc."&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The debugging checklist
&lt;/h2&gt;

&lt;p&gt;When a rule is being ignored, run through this in order:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is the file in .cursor/rules/ (not .cursorrules)?&lt;/li&gt;
&lt;li&gt;Does the frontmatter have both opening and closing ---?&lt;/li&gt;
&lt;li&gt;Are glob patterns wrapped in an array ["..."]?&lt;/li&gt;
&lt;li&gt;Is alwaysApply lowercase true or false?&lt;/li&gt;
&lt;li&gt;Does the rule appear in Cursor Settings &amp;gt; Rules?&lt;/li&gt;
&lt;li&gt;Is the rule file under 150 lines?&lt;/li&gt;
&lt;li&gt;Is the rule phrased positively?&lt;/li&gt;
&lt;li&gt;Is this a fresh session?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the Claude Code leak teaches us
&lt;/h2&gt;

&lt;p&gt;The leaked Claude Code source code (March 31) revealed something relevant: even Anthropic's own production agent treats constraints as hints, not truth by default. It actively re-reads source files before acting because it knows its own memory is unreliable.&lt;/p&gt;

&lt;p&gt;The lesson: rule enforcement is not a passive property of having a rule file. It requires the rule to be correctly formatted, the task to be scoped small enough that the rule stays in context, and explicit invocation for critical operations.&lt;/p&gt;

&lt;p&gt;That is the same principle behind structuring rules as small, focused, glob-targeted files instead of one giant instruction document.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Next in this series: how to structure more than 20 rules without causing constraint drift.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cursor</category>
      <category>ai</category>
      <category>nextjs</category>
      <category>productivity</category>
    </item>
    <item>
      <title>The Claude Code leak proves what I've been building for months - AI architecture rules are not optional</title>
      <dc:creator>Lavie</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:13:33 +0000</pubDate>
      <link>https://dev.to/vibestackdev/the-claude-code-leak-proves-what-ive-been-building-for-months-ai-architecture-rules-are-not-20ma</link>
      <guid>https://dev.to/vibestackdev/the-claude-code-leak-proves-what-ive-been-building-for-months-ai-architecture-rules-are-not-20ma</guid>
      <description>&lt;h2&gt;
  
  
  Anthropic just accidentally published the best argument for .mdc rules
&lt;/h2&gt;

&lt;p&gt;On March 31, Anthropic shipped a source map file in their Claude Code npm package. Inside: 512,000 lines of unminified TypeScript showing exactly how a production-grade AI coding agent works internally.&lt;/p&gt;

&lt;p&gt;The developer community has been picking through it for 24 hours. I have been reading every analysis I can find, and one thing jumped out immediately:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Claude Code's own architecture validates the exact approach I have been building for the past two months.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The pattern Anthropic uses internally
&lt;/h2&gt;

&lt;p&gt;Claude Code is not a thin wrapper around an API. It is a full agent harness with five key architectural patterns:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Persistent rule files (CLAUDE.md)
&lt;/h3&gt;

&lt;p&gt;Claude Code loads a CLAUDE.md file at session start -- a markdown document containing project constraints, architectural rules, and "things to never do." Sound familiar?&lt;/p&gt;

&lt;p&gt;This is the same concept as .mdc rules in Cursor, .github/copilot-instructions.md in GitHub Copilot, and .windsurf/rules/ in Windsurf. Every major AI coding tool has converged on the same idea: &lt;strong&gt;persistent, file-based architectural constraints&lt;/strong&gt; that survive between sessions.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A "fail-closed" permission pipeline
&lt;/h3&gt;

&lt;p&gt;Every action in Claude Code passes through a permission system that classifies operations as LOW, MEDIUM, or HIGH risk. The evaluation order is strict: &lt;strong&gt;Deny &amp;gt; Ask &amp;gt; Allow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When a permission is denied, the denial gets fed back to the model as an error, so it learns to adjust its plan. &lt;strong&gt;Constraints must be enforced at generation time, not audited after the fact.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Skeptical retrieval -- never trust your own memory
&lt;/h3&gt;

&lt;p&gt;The leaked code shows that Claude Code treats its own stored knowledge as "hints, not truth." Before acting on something it "remembers," it re-reads the actual source file to verify.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Strict Write Discipline
&lt;/h3&gt;

&lt;p&gt;Claude Code only updates its memory after verified success. If a file write fails or a test breaks, nothing gets stored. This prevents the agent from "learning" incorrect patterns from its own failures.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. KAIROS and autoDream -- where this is headed
&lt;/h3&gt;

&lt;p&gt;The leaked source references an unreleased autonomous daemon called KAIROS (mentioned 150+ times). It runs background sessions and monitors GitHub webhooks even when the developer is idle. Its companion, autoDream, consolidates memory during idle time -- removing contradictions and converting observations into facts.&lt;/p&gt;

&lt;p&gt;The rules you write today become seeds of your project's permanent architectural memory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this matters for your Next.js project
&lt;/h2&gt;

&lt;p&gt;The leak reveals the architecture, but not domain-specific rules. Here are three hallucination patterns I have been cataloguing:&lt;/p&gt;

&lt;h3&gt;
  
  
  The getSession() hallucination
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// INSECURE -- reads JWT without verification&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// SECURE -- verifies with auth server  &lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The Next.js 15 async params crash
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Compiles fine. Crashes at runtime in Next.js 15.&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Correct -- params is a Promise in Next.js 15&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Middleware auth enforcement (false security)
&lt;/h3&gt;

&lt;p&gt;Middleware runs on Edge Runtime and cannot verify Supabase JWTs. Auth enforcement must happen in layouts/pages via getUser(), not middleware.&lt;/p&gt;




&lt;h2&gt;
  
  
  The takeaway
&lt;/h2&gt;

&lt;p&gt;The Claude Code leak is a roadmap. Anthropic built a constraint-first architecture because that is the only way to make AI coding reliable at scale.&lt;/p&gt;

&lt;p&gt;I maintain 25 free .mdc architecture rules for Next.js 15 + Supabase + Stripe: &lt;a href="https://github.com/vibestackdev/vibe-stack" rel="noopener noreferrer"&gt;github.com/vibestackdev/vibe-stack&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow me for the full series on preventing AI hallucinations in modern web development.&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>The Claude Code leak proves what I've been claudecode, cursor, nextjs, aibuilding for months</title>
      <dc:creator>Lavie</dc:creator>
      <pubDate>Wed, 01 Apr 2026 14:12:43 +0000</pubDate>
      <link>https://dev.to/vibestackdev/the-claude-code-leak-proves-what-ive-been-claudecode-cursor-nextjs-aibuilding-for-months-30na</link>
      <guid>https://dev.to/vibestackdev/the-claude-code-leak-proves-what-ive-been-claudecode-cursor-nextjs-aibuilding-for-months-30na</guid>
      <description></description>
    </item>
    <item>
      <title>I catalogued every AI hallucination in Next.js 15 — then I built a system to prevent them permanently</title>
      <dc:creator>Lavie</dc:creator>
      <pubDate>Mon, 30 Mar 2026 16:51:27 +0000</pubDate>
      <link>https://dev.to/vibestackdev/i-catalogued-every-ai-hallucination-in-nextjs-15-then-i-built-a-system-to-prevent-them-54dc</link>
      <guid>https://dev.to/vibestackdev/i-catalogued-every-ai-hallucination-in-nextjs-15-then-i-built-a-system-to-prevent-them-54dc</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; AI coding assistants have 5 recurring hallucination patterns that ship critical bugs to production. I documented 47 of them, then wrote 22 &lt;code&gt;.mdc&lt;/code&gt; rule files that physically prevent Cursor from generating insecure auth, deprecated imports, and broken Next.js 15 patterns. The entire system is &lt;a href="https://github.com/vibestackdev/vibe-stack" rel="noopener noreferrer"&gt;free and open source&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I stopped writing code three months ago.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not because I was burned out or because I found a "no-code" tool. I stopped because I realized I was playing the wrong game.&lt;/p&gt;

&lt;p&gt;I was using Cursor's Agent mode to build a Next.js SaaS app. The code looked clean. The types were correct. Everything compiled. And then I deployed to production and discovered that &lt;strong&gt;every single auth check in my application was insecure.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The AI had used &lt;code&gt;getSession()&lt;/code&gt; everywhere -- a function that reads a JWT from cookies &lt;em&gt;without verifying it&lt;/em&gt;. Any user could craft a fake token and my app would treat them as authenticated.&lt;/p&gt;

&lt;p&gt;That bug cost me 6 hours to fix. And it taught me something important.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Uncomfortable Truth About AI Coding
&lt;/h2&gt;

&lt;p&gt;AI models are crowd-pleasers. They'll generate whatever you ask for, with perfect TypeScript syntax and zero compiler errors. But they have &lt;strong&gt;systematic blind spots&lt;/strong&gt; -- patterns baked into their training data that are now deprecated, insecure, or broken.&lt;/p&gt;

&lt;p&gt;I started keeping a spreadsheet. Every time Cursor generated a pattern that I had to manually fix, I logged it. After three months, I had documented &lt;strong&gt;47 recurring hallucination patterns&lt;/strong&gt; across Next.js 15, Supabase, and Stripe.&lt;/p&gt;

&lt;p&gt;Here are the five most dangerous:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The getSession() Security Flaw
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the AI generates:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Looks correct. Compiles fine. Catastrophically insecure.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;session&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Why it's dangerous:&lt;/strong&gt; &lt;code&gt;getSession()&lt;/code&gt; reads the JWT from cookies but does NOT verify it with Supabase's auth server. An attacker can craft a fake JWT and your server will accept it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The secure pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This sends a request to Supabase to cryptographically verify the token&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;redirect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The training data is dominated by pre-2024 tutorials when &lt;code&gt;getSession()&lt;/code&gt; was the recommended approach. Supabase changed their guidance -- but the models haven't caught up.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Next.js 15 Params Time Bomb
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the AI generates:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;params&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;  &lt;span class="c1"&gt;// CRASHES: params is a Promise in Next.js 15&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The correct pattern:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Page&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;slug&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;slug&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;params&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;slug&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h1&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This breaks silently in development (Next.js does some auto-handling) but explodes in production builds. I've seen 4 different open-source repos with this exact bug.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Deprecated Package Import
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;What the AI generates:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createClientComponentClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@supabase/auth-helpers-nextjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The correct import:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createBrowserClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@supabase/ssr&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;auth-helpers-nextjs&lt;/code&gt; was deprecated in favor of &lt;code&gt;@supabase/ssr&lt;/code&gt;. The old package doesn't handle cookie-based sessions correctly with the App Router. But the AI has seen 10x more examples of the old package.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. The Client Component Disease
&lt;/h3&gt;

&lt;p&gt;The AI loves &lt;code&gt;'use client'&lt;/code&gt;. Ask it for a product page, and it'll slap &lt;code&gt;'use client'&lt;/code&gt; at the top and use &lt;code&gt;useState&lt;/code&gt; + &lt;code&gt;useEffect&lt;/code&gt; to fetch data from an API route.&lt;/p&gt;

&lt;p&gt;This is the React equivalent of driving a Ferrari in first gear. Server Components fetch data on the server with zero client-side JavaScript. They're faster, more secure, and simpler. But the AI defaults to 2022 patterns because that's what it was trained on.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The Missing RLS Time Bomb
&lt;/h3&gt;

&lt;p&gt;When the AI creates a Supabase table, it almost never enables Row Level Security. This means your database is wide open -- any authenticated user can read any other user's data using the anon key.&lt;/p&gt;




&lt;h2&gt;
  
  
  My Solution: Architecture Rules That Physically Constrain the AI
&lt;/h2&gt;

&lt;p&gt;After documenting all 47 patterns, I realized that "being careful" wasn't enough. I needed &lt;strong&gt;guardrails that work automatically&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Cursor supports &lt;code&gt;.mdc&lt;/code&gt; rule files -- markdown files with YAML frontmatter that the AI reads based on which files you're editing. They act like a senior code reviewer that's always watching.&lt;/p&gt;

&lt;p&gt;Here's how they work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Supabase auth security -- enforces getUser() over getSession()&lt;/span&gt;
&lt;span class="na"&gt;globs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/app/**"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/actions/**"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;**/api/**"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Supabase Auth Security&lt;/span&gt;

SECURITY CRITICAL: NEVER use getSession() in server-side code.
ALWAYS use getUser() -- it verifies the JWT against the auth server.

SECURE: const { data: { user } } = await supabase.auth.getUser()
INSECURE: const { data: { session } } = await supabase.auth.getSession()

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When this rule is active, every file matching &lt;code&gt;**/app/**&lt;/code&gt; automatically receives this constraint. The AI &lt;em&gt;cannot&lt;/em&gt; generate &lt;code&gt;getSession()&lt;/code&gt; calls because the rule explicitly overrides its training data.&lt;/p&gt;

&lt;p&gt;I wrote 22 of these rules. Not 22 random guidelines -- 22 precisely scoped constraints targeting the most dangerous hallucination patterns across:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Authentication security&lt;/strong&gt; (getUser vs getSession, deprecated packages)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Next.js 15 breaking changes&lt;/strong&gt; (async params, generateMetadata)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component architecture&lt;/strong&gt; (Server vs Client, minimal 'use client')&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database security&lt;/strong&gt; (RLS enforcement, service_role restrictions)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Input validation&lt;/strong&gt; (Zod at every boundary)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Performance&lt;/strong&gt; (parallel fetching, N+1 prevention, dynamic imports)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Payments&lt;/strong&gt; (server-only Stripe, webhook signature verification)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OWASP Top 10&lt;/strong&gt; (injection, XSS, rate limiting)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The 3-Stage Agentic Loop
&lt;/h2&gt;

&lt;p&gt;The rules prevent bad output. But to get &lt;em&gt;great&lt;/em&gt; output, you need a prompting framework. Here's mine:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 1: Plan (Act as the Architect)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"I need to build a magic-link auth flow. Before writing any code, create an Architectural Plan: which files, which are Server vs Client Components, what RLS policies. Wait for my approval."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Forcing the AI to plan before coding catches bad architectural decisions before they become bad code.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 2: Implement (Constrained Batches)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Execute Phase 1 only -- database schemas and RLS policies. Stop when Phase 1 is done."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;AI output quality degrades with length. Small batches keep it sharp.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stage 3: Verify (Self-Audit)&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Review the server action you wrote. Did you use getUser()? Did you validate input with Zod? Check against our project rules."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Self-reflection catches 80% of hallucinations before you hit save.&lt;/p&gt;




&lt;h2&gt;
  
  
  Try It -- It's Free and Open Source
&lt;/h2&gt;

&lt;p&gt;I packaged the entire system as &lt;strong&gt;&lt;a href="https://github.com/vibestackdev/vibe-stack" rel="noopener noreferrer"&gt;Vibe Stack&lt;/a&gt;&lt;/strong&gt; -- a Next.js 15 + Supabase boilerplate with all 22 rules pre-configured.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What you get:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Next.js 15 App Router + React 19 + TypeScript (strict mode)&lt;/li&gt;
&lt;li&gt;22 battle-tested &lt;code&gt;.mdc&lt;/code&gt; architecture rules&lt;/li&gt;
&lt;li&gt;Supabase SSR auth (the secure way)&lt;/li&gt;
&lt;li&gt;4 pre-configured MCP servers (GitHub, Filesystem, Supabase, Browser)&lt;/li&gt;
&lt;li&gt;Working auth flow (login, signup, email confirm, protected dashboard)&lt;/li&gt;
&lt;li&gt;Error boundaries, loading states, custom 404&lt;/li&gt;
&lt;li&gt;Complete Architecture Decision Records explaining every "why"
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/vibestackdev/vibe-stack.git
&lt;span class="nb"&gt;cd &lt;/span&gt;vibe-stack &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install
cp&lt;/span&gt; .env.example .env.local
npm run dev

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open it in Cursor. Start building. Watch the AI write architecture instead of spaghetti.&lt;/p&gt;




&lt;h2&gt;
  
  
  try the Premium Stack
&lt;/h2&gt;

&lt;p&gt;If you want the extended version with Stripe subscription management, Resend email templates, n8n automation workflows, and the complete "Vibe Coding Masterclass" -- it's linked in the repo's README.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What hallucination patterns have you been fighting? I'm building new rules every week -- drop your worst AI-generated bugs in the comments and I'll turn them into constraints.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Follow me for more posts on AI-assisted architecture. Next up: "How I use MCP servers to give Cursor superpowers."&lt;/em&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
