<?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: Dave Kurian</title>
    <description>The latest articles on DEV Community by Dave Kurian (@davekurian).</description>
    <link>https://dev.to/davekurian</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%2F3962819%2F50b558da-9a83-43ee-a473-5381fe6bb0d4.png</url>
      <title>DEV Community: Dave Kurian</title>
      <link>https://dev.to/davekurian</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/davekurian"/>
    <language>en</language>
    <item>
      <title>Why 'Write Once, Run Everywhere' Often Falls Short in Practice</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sun, 12 Jul 2026 19:04:45 +0000</pubDate>
      <link>https://dev.to/davekurian/why-write-once-run-everywhere-often-falls-short-in-practice-246c</link>
      <guid>https://dev.to/davekurian/why-write-once-run-everywhere-often-falls-short-in-practice-246c</guid>
      <description>&lt;p&gt;The promise of "write once, run everywhere" has been a lie for thirty years. The desire behind it is real — nobody wants three codebases drifting — but the actual delivery has been: write once, debug everywhere, ship three slightly different apps.&lt;/p&gt;

&lt;p&gt;I've watched teams ship a beautiful web product, then spend six months rebuilding it for iOS, then three more months for Android, then quietly accept that notifications behave differently per platform forever. The fragmentation isn't a bug. It's the default outcome of trying to make fundamentally different rendering systems agree on every pixel. The hardest part isn't the rendering itself — it's the long tail of edge cases where the platforms diverge. A button that wraps to two lines on Android but one on iOS. A modal that scrolls the wrong element on web. A date picker that picks a different timezone offset by platform. Multiply by 200 components and the team is now maintaining three divergent visual languages inside one product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three failure modes of cross-platform
&lt;/h2&gt;

&lt;p&gt;Every cross-platform tool fails in roughly the same three ways. Naming them is the first step to escaping them.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Visual drift.&lt;/strong&gt; The component "looks close" but not identical. A border that renders 1px on web and 0.5pt on iOS. A shadow implemented with &lt;code&gt;box-shadow&lt;/code&gt; on web and a native elevation on Android — and they're never quite the same depth. The team accepts "good enough" and ships three slightly different visual languages under one brand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Behavior drift.&lt;/strong&gt; The component has the same name but different behavior. A &lt;code&gt;&amp;lt;Select&amp;gt;&lt;/code&gt; that opens on mousedown on web and on tap on mobile — fine — but a &lt;code&gt;&amp;lt;Dialog&amp;gt;&lt;/code&gt; that traps focus correctly on web and skips focus restoration on Android. The team writes platform-conditional code inside every component. "Write once" is now write-three-times-disguised-as-one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capability drift.&lt;/strong&gt; Platform X has a feature the others don't. iOS gets haptics. Android gets the back button. Web gets URL state. The "unified" component decides: omit the feature, fake it on the unsupported platforms, or maintain three implementations. All three cost more than writing the component twice with shared types.&lt;/p&gt;

&lt;h2&gt;
  
  
  The contract that actually works
&lt;/h2&gt;

&lt;p&gt;The lie of "write once" comes from conflating one source file with one outcome. The real goal is the latter. A single component definition that produces a visually and behaviorally identical result on web, iOS, and Android requires four things held in tension:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Same component name.&lt;/strong&gt; &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; is &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; is &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt;. Not &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ButtonNative&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;ButtonWeb&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same props.&lt;/strong&gt; &lt;code&gt;variant&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;, &lt;code&gt;onPress&lt;/code&gt; mean the same thing and accept the same values on every platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same design tokens.&lt;/strong&gt; &lt;code&gt;colors.primary.500&lt;/code&gt; resolves to the exact same hex on every platform, even when the underlying color format differs (hex strings on web, native color objects on iOS and Android).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same render contract.&lt;/strong&gt; Tap on mobile equals click on web. Focus trap works the same way. Keyboard navigation is identical. The accessibility tree is identical.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Miss any one of these and the platforms drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this looks like in code
&lt;/h2&gt;

&lt;p&gt;The same component file ships to all three platforms. Web imports from &lt;code&gt;@otfdashkit/ui&lt;/code&gt;; iOS and Android from &lt;code&gt;@otfdashkit/ui-native&lt;/code&gt; — same JSX, same component names, same props.&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&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;@otfdashkit/ui&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;SignupForm&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="nc"&gt;Card&lt;/span&gt; &lt;span class="na"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lg"&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;Stack&lt;/span&gt; &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md"&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;Input&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Email"&lt;/span&gt;
          &lt;span class="na"&gt;placeholder&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"you@example.com"&lt;/span&gt;
          &lt;span class="na"&gt;keyboardType&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&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;Input&lt;/span&gt;
          &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"Password"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;secureTextEntry&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;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt;
          &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt;
          &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Create account
        &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="nc"&gt;Stack&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;Card&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;p&gt;The web bundle ships this as real DOM with platform-native accessibility. The iOS and Android bundles ship the same JSX through a native renderer that maps &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; to the native button on each platform — same &lt;code&gt;variant&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;disabled&lt;/code&gt;, and &lt;code&gt;onPress&lt;/code&gt; semantics, the same tap-target sizes, the same focus behavior. Props that look platform-specific (&lt;code&gt;keyboardType&lt;/code&gt;, &lt;code&gt;secureTextEntry&lt;/code&gt;) are passed through as no-ops on platforms that don't have them. The component contract stays clean, and the screen code never branches on platform.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design tokens flip one theme across every platform
&lt;/h2&gt;

&lt;p&gt;Visual identity comes from tokens, not from the component code. One token file defines the entire theme:&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;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F0F7FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0066FF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#003D99&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;raised&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F7F8FA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;sunken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#EEF0F4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#0B1220&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;secondary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#5A6478&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;inverse&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;'&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="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;6px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;10px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;16px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;999px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;4px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;8px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;16px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;24px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;xl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;32px&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;caption&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;13&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lineHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;1.4&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A theme flip is one swap:&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;applyTheme&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;@otfdashkit/tokens&lt;/span&gt;&lt;span class="dl"&gt;'&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;darkTheme&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;./dark-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;applyTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;darkTheme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Web swaps CSS custom properties. iOS swaps native color references. Android swaps color resources. The component code is untouched. Every &lt;code&gt;&amp;lt;Button variant="primary"&amp;gt;&lt;/code&gt; becomes the new primary color on every platform in the same paint frame. No stale iOS button lingering with the old brand color three weeks after a redesign.&lt;/p&gt;

&lt;p&gt;[[DIAGRAM: design tokens flow from a single source file to three renderers — web, iOS, Android — each translating to its native color and unit format, while every component reads the same token name]]&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI coding agents make this worse (and how to fix it)
&lt;/h2&gt;

&lt;p&gt;AI coding tools accelerate cross-platform work — but they accelerate the drift too. An agent extending a codebase without strict component conventions will happily invent &lt;code&gt;&amp;lt;Button2&amp;gt;&lt;/code&gt; or sprinkle &lt;code&gt;style={{ background: '#0066FF' }}&lt;/code&gt; directly into JSX. The codebase now has 200 components in &lt;code&gt;ui/&lt;/code&gt;, 47 ad-hoc button copies in &lt;code&gt;screens/&lt;/code&gt;, and three different blues for the primary brand color.&lt;/p&gt;

&lt;p&gt;The fix isn't to disable the agent. It's to ship the conventions alongside the components.&lt;/p&gt;

&lt;p&gt;Every full-stack kit ships with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CLAUDE.md&lt;/code&gt; and &lt;code&gt;.cursorrules&lt;/code&gt; describing the component contract — use &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt;, never raw platform primitives; use &lt;code&gt;colors.primary.500&lt;/code&gt;, never inline hex.&lt;/li&gt;
&lt;li&gt;20+ tested prompts in &lt;code&gt;ai/prompts/&lt;/code&gt; — "add a new screen", "swap the brand color", "wire auth to a new backend" — that produce code conforming to the kit's conventions.&lt;/li&gt;
&lt;li&gt;A 24-item design checklist enforced by a script before any kit ships, so the conventions are enforced in CI rather than tribal knowledge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The agent extends the kit. It doesn't regenerate it. That's the difference between an AI-native codebase and an AI-fragmented one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this enables
&lt;/h2&gt;

&lt;p&gt;When the component contract actually holds, several things change at once.&lt;/p&gt;

&lt;p&gt;A new platform is one renderer, not one rebuild. A hypothetical desktop or wearable target adds a fourth &lt;code&gt;@otfdashkit/ui-*&lt;/code&gt; package. The component code, the tokens, the props, the screen code — none of it changes.&lt;/p&gt;

&lt;p&gt;A redesign is one token swap. Change &lt;code&gt;primary.500&lt;/code&gt; from &lt;code&gt;#0066FF&lt;/code&gt; to &lt;code&gt;#0052CC&lt;/code&gt; once. Every component, every platform, every screen updates in the same paint frame. Marketing can ship a brand refresh in an afternoon instead of a quarter.&lt;/p&gt;

&lt;p&gt;AI agents become multipliers, not entropy sources. With the conventions and prompts shipped, an agent adding a new screen produces code that conforms. The codebase stays a kit, not a museum.&lt;/p&gt;

&lt;p&gt;Quality is measurable. The 24-item checklist runs in CI. A new component that drifts on color contrast, focus order, or tap-target size fails the script before it ships.&lt;/p&gt;

&lt;h2&gt;
  
  
  The lie, restated
&lt;/h2&gt;

&lt;p&gt;"Write once, run everywhere" was never the goal. The goal is ship-one-product, render-everywhere-correctly. The mechanism that gets you there isn't a magic compiler. It's a strict contract — same name, same props, same tokens, same behavior — held consistently across renderers.&lt;/p&gt;

&lt;p&gt;OTF ships that contract as ~200 components on three platforms, design tokens that flip themes identically, AI-tool configs that keep agents honest, and full-stack kits that demonstrate it end-to-end. The components are MIT-licensed on npm (&lt;code&gt;@otfdashkit/ui&lt;/code&gt;, &lt;code&gt;@otfdashkit/ui-native&lt;/code&gt;, &lt;code&gt;@otfdashkit/tokens&lt;/code&gt;). The kits own the code outright, ship to production with a single script, and start at $99 (Everything Bundle $149). Live at saas.otf-kit.dev and fitness-preview.otf-kit.dev.&lt;/p&gt;

&lt;p&gt;The myth isn't worth chasing. The contract is.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>SpaceX Buys Cursor for $60B to Build the World's Most Useful AI Models</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sun, 12 Jul 2026 18:04:34 +0000</pubDate>
      <link>https://dev.to/davekurian/spacex-buys-cursor-for-60b-to-build-the-worlds-most-useful-ai-models-1mlh</link>
      <guid>https://dev.to/davekurian/spacex-buys-cursor-for-60b-to-build-the-worlds-most-useful-ai-models-1mlh</guid>
      <description>&lt;p&gt;$60 billion for an editor. Read that again.&lt;/p&gt;

&lt;p&gt;Yes, SpaceX just paid sixty billion dollars for Cursor. All stock. Largest software acquisition ever recorded. Take a second on that sentence, because the next one is the part that actually matters: &lt;strong&gt;at $60B, SpaceX is paying 20–30× Cursor's current revenue.&lt;/strong&gt; That's not a price paid for what Cursor is today — that's a price paid for what the market believes Cursor will be at the end of 2026, when annualized revenue hits $6B (up from $2B today). The model layer just got repriced. Hard. Here's why that matters to anyone writing code on a Tuesday morning.&lt;/p&gt;

&lt;h2&gt;
  
  
  The receipt
&lt;/h2&gt;

&lt;p&gt;The numbers, straight from the &lt;a href="https://themerkle.com/spacex-acquires-cursor-for-60-billion-in-the-largest-software-deal-in-history" rel="noopener noreferrer"&gt;official filing&lt;/a&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$60B, all-stock.&lt;/strong&gt; Every Cursor share converts to SpaceX Class A common at closing, at the volume-weighted average closing price of SPCX at the time. Cursor shareholders aren't cashing out — they're becoming SpaceX shareholders.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;3.4% dilution&lt;/strong&gt; at SpaceX's IPO valuation. Big, but not crushing for a company that raised $75B in its IPO four days ago and is trading well above offer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor today:&lt;/strong&gt; 1M+ paying customers, $2B ARR, built by Anysphere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor by EOY 2026, per the deal's own projected trajectory:&lt;/strong&gt; $6B ARR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close:&lt;/strong&gt; Q3 2026, subject to regulatory approval.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The comps are almost unfair to write. Microsoft bought GitHub for $7.5B in 2018, Salesforce paid $27.7B for Slack in 2021, Activision went to Microsoft for $69B. A software deal isn't supposed to look like this. SpaceX isn't paying for code-editor market share; it's paying for share of the next decade of model-and-software co-evolution — and the market put a price on it accordingly.&lt;/p&gt;

&lt;h2&gt;
  
  
  The thing that already started
&lt;/h2&gt;

&lt;p&gt;Here's the part the headline buries. Per the announcement, SpaceXAI has been &lt;strong&gt;jointly training a model with Cursor for the past several months&lt;/strong&gt;. That model is expected to ship inside both Cursor and Grok Build in the near term. This isn't a buy-and-figure-it-out-later deal. The integration runway is already in flight.&lt;/p&gt;

&lt;p&gt;That's the actual story. Two teams converged on the same product bet before the corporate paperwork caught up. SpaceXAI wants to build "the world's most useful AI models." Cursor has the funnel where every model gets stress-tested by a paying developer within seconds of release. The 20–30× multiple is the market's price for that loop running at $6B ARR instead of $2B.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually use Cursor today
&lt;/h2&gt;

&lt;p&gt;This is the part most "Cursor coverage" skips. If you haven't tried it — and 1M people have, so statistically you probably have — here's the fastest way in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS / Linux&lt;/span&gt;
curl  &lt;span class="nt"&gt;-L&lt;/span&gt; | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default model is good. The move that changes everything is pointing Cursor at OpenRouter so you can swap models per-task without leaving the editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ~/.cursor/settings.json&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cursor.ai.primaryModel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="s2"&gt;"openrouter/anthropic/claude-sonnet-4.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cursor.ai.fallbackModel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="s2"&gt;"openrouter/minimax/minimax-m3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"openrouter.apiKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="s2"&gt;"${OPENROUTER_API_KEY}"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;Cmd-K&lt;/code&gt; opens a prompt whose base URL is yours. Frontier one moment, cheap one the next. The 1M paying customers aren't paying for tab completion — they're paying for the loop where Cursor reads the whole repo, proposes a diff, runs the tests, fixes the tests, and lands the patch.&lt;/p&gt;

&lt;p&gt;That's the loop SpaceX is buying. Not the editor.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the 20–30× signal means for builders
&lt;/h2&gt;

&lt;p&gt;When the public market pays 20–30× revenue for a coding tool, it says one of two things. Either it's wrong and in 18 months Cursor trades at half that multiple — and we're writing the autopsy. Or it's right, and the price of AI-native software just re-rated above every prior SaaS benchmark in history.&lt;/p&gt;

&lt;p&gt;Honest read: in 2026, AI-native tools earn a premium multiple &lt;strong&gt;when they own a workflow loop that compounds with the model.&lt;/strong&gt; Cursor owns the loop from &lt;em&gt;intent&lt;/em&gt; → &lt;em&gt;diff&lt;/em&gt; → &lt;em&gt;test run&lt;/em&gt; → &lt;em&gt;landed PR&lt;/em&gt;. The moat is the user-trained completion corpus, the deep IDE hooks, and the paying-developer feedback loop that ships every improvement back into a billion-token training set. If you're building an AI tool, that's the line you have to clear — not "calls a model," but a loop that gets tighter the more paying users run it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that doesn't move when the model does
&lt;/h2&gt;

&lt;p&gt;Here's where it lands for the rest of us.&lt;/p&gt;

&lt;p&gt;A year ago the AI-editor story was Cursor vs Copilot. Then Claude Code showed up and ate the terminal-and-prompt crowd. Then Lovable, Bolt, Rork, v0 took the no-code side of the same wave. Now SpaceX is putting $60B behind the loop. The &lt;strong&gt;model layer moves every quarter&lt;/strong&gt; — the agents, the editors, the harnesses, the base URLs. Some ship 15× faster than their peers, then a peer ships 16× faster the next month. The harness is, by construction, disposable.&lt;/p&gt;

&lt;p&gt;What compounds across that churn is &lt;strong&gt;what the loop writes into.&lt;/strong&gt; Your components. Your shipping surface. Whether the button the agent just shipped renders the same on web, iOS, and Android without you pasting a third adapter file. That's the layer where a year of agent commits either holds up or doesn't.&lt;/p&gt;

&lt;p&gt;[[COMPARE: the churning AI tool layer vs the durable component layer]]&lt;/p&gt;

&lt;p&gt;The honest framing isn't "Cursor vs a cross-platform kit." It's: use Cursor — it's the best agent loop on the market and $60B just said so out loud — and pair it with a component layer where the AI's diff doesn't break three platforms at once. One API, same component, same behavior on web, iOS, Android. That's the bit that pays you back whether tomorrow's editor is Cursor, Claude Code, or something none of us have heard of yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this gets us
&lt;/h2&gt;

&lt;p&gt;A few things, concretely:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cheaper frontier access, likely.&lt;/strong&gt; A SpaceX-Cursor combo has every incentive to subsidize model access inside Cursor's editor to keep the loop competitive. Watch for Sonnet- and M3-class pricing inside Cursor dropping hard over the next two quarters. (Listed as likely, not confirmed — no terms have been published yet.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real co-trained tools.&lt;/strong&gt; The joint model shipping in Cursor and Grok Build is the first frontier model trained on the inside of a paying-developer feedback loop. Expect it to be noticeably better at long-horizon coding tasks than models trained on scraped GitHub alone — that's the bet, at least.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A repriced market.&lt;/strong&gt; 20–30× ARR for AI-native tools sets the floor for the next 18 months of funding rounds. Smaller AI-coding startups are now valued against this comp, whether their loops deserve it or not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A reminder that loops beat wrappers.&lt;/strong&gt; The Cursor story is "a loop that gets tighter with usage." If your AI tool doesn't have one, the 20–30× multiple was never going to be yours.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What to watch
&lt;/h2&gt;

&lt;p&gt;Three signals will tell you whether the 20–30× bet holds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One:&lt;/strong&gt; what ships inside Cursor and Grok Build within 60 days of closing. If it's a clearly better coding model than the public frontier, the multiple was right.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two:&lt;/strong&gt; regulatory posture. A $60B all-stock deal in a sector the FTC has already come after GitHub and Activision for won't close quietly. Watch for remedies, not refusal.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three:&lt;/strong&gt; the cohort. When the deal closes, every Cursor employee is now a SpaceX shareholder with vested upside tied to SPCX. Retention is the real risk — and the first thing SpaceX should solve, because the loop is the asset.&lt;/p&gt;

&lt;p&gt;Deal closes Q3 2026. By then the next Cursor competitor — probably already incubated inside SpaceXAI — will have shipped something. The model layer keeps moving. Build on something that doesn't.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Claude Code's Auto Mode Now Default on Major Cloud Platforms</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sun, 12 Jul 2026 16:06:29 +0000</pubDate>
      <link>https://dev.to/davekurian/claude-codes-auto-mode-now-default-on-major-cloud-platforms-3eci</link>
      <guid>https://dev.to/davekurian/claude-codes-auto-mode-now-default-on-major-cloud-platforms-3eci</guid>
      <description>&lt;p&gt;Anthropic shipped Claude Code 2.1.207 on Friday, and the one-line changelog entry hides a more interesting story than the headline suggests. Auto mode is now on by default on Amazon Bedrock, Google Vertex AI, and Microsoft Azure Foundry. The auto-mode classifier — a second model that reviews every pending file edit, build, commit, and pull request against the conversation context before execution — has been an opt-in feature since Claude Code launched in early 2026. After Friday's update, every enterprise team running Claude Code through those platforms has it on by default.&lt;/p&gt;

&lt;p&gt;The reasoning Anthropic published alongside the change is the part worth sitting with. Their own telemetry found that 93% of prompts in manual mode were approved reflexively, without actual review. That number is a quiet indictment of how the manual-approval flow actually worked in practice: humans were rubber-stamping everything, and the approval gate was theatre. Auto mode replaces the rubber stamp with a second model that is, at minimum, not bored.&lt;/p&gt;

&lt;p&gt;[[COMPARE: Claude Code auto mode before 2.1.207 vs after 2.1.207]]&lt;/p&gt;

&lt;p&gt;That is worth appraising on its own terms before we get to the governance shift, because the two arguments are independent. The classifier is, on its own merits, probably a better safety net than an exhausted engineer mashing &lt;code&gt;y, y, y, y&lt;/code&gt; at 4pm on a Thursday — and it is also a default that your compliance team did not sign off on. Both of those can be true.&lt;/p&gt;

&lt;h2&gt;
  
  
  What auto mode actually is
&lt;/h2&gt;

&lt;p&gt;Auto mode sits between two extremes that enterprise teams have been picking between since Claude Code launched. On one end is manual mode, the default for interactive CLI sessions: every file write, shell command, test run, and commit requires a human to approve it. On the other end is the mode that bypasses all safety checks and is unsuitable for production environments holding SSH keys, secrets, and live credentials — a tool for sandboxes, not enterprise machines.&lt;/p&gt;

&lt;p&gt;Auto mode is the middle path. Before executing any action, a dedicated second model — the classifier — reviews the pending operation against the conversation context and decides whether to allow or block it. The classifier monitors three categories of risk; scope escalation — the agent drifting from "fix the lint errors in this file" to "rewrite the auth module" — is the one named in the release notes.&lt;/p&gt;

&lt;p&gt;[[DIAGRAM: pending action enters queue → classifier reviews against conversation context → allow or block → execute or stop]]&lt;/p&gt;

&lt;p&gt;The key property is that the classifier is not the same model doing the work. It's a separate model with a narrower job, which means its failure mode is different from the agent's. When the agent is confidently wrong, the classifier is still reviewing. When the agent is being sycophantic, the classifier isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  The inversion: from opt-in to default-on
&lt;/h2&gt;

&lt;p&gt;Here is the part that compliance teams should read twice. Before Friday, a security team that had not explicitly evaluated auto mode was protected by inertia — the feature was off, by default, and enabling it required setting an environment variable. After Friday, that same team must take affirmative action to keep auto mode off. The setting name, per the release: &lt;code&gt;disableAutoMode&lt;/code&gt;, set in managed settings.&lt;/p&gt;

&lt;p&gt;The operational consequence is a reversal of who bears the burden of action. In regulated industries — finance, healthcare, anything where every change to how AI agents interact with production systems potentially triggers a review — that inversion is the actual news. The classifier may be excellent. The classifier may be the right default for most teams. Neither of those facts changes that an enterprise-wide change in how AI agents touch production systems just shipped on a Friday afternoon with a one-line changelog entry.&lt;/p&gt;

&lt;p&gt;That is the governance story, and it is bigger than the engineering story.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things to do this week
&lt;/h2&gt;

&lt;p&gt;For teams running Claude Code through Bedrock, Vertex, or Azure Foundry:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Confirm the current state.&lt;/strong&gt; Check whether auto mode is on for your team. If you have not set &lt;code&gt;disableAutoMode&lt;/code&gt; in managed settings, the answer is now yes after the next update propagates. The signal to grep for in your org's run logs is whether agent-initiated commits are appearing without a corresponding approval prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Decide, explicitly.&lt;/strong&gt; For teams that want manual control, set &lt;code&gt;disableAutoMode&lt;/code&gt; in managed settings before the next Claude Code update rolls out. The exact location depends on the platform — Bedrock, Vertex, and Azure Foundry each have their own managed-settings surface — but the flag name is the same across all three. Do not assume the previous opt-in environment variable still works; per the release notes, it has been removed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. For teams keeping auto mode on, scope the agent.&lt;/strong&gt; You cannot tune the classifier directly, but you can constrain what the agent has access to. The narrower the filesystem, the fewer credentials, the smaller the blast radius, the less work the classifier has to do. Auto mode is a better default than reflexive human approval — but "better than rubber-stamping" is a low bar, and the classifier is not a substitute for sensible agent scoping.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this gets us — and what doesn't change
&lt;/h2&gt;

&lt;p&gt;The throughput case for auto mode is real. A classifier that catches scope escalation and blocks genuinely risky operations means your team can hand off the boring 80% — lint cleanups, dependency bumps, test scaffolding, the rote PRs that everyone skips review on anyway — and spend human review time on the 20% that actually changes product behavior. For most teams, that is a genuine productivity enable, not a marketing line.&lt;/p&gt;

&lt;p&gt;What does not change: the components your agent writes still have to render identically on every platform your users actually touch. An auto-mode agent editing forty files an hour is going to drift toward platform-specific patterns fast — web-only CSS here, an iOS-only animation there, an Android-only intent filter somewhere else. The model changes. The frameworks it picks change with every release. The output has to compile and look the same on web, iOS, and Android regardless of which model wrote the call site.&lt;/p&gt;

&lt;p&gt;That is the durable layer underneath the tool churn. Use auto mode for throughput, and keep the UI primitives your agent composes against independent of the agent itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Claude Code 2.1.207 is a one-line changelog that flips a governance default. The classifier is the most interesting part — a second model, watching the first, with a different failure mode. The Friday-afternoon rollout is the part that compliance teams should treat as the actual news. Three concrete steps cover both: confirm the state, decide explicitly, scope the agent. The throughput wins are real. The governance posture is now your problem to set, not the platform's.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>claude</category>
      <category>cloud</category>
    </item>
    <item>
      <title>Achieve True Cross-Platform Consistency with OTF's Unified Component System</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sun, 12 Jul 2026 06:03:17 +0000</pubDate>
      <link>https://dev.to/davekurian/achieve-true-cross-platform-consistency-with-otfs-unified-component-system-2j0c</link>
      <guid>https://dev.to/davekurian/achieve-true-cross-platform-consistency-with-otfs-unified-component-system-2j0c</guid>
      <description>&lt;p&gt;Writing a component once and shipping it to web, iOS, and Android — same name, same props, same pixels — is the boring dream every cross-platform framework has been chasing for a decade. Most of them get 80% of the way there and then quietly leak the last 20% as platform-specific escape hatches. We tried most of them. Here's the part that actually works.&lt;/p&gt;

&lt;h2&gt;
  
  
  The parity tax, line by line
&lt;/h2&gt;

&lt;p&gt;Every cross-platform project ships a balance sheet nobody wants to read. Three button components that drift apart over six months. Three inputs where the Android one loses its focus ring. Three loading spinners that look related but aren't. None of it is dramatic. All of it costs.&lt;/p&gt;

&lt;p&gt;The line items:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Build drift.&lt;/strong&gt; You write &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; on Tuesday, ship a new prop on Wednesday on iOS, push the same prop to web on Friday, forget Android until QA pings you three weeks later. The bug isn't the prop — it's the calendar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design drift.&lt;/strong&gt; A designer tweaks the primary colour in Figma on a Tuesday. The web theme updates in a PR the same day. Native ships the old shade for a quarter because the token export from Figma never reached the native repos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Refactor drag.&lt;/strong&gt; Changing one component means auditing three codebases, three type files, three test suites, three sets of snapshots, and three different review queues. The PR is 200 lines. The deployment dance is 2,000.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Three platforms. Three of everything. That's the tax.&lt;/p&gt;

&lt;h2&gt;
  
  
  One component, three platforms, one API
&lt;/h2&gt;

&lt;p&gt;The same import path, the same props, the same JSX. Web:&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&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;@otfdashkit/ui&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;CTA&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="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;startCheckout&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Get started
    &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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Native (iOS + Android, same file):&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;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Button&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;@otfdashkit/ui-native&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;CTA&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="nc"&gt;Button&lt;/span&gt; &lt;span class="na"&gt;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md"&lt;/span&gt; &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;startCheckout&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      Get started
    &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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same &lt;code&gt;variant&lt;/code&gt;. Same &lt;code&gt;size&lt;/code&gt;. Same &lt;code&gt;onPress&lt;/code&gt; shape. The ~200 components in the library cover the ground most apps actually need — buttons, inputs, sheets, lists, dialogs, tabs, toasts, navigation primitives — and every one of them ships with the same surface area on every platform.&lt;/p&gt;

&lt;p&gt;That isn't a marketing claim. It's the API. You write &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; once, you ship it three times.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tokens are the source of truth
&lt;/h2&gt;

&lt;p&gt;Components are half the problem. The other half is colour, spacing, type, radius — the visual grammar. If those drift, the components drift with them, and no amount of component-library discipline will save you.&lt;/p&gt;

&lt;p&gt;Install the token package alongside the components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i @otfdashkit/ui @otfdashkit/ui-native @otfdashkit/tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tokens look like this:&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;tokens&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;@otfdashkit/tokens&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;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;blue&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;900&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="na"&gt;muted&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;gray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;600&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="na"&gt;space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;space&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;md&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;p&gt;Flip &lt;code&gt;bg.primary&lt;/code&gt; from blue to teal once, on one platform. All three update. The same &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; on iOS, Android, and web renders with the same hex, the same padding, the same border-radius. Visual parity is a property of the token graph, not a hope held by the team.&lt;/p&gt;

&lt;p&gt;Theme switching is the same primitive. A dark theme is a second token export. Components don't know it exists — they consume whatever the current theme hands them, and the theme is the only thing that knows which mode is active.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 24-item gate before anything ships
&lt;/h2&gt;

&lt;p&gt;Most cross-platform libraries ship whatever compiles. The cross-platform kits ship after a script gates them against a 24-item design checklist. Touch targets meet platform minimums. Contrast passes WCAG AA. Focus rings are visible against every background token. Safe-area insets are honoured on every sheet and modal.&lt;/p&gt;

&lt;p&gt;The checklist is enforced, not aspirational. A kit doesn't reach &lt;code&gt;npm publish&lt;/code&gt; until the script returns zero. That's the difference between "looks right on my laptop" and "passes review on a real device, in dark mode, with the user's preferred text size".&lt;/p&gt;

&lt;p&gt;A few items from the actual list:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Why it matters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Tap target ≥ 44pt iOS / 48dp Android&lt;/td&gt;
&lt;td&gt;App Store and Play Store floor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contrast ratio ≥ 4.5:1 for body text&lt;/td&gt;
&lt;td&gt;WCAG AA, also just legible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dark-mode parity per token&lt;/td&gt;
&lt;td&gt;No "almost dark" surfaces&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Safe-area inset handling&lt;/td&gt;
&lt;td&gt;Notch, dynamic island, gesture bar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduced-motion fallback&lt;/td&gt;
&lt;td&gt;System setting respected, not ignored&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If a kit fails any one of those, it doesn't ship. The gate is the contract.&lt;/p&gt;

&lt;h2&gt;
  
  
  AI agents extend, they don't regenerate
&lt;/h2&gt;

&lt;p&gt;This is the part most cross-platform stacks get wrong. Hand an AI coding agent a fresh empty repo and it invents its own conventions. Hand it a kit with no documentation and it does the same thing, but louder.&lt;/p&gt;

&lt;p&gt;Every full-stack kit ships the conventions the agent needs to extend the kit instead of replacing it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;my-app/
├── CLAUDE.md            # how the kit is structured
├── .cursorrules         # what the agent must / must not change
├── ai/
│   └── prompts/         # 20+ tested prompts for common tasks
└── src/
    └── ...              # your code, on top of the kit
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; says: this is a cross-platform kit, components live here, tokens flow through here, do not regenerate &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt;. &lt;code&gt;.cursorrules&lt;/code&gt; says the same thing in Cursor's dialect. The 20+ prompts in &lt;code&gt;ai/prompts/&lt;/code&gt; are the ones actually used to extend real kits — add a screen, wire auth, add a Stripe product, scaffold a new page, hook up a server action.&lt;/p&gt;

&lt;p&gt;The agent extends. It doesn't replace. Convention beats configuration, and the convention is written down.&lt;/p&gt;

&lt;p&gt;[[DIAGRAM: one component source feeding web, iOS, and Android through a shared token graph; AI agent reads CLAUDE.md and .cursorrules to extend the kit, not regenerate it]]&lt;/p&gt;

&lt;h2&gt;
  
  
  From dev to production in one script
&lt;/h2&gt;

&lt;p&gt;Cross-platform parity doesn't end at "it renders correctly in dev". You still need a domain, DNS, TLS, and a mobile build that survives App Store review. The SaaS Dashboard kit and the Fitness kit both ship with a deploy script that wires all of it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./scripts/production.sh my-app.example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What it does, in order: validates env, builds web, builds the iOS archive, builds the Android AAB, points DNS at the deployment, issues and pins the TLS cert, runs the post-deploy smoke checks. One command. The script is the same one used internally — the same one that keeps saas.otf-kit.dev and fitness-preview.otf-kit.dev alive.&lt;/p&gt;

&lt;p&gt;That's the boring half of cross-platform nobody talks about at a hackathon and everybody pays for the week before launch.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this enables
&lt;/h2&gt;

&lt;p&gt;A team of two shipping an iOS app, an Android app, and a web app from one component tree. A designer updating one token and seeing it on three platforms the same day. An AI agent that asks "where do I add a new screen?" and gets a real answer from &lt;code&gt;CLAUDE.md&lt;/code&gt; instead of inventing a fourth codebase. A release that doesn't need a "wait, does this work on Android?" channel in Slack on Friday afternoon.&lt;/p&gt;

&lt;p&gt;You can start in two ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Option A — copy-paste CLI, scaffolds into the current directory&lt;/span&gt;
npx otf-kit init

&lt;span class="c"&gt;# Option B — straight npm install, bring your own scaffold&lt;/span&gt;
npm i @otfdashkit/ui @otfdashkit/ui-native @otfdashkit/tokens
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Free MIT SDK on npm. ~200 components, three platforms, one API. Paid full-stack kits at $99 each (Everything Bundle $149) ship auth, billing, DB, Stripe, and the production script wired — you own the code. 15 single-page landing templates at $9 each when all you need is a marketing surface.&lt;/p&gt;

&lt;h2&gt;
  
  
  Closing
&lt;/h2&gt;

&lt;p&gt;Cross-platform parity is one of those problems everyone agrees is real and almost nobody fixes at the level it deserves. The fix isn't a cleverer framework. It's the same component name on every platform, the same token graph feeding it, and a checklist that stops bad builds before they ship. The model layer underneath will keep churning — context windows will grow, prices will drop, agents will get smarter. The render target underneath the model layer won't.&lt;/p&gt;

&lt;p&gt;Write &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; once. Ship it three times. Sleep on launch night.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Bezier AI Launches Conversational Website Builder for Rapid Prototyping</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sun, 12 Jul 2026 03:03:12 +0000</pubDate>
      <link>https://dev.to/davekurian/bezier-ai-launches-conversational-website-builder-for-rapid-prototyping-2nh9</link>
      <guid>https://dev.to/davekurian/bezier-ai-launches-conversational-website-builder-for-rapid-prototyping-2nh9</guid>
      <description>&lt;p&gt;Bezier turns one prompt into a working site. That's the headline, and it earns a real look.&lt;/p&gt;

&lt;p&gt;For years "AI for web" meant autocomplete inside an editor — Copilot filling the next line, Cursor refactoring a function, Replit nudging you through a cloud IDE. Bezier AI is taking a different swing: type a sentence, get a deployable site. It's a category move worth taking seriously, not because the output is magical, but because the speed-of-concept jump is large enough to change how product teams plan their week. A founder who used to wait two engineering-weeks for a prototype can now test three positioning angles before lunch.&lt;/p&gt;

&lt;p&gt;The shift from lines to sentences&lt;/p&gt;

&lt;p&gt;Building a webpage used to require HTML, CSS, JavaScript, and the patience to wire a framework together. No-code platforms collapsed some of that — Webflow, Squarespace, Wix — but the user still chose every block, dragged every column, picked every color. Generative AI inverts the contract. The user describes the destination; the model drives.&lt;/p&gt;

&lt;p&gt;This is what some builders now call vibe coding — natural language as the primary input for software creation. It doesn't eliminate developers. It shifts their focus to architecture, integrations, and the parts of the system that actually need judgment, while the AI handles the repetitive setup. Bezier AI is positioned squarely in that inversion. The launch frames it as a conversational website builder: where Copilot fills the next function and Cursor edits the file you have open, Bezier generates the file (and the layout, and the responsive breakpoints) from a single description. That's the meaningful distinction, and it's why the launch is positioned against the whole category rather than against any single editor.&lt;/p&gt;

&lt;p&gt;[[COMPARE: editor assistants vs full-site generators]]&lt;/p&gt;

&lt;p&gt;How to actually use it today&lt;/p&gt;

&lt;p&gt;The onboarding is the product. You describe the site you want in a sentence or two and the conversational loop begins. The launch gives a useful canonical example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Create a modern SaaS landing page with a dark theme,
pricing section, testimonials, animations,
and responsive design.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single prompt is the input. The output is a functional starting point — hero, sections, basic interactivity — that you can iterate on by talking to it. The model handles layout, design tokens, and the boilerplate wiring. You handle taste, copy, and the parts that need real engineering (auth, payments, data, anything customer-facing).&lt;/p&gt;

&lt;p&gt;A practical session runs roughly like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Describe the destination in plain English. Be specific about audience, vibe, and the sections you need. Vague prompts get vague sites; "modern SaaS landing page with dark theme, three-tier pricing, and a logo wall" produces something sharper than "make me a website."&lt;/li&gt;
&lt;li&gt;Review the first pass as a draft, not a deliverable. Look for the structural decisions — what it chose to put above the fold, what it treated as primary CTA, what it left out. The first render is meant to be argued with.&lt;/li&gt;
&lt;li&gt;Iterate by talking. "Make the hero more confident." "Swap the testimonials for a logo wall." "Tighten the pricing to three tiers." Each turn is a small course correction, and the conversational loop is the point — every refinement stays inside the same session rather than becoming a new ticket.&lt;/li&gt;
&lt;li&gt;Pull what you can keep. Export the components, the design tokens, the structure. Most prompt-driven builders give you code at the end; treat it as starting material, not a final repo.&lt;/li&gt;
&lt;li&gt;Wire the real backend yourself. Auth, payments, data, integrations — that's not what the conversational builder is for. That's where engineering still earns its keep.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;[[DIAGRAM: prompt in → first draft → iterate by talking → export → engineer wires the real backend]]&lt;/p&gt;

&lt;p&gt;Where Bezier fits in a crowded field&lt;/p&gt;

&lt;p&gt;The AI development ecosystem expanded fast, and each tool now owns a different slice.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Copilot and Cursor are editors. They live inside your file and assist line-by-line. Strong for engineers who already know what they're building.&lt;/li&gt;
&lt;li&gt;Replit is a cloud IDE plus AI. The environment and the model are bundled; you stay in the browser.&lt;/li&gt;
&lt;li&gt;Lovable, Bolt.new, and v0 popularized prompt-driven interface generation — strong for designers and founders who think in screens, not files.&lt;/li&gt;
&lt;li&gt;Bezier AI is targeting the same prompt-driven audience — founders, agencies, marketers, small businesses — but is leaning harder than most into the conversational bit. The pitch is one description, one working site, then a back-and-forth loop to refine it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two philosophies, both valid. Editor assistants keep you in code; full-site generators trade code-fidelity for speed-of-concept. Bezier is firmly in the second camp, which is why the launch is framed against the entire category rather than against any one editor.&lt;/p&gt;

&lt;p&gt;The honest limits&lt;/p&gt;

&lt;p&gt;A prompt-driven builder earns its place on week one and tests your patience by week three. Three things to know before you commit:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Customization has a ceiling. The model is making thousands of small decisions for you. Override enough of them and you've effectively rewritten the site by hand, at which point a real codebase is faster than fighting the generator.&lt;/li&gt;
&lt;li&gt;Generated code is a draft. It works. It probably isn't optimized. Bundle size, accessibility, semantic markup, and edge cases in responsive layout are all places you'll want a human eye before it ships to real users.&lt;/li&gt;
&lt;li&gt;Reliance on AI accuracy is real. The model doesn't know your brand, your customers, or the regulatory shape of your industry. Treat the output as a fast first draft that still needs review.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For product teams specifically, the math is simple: if you're trying to decide whether to spend two engineer-weeks prototyping an idea, spending an afternoon with a conversational builder first is now the obviously correct move. The risk of being wrong early is much smaller when the early draft takes an hour.&lt;/p&gt;

&lt;p&gt;What this enables&lt;/p&gt;

&lt;p&gt;The interesting part of Bezier AI isn't the site it produces — it's what the conversational workflow enables for the people using it. A founder can test three positioning angles before lunch. A marketer can ship a campaign page without filing a ticket. A designer can rough out five layout directions and pick one before opening Figma. That speed is the real product; the code is the artifact, and the artifact is disposable in a way the underlying idea is not.&lt;/p&gt;

&lt;p&gt;It also surfaces a layer that's easy to miss. When the generation step is fast and conversational, the part that &lt;em&gt;doesn't&lt;/em&gt; change becomes more valuable, not less. The churn above this layer — which model, which builder, which prompt style — is going to keep moving. New launches will keep arriving on a monthly cadence; that's the new normal. The durable layer underneath — the components, the conventions, the cross-platform primitives that survive every model swap — is what carries the project from prototype to production.&lt;/p&gt;

&lt;p&gt;That's the bet worth making. Use the conversational builder for the speed it gives you in week one. Build on primitives that hold up when the model changes underneath you. The site you ship in an hour should still be the site you're proud of in a year — and that part is yours, not the model's.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Cursor's AI Agent Takes Aim at Anthropic's Claude Cowork</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 18:06:01 +0000</pubDate>
      <link>https://dev.to/davekurian/cursors-ai-agent-takes-aim-at-anthropics-claude-cowork-9bp</link>
      <guid>https://dev.to/davekurian/cursors-ai-agent-takes-aim-at-anthropics-claude-cowork-9bp</guid>
      <description>&lt;h2&gt;
  
  
  Claude Cowork and Cursor's agent are racing for the same seat — your daily workflow
&lt;/h2&gt;

&lt;p&gt;Claude Cowork works like a virtual colleague. It plans tasks, edits files, runs tests, drafts emails, organizes folders — then reports back. Cursor's response is a general-purpose agent built on top of the IDE it already owns. Two genuinely different bets on where autonomy lives, and they are about to compete for the same seat at your desk.&lt;/p&gt;

&lt;p&gt;For years the IDE was an autocomplete machine. The bottleneck moved from "can I edit this file" to "can I delegate this workflow," and Cowork and Cursor's agent are racing to own that delegation. Here is the actual thesis: whichever agent you drive today, the part that survives the churn is the repo underneath — its conventions, its tests, its type boundaries. That is the durable layer, and that is where you should spend your energy.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Claude Cowork actually does
&lt;/h2&gt;

&lt;p&gt;Claude Cowork is the asynchronous one. You give it a goal, it accesses your computer, plans the steps, edits files, runs tests, and reports back. Anthropic launched it as an extension of its reasoning models, and the framing matters: this is not an editor feature, it is a colleague.&lt;/p&gt;

&lt;p&gt;The numbers tell the story. Its 200,000-token context window lets it grasp project architecture in ways autocomplete tools rarely match. Early tests show it excels at complex refactoring: one prompt can trigger changes across 15 files, test runs, bug fixes and a clean commit message. It also handles non-coding chores — drafting emails, organizing folders — which is the part that actually changes your day.&lt;/p&gt;

&lt;p&gt;If you have ever wished for a junior engineer who could take a ticket and come back with a PR, this is the closest thing shipping today.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Cursor's agent is shipping
&lt;/h2&gt;

&lt;p&gt;Cursor took a different path. Built as a complete editor — a VS Code fork loaded with context-aware intelligence — it emphasized speed and familiarity first. You index your codebase, chat with your project, and the editor knows what you are looking at.&lt;/p&gt;

&lt;p&gt;The feature list as it stands today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time autocomplete&lt;/li&gt;
&lt;li&gt;Inline chat&lt;/li&gt;
&lt;li&gt;Composer mode for iterative refinement&lt;/li&gt;
&lt;li&gt;Agent mode for multi-file changes with previews before commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Developers who switched from traditional IDEs often cite the smooth codebase understanding as the biggest draw. Where Cursor lagged was autonomy. A CoworkHow analysis from January placed Claude Cowork at the high-autonomy end of the spectrum for multi-step work, with Cursor sitting in the middle. The &lt;a href="https://www.webpronews.com/cursors-bold-push-into-ai-agents-puts-it-on-collision-course-with-anthropic/" rel="noopener noreferrer"&gt;new general-purpose agent project&lt;/a&gt; is Cursor's attempt to close that gap without giving up its IDE strengths.&lt;/p&gt;

&lt;p&gt;[[COMPARE: Claude Cowork vs Cursor's agent]]&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the autonomy actually lives
&lt;/h2&gt;

&lt;p&gt;The cleanest split is where the autonomy sits in your day. Cowork is a colleague that comes to your machine — kick it off, walk away, come back to a report. Cursor's agent is something that grows inside your editor — you stay in the loop, review previews, commit when the diff looks right.&lt;/p&gt;

&lt;p&gt;Both can do multi-file edits. Both can run tests. The real difference is whether your daily flow is "delegate and review later" or "drive and commit now." If you live in your editor and want previews before every commit, Cursor's surface is the fit. If you want a teammate who takes a ticket and returns a PR, Cowork is the closer match.&lt;/p&gt;

&lt;p&gt;The competition between them is not really about features. It is about who owns the developer's daily workflow, and that is decided by surface, not by spec sheet.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Claude Cowork today
&lt;/h2&gt;

&lt;p&gt;Cowork is at its best when you give it a concrete goal — not open-ended exploration. Three patterns that work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Refactor sweep.&lt;/strong&gt; "Find every place we call the legacy config parser and replace it with v2, then run the test suite." It plans, edits, tests, and hands you a diff with a clean commit message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test repair.&lt;/strong&gt; "The CI suite is red on the auth module. Diagnose, fix, and verify locally." Async mode means you walk away and come back to a report.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chore work.&lt;/strong&gt; "Inbox triage, draft replies for the top five threads, file the rest under &lt;code&gt;waiting/&lt;/code&gt;." The non-coding tasks are not a gimmick — they buy you hours a week.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The general shape: state the goal in one paragraph, give it access, and let it run. The 200k context window means it can carry the whole project in its head while it works.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to use Cursor's agent today
&lt;/h2&gt;

&lt;p&gt;Cursor's setup is different because the editor is the surface. The flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the editor and let it index the repo. Codebase awareness is the whole game.&lt;/li&gt;
&lt;li&gt;Use project chat to ask codebase-wide questions — "Where does the rate limiter live?" "What calls into the auth middleware?" You are training yourself to treat the editor as a teammate that has read the code.&lt;/li&gt;
&lt;li&gt;Switch to agent mode when you want multi-file changes. Cursor shows you the diff preview before anything is committed — this is the safety net.&lt;/li&gt;
&lt;li&gt;Use Composer mode for iterative refinement on a single change. Stay in the editor. Tighten the loop.&lt;/li&gt;
&lt;li&gt;Commit only after you have read the preview. The previews are the feature; the agent is the engine.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The CoworkHow analysis placed Cursor in the middle of the autonomy spectrum, which is honest: it is not going to walk off and come back with a PR. It is going to stay next to you and let you drive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that doesn't change when the agent does
&lt;/h2&gt;

&lt;p&gt;[[CONCEPT: the repo is the contract]]&lt;/p&gt;

&lt;p&gt;Here is what survives whether you pick Cowork, Cursor's agent, or whatever ships next quarter: the repo is the contract. Conventions, structure, tests, type boundaries — these are what make an agent useful. Without them, both tools stall on a fresh repo and produce the same vague guesses.&lt;/p&gt;

&lt;p&gt;A repo that tells the agent what to do — where state lives, how tests run, what "done" means — turns a 15-file refactor into a five-minute job. A repo that doesn't turns the same refactor into a forty-minute cleanup. The agent is the worker; the repo is the brief.&lt;/p&gt;

&lt;p&gt;That is the durable layer underneath the tool churn. Build for the agent you have today, and you are ready for the agent that ships next month. Convention beats configuration. Every round.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this enables
&lt;/h2&gt;

&lt;p&gt;Multi-file edits become routine. Tests run before you review. Commits write themselves. The bottleneck moves from "can I do this" to "do I have the right convention" — a real productivity enable most teams under-invest in.&lt;/p&gt;

&lt;p&gt;You do not need to pick a winner to benefit from this round. Pick the agent that fits your surface, put your conventions in writing, and use whichever one is closest when the work arrives. The autonomous coding stack just got real. The repo that survives the agent is the one that was honest about its conventions all along.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Cursor's Ambitious Leap into AI Agents Challenges Anthropic's Claude Cowork</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 17:04:39 +0000</pubDate>
      <link>https://dev.to/davekurian/cursors-ambitious-leap-into-ai-agents-challenges-anthropics-claude-cowork-gj2</link>
      <guid>https://dev.to/davekurian/cursors-ambitious-leap-into-ai-agents-challenges-anthropics-claude-cowork-gj2</guid>
      <description>&lt;p&gt;A single prompt. Fifteen files rewritten. Tests run, bugs surfaced, a clean commit message waiting in the wings. That is Claude Cowork in action — Anthropic's reasoning-model extension that behaves less like a chatbot and more like a colleague who happens to live on your machine. It is the reason Cursor, until now the most loved AI-native code editor, is reportedly building its own general-purpose agent to compete head-to-head.&lt;/p&gt;

&lt;p&gt;This is not a hand-wringing piece. Two real shifts are happening at once: Claude Cowork is climbing the autonomy ladder into multi-step async work, and Cursor is reaching up to meet it. If you build software for a living, you will use both within the year. Here is what each actually does, where they win, and the part of the stack that does not care which agent wrote it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What makes Cowork exciting
&lt;/h2&gt;

&lt;p&gt;Cowork's pitch is the one Anthropic keeps paying off: bigger context, longer reasoning, and now — physical reach. As an extension of Anthropic's reasoning models, the agent accesses a user's computer, works asynchronously, and reports back. You start a refactor in the morning; by lunch it has edited files, run the test suite, and queued a commit.&lt;/p&gt;

&lt;p&gt;The numbers worth holding onto: a &lt;strong&gt;200,000-token context window&lt;/strong&gt;, and a single prompt that early tests show triggering changes across &lt;strong&gt;15 files&lt;/strong&gt; including test runs, bug fixes, and a clean commit message. That is not autocomplete with extra steps. That is a junior engineer scoping a PR.&lt;/p&gt;

&lt;p&gt;The non-coding chores get less attention but matter as much: drafting emails, organizing folders, the slow admin that eats a senior engineer's afternoon. Cowork handles that too, which is why Anthropic framed it as a colleague, not a coding tool. The interesting bit is not the benchmark, it is the shape of the feedback loop — kick off, walk away, come back to a diff.&lt;/p&gt;

&lt;p&gt;That is genuinely exciting. A 200k context window applied to "actually do the work" turns the model from oracle into operator.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Cursor is shipping in response
&lt;/h2&gt;

&lt;p&gt;Cursor's roots are different. The current editor feels, per the &lt;a href="https://www.webpronews.com/cursors-bold-push-into-ai-agents-puts-it-on-collision-course-with-anthropic/" rel="noopener noreferrer"&gt;WebProNews report&lt;/a&gt;, like a VS Code fork loaded with context-aware intelligence. Users index entire codebases, chat with projects, and lean on Agent mode for multi-file changes with previews before commits. Developers who switched from traditional IDEs often cite the smooth codebase understanding as the biggest draw.&lt;/p&gt;

&lt;p&gt;The current feature set gives a sense of the shape Cursor is extending from:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Real-time autocomplete&lt;/strong&gt; that has been the headline Cursor feature for years&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline chat&lt;/strong&gt; on selected code&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composer mode&lt;/strong&gt; for iterative refinement across files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent mode&lt;/strong&gt; with diff previews before any file is touched&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The new general-purpose agent project is the natural next step. Two people familiar with the project, speaking to The Information, describe an effort to push beyond code completion into the &lt;strong&gt;broader autonomy&lt;/strong&gt; Cowork already owns. The sourcing matters: this is not vapor. It is a stated strategic move with engineering effort behind it, and it directly acknowledges the gap — Cursor's autonomy has, until now, lagged behind dedicated agents.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually try both today
&lt;/h2&gt;

&lt;p&gt;Comparison posts often skip the install. Both tools are live and one will take you ten minutes to evaluate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cursor&lt;/strong&gt; — install the editor, point it at a real repo, and turn on the model picker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# macOS&lt;/span&gt;
brew &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--cask&lt;/span&gt; cursor

&lt;span class="c"&gt;# Then inside the app:&lt;/span&gt;
&lt;span class="c"&gt;# 1. Open a project you've actually shipped&lt;/span&gt;
&lt;span class="c"&gt;# 2. Cmd+Shift+P -&amp;gt; "Cursor: Index Project"&lt;/span&gt;
&lt;span class="c"&gt;# 3. Settings -&amp;gt; Models -&amp;gt; pick Claude Sonnet / GPT-4o / your preference&lt;/span&gt;
&lt;span class="c"&gt;# 4. Toggle into Agent mode for a multi-file refactor&lt;/span&gt;
&lt;span class="c"&gt;# 5. Watch the diff previews stack before you commit anything&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Claude Cowork&lt;/strong&gt; — available through Anthropic's Claude surfaces with computer access as the headline capability. The single command that converts skeptics is the async refactor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Through the Claude desktop client or API&lt;/span&gt;
claude &lt;span class="s2"&gt;"refactor the auth module to use the new session helper, &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;
  run the test suite, and draft a PR description including the diff summary"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That one prompt — and watching it fan out across 15 files — is the moment Cowork clicks. Then open the same task in Cursor's Agent mode and compare three things: the diff preview cadence, the breadth of files touched, and how much hand-holding each needs to keep the test suite green.&lt;/p&gt;

&lt;p&gt;That you can switch between them in a Saturday afternoon is itself the story. The agent war is no longer theoretical.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where each wins right now
&lt;/h2&gt;

&lt;p&gt;A January CoworkHow analysis placed Claude Cowork at the high-autonomy end of the spectrum for multi-step work. Cursor, the same analysis notes, sat in the middle — full positioning beyond that point was cut from the source I am working from, so do not quote me on a ranking it did not finish making. With that caveat, the shape of the split as of mid-2026 looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pick Claude Cowork when&lt;/strong&gt;: refactors span many files, the task crosses coding and non-coding territory, or you can hand over the keyboard for an hour and want a clean PR back. The async model is the point — fire, walk away, return to a tree you can review.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pick Cursor when&lt;/strong&gt;: you live in the editor all day, want low-latency inline completions, need diff previews to feel safe before commits, or your team standardized on a VS Code-derived workflow. The "smooth codebase understanding" the converted developers cite only matters if you are sitting in the editor while it happens.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Use both&lt;/strong&gt;: Cursor for the typing loop and the tight feedback cycle, Cowork for the long async refactor that does not need your eyes on it. Right now they do not even contend on the same surface — Cursor owns the editor, Cowork owns the agent runtime — which is exactly why Cursor is now building one of its own.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The interesting strategic tension is this: Cursor's bet is that an editor with deep codebase understanding and a pre-existing user base can extend up into the agent layer faster than a reasoning model lab can extend down into the IDE. Cowork has the head start. Cursor has the typing-loop lock-in. Both bets are defensible; only execution will tell.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that does not change when the model does
&lt;/h2&gt;

&lt;p&gt;Here is the through-line. Last year the question was "which autocomplete is best." This year it is "which agent hands back the cleanest diff across 15 files." Next year it will be another model, another agent, another context window headline.&lt;/p&gt;

&lt;p&gt;The components you ship do not get to churn at that pace. A Button on web needs to still be a Button on iOS and Android next quarter, regardless of which agent wrote it or which context window drafted the spec. The part of the stack that owns the durable contract between your code and your users — the same component, behaving the same, on every platform, with one API — is what survives when the agent underneath gets swapped.&lt;/p&gt;

&lt;p&gt;Use Cursor and Cowork together. They are both genuinely good. Then put the durable layer underneath them. That is the only stack the agent wars do not touch.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>China's AI Developers Pivot to Homegrown Tools After Claude Code Backdoor Alert</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 14:07:47 +0000</pubDate>
      <link>https://dev.to/davekurian/chinas-ai-developers-pivot-to-homegrown-tools-after-claude-code-backdoor-alert-1ndl</link>
      <guid>https://dev.to/davekurian/chinas-ai-developers-pivot-to-homegrown-tools-after-claude-code-backdoor-alert-1ndl</guid>
      <description>&lt;p&gt;A government vulnerability database just named a specific AI coding agent as a security risk and told developers to uninstall it. That hasn't happened before — at least not at this scale, not from China's NVDB, and not against a tool as widely adopted as Claude Code.&lt;/p&gt;

&lt;p&gt;On July 11, China's National Vulnerability Database (NVDB), overseen by the Ministry of Industry and Information Technology, published an alert claiming multiple versions of Anthropic's Claude Code contained a security "back door" capable of sending user locations and identities to remote servers without consent. The advisory told local organisations to uninstall the affected versions immediately or upgrade to patched releases. Cai Peng, a Beijing-based cybersecurity partner at Zhong Lun Law Firm, &lt;a href="https://www.scmp.com/tech/tech-trends/article/3360148/beyond-claude-code-chinese-ai-tools-poised-benefit-after-back-door-alert" rel="noopener noreferrer"&gt;told the South China Morning Post&lt;/a&gt; he expected more Chinese companies to abandon foreign AI tools, driven by mounting security concerns and what he called the country's "strategic imperative" for tech self-reliance.&lt;/p&gt;

&lt;p&gt;This is the moment to stop treating your AI coding toolchain as a productivity decision and start treating it as a part of your security perimeter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the NVDB actually flagged
&lt;/h2&gt;

&lt;p&gt;A "back door" is a strong word, and it's used carefully. In vulnerability-disclosure language it usually means one of three things: a hidden network egress path the user didn't consent to, an undocumented account or credential, or an unauthenticated remote-access mechanism. The NVDB's wording — software that "could send user locations and identities to remote servers without consent" — maps cleanly onto the first definition. That's an architectural claim about the product, not a configuration mistake.&lt;/p&gt;

&lt;p&gt;Two details separate this advisory from a routine CVE:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;It targets the tool, not a deployment.&lt;/strong&gt; Most vulnerability reports name a specific version of a library with a patch path. The NVDB is naming a flagship AI coding product and telling users to remove it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It comes from a state-level database.&lt;/strong&gt; When MIIT-overseen infrastructure publishes an advisory, the downstream effect on procurement, vendor approval, and corporate policy is structural. Compliance teams and purchasing decisions move within days, not weeks.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For developers inside China, the practical translation is straightforward: if your employer is a state-owned enterprise, a financial institution, or a company handling any data covered by the Cybersecurity Law or the Data Security Law, the Claude Code install is now a compliance question, not a productivity question. The patch path the NVDB offers ("upgrade to patched releases") is contingent on Anthropic shipping a version the agency considers clean, and on that version being reachable inside China's network.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this advisory is different from a normal CVE
&lt;/h2&gt;

&lt;p&gt;The coding-agent category has been quietly exempted from most enterprise security reviews because it reads as "developer tooling" — productivity, not infrastructure. The NVDB just ended that exemption.&lt;/p&gt;

&lt;p&gt;The interesting question is not whether Claude Code specifically is a back door. It's whether any AI coding agent that runs as a long-lived process on a developer's machine, with network access and access to source code, deserves the same scrutiny you'd give a database or a CI runner. Most teams have never run that audit. The Chinese developers who run it now will set a pattern that propagates.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the domestic alternatives are offering
&lt;/h2&gt;

&lt;p&gt;The article doesn't name specific Chinese AI coding products, and I'm not going to invent any here. What it does name is the &lt;em&gt;category&lt;/em&gt; of advantage Cai Peng expects to drive adoption. The shape is consistent across the domestic space:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data residency&lt;/strong&gt; that stays inside mainland networks by default&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model deployments&lt;/strong&gt; that can be audited under China's algorithm registry&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vendor accountability&lt;/strong&gt; through entities MIIT can actually reach&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pricing&lt;/strong&gt; that undercuts dollar-denominated tools once FX and licensing terms are factored in&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a developer evaluating any candidate — domestic or otherwise — the practical audit is the same four steps:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Confirm the tool's data residency in writing&lt;/span&gt;
&lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s2"&gt;"data residency|data location|境内|domestic"&lt;/span&gt; vendor-contract.pdf

&lt;span class="c"&gt;# 2. Check whether the model is on the algorithm registry&lt;/span&gt;
&lt;span class="c"&gt;#    (ask the vendor for their 备案号 / filing number)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Capture egress — does the agent phone home at runtime?&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-w&lt;/span&gt; agent.pcap &lt;span class="s1"&gt;'host &amp;lt;vendor-api-endpoint&amp;gt;'&lt;/span&gt;
&lt;span class="c"&gt;# Inspect agent.pcap in Wireshark. Anything not in the contract is a finding.&lt;/span&gt;

&lt;span class="c"&gt;# 4. Test in a sandbox with synthetic code first&lt;/span&gt;
git clone 
&lt;span class="c"&gt;# Run the agent against it. Watch what it sends.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last block — actually capturing the agent's network traffic during a normal coding session — is the audit step most teams skip. The NVDB's claim is fundamentally an egress claim. The only way to verify it against any tool is to watch the packets.&lt;/p&gt;

&lt;h2&gt;
  
  
  A fair comparison, with the gaps named
&lt;/h2&gt;

&lt;p&gt;The article doesn't benchmark specific Chinese AI coding tools against Claude Code on speed or accuracy, so neither will I. A side-by-side on what's actually knowable looks roughly like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dimension&lt;/th&gt;
&lt;th&gt;Claude Code (pre-patch)&lt;/th&gt;
&lt;th&gt;Domestic Chinese AI coding tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Regulatory posture in China&lt;/td&gt;
&lt;td&gt;Subject of NVDB advisory&lt;/td&gt;
&lt;td&gt;Aligned with MIIT and CAC registries&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data residency&lt;/td&gt;
&lt;td&gt;Cross-border by default&lt;/td&gt;
&lt;td&gt;Mainland-resident by default&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Auditability of model&lt;/td&gt;
&lt;td&gt;Foreign vendor, foreign jurisdiction&lt;/td&gt;
&lt;td&gt;Domestic vendor, algorithm-registry filing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Procurement friction&lt;/td&gt;
&lt;td&gt;Rising&lt;/td&gt;
&lt;td&gt;Lower&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The honest gap: foreign tooling still has a meaningful lead in raw coding-agent capability on certain workloads, and that gap matters when the team is shipping product, not filing compliance reports. Cai Peng's point isn't that domestic tools are better today. It's that the security and sovereignty calculus is now bigger than the capability delta.&lt;/p&gt;

&lt;p&gt;For teams that have to move regardless, the play is to start the migration on internal tools and non-customer-facing code, where a regression costs less. Customer-facing production code can wait until the new tool has earned trust on real workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrating without breaking Friday
&lt;/h2&gt;

&lt;p&gt;A clean migration is incremental and reversible. Treat it like a refactor, not a procurement event.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Days 1–3: Inventory.&lt;/strong&gt; List every place the tool is installed: dev laptops, CI runners, internal portals, agent sandboxes. Capture the config so you know what you'll need to replicate in the replacement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Export your current agent config&lt;/span&gt;
&amp;lt;your-ai-tool&amp;gt; config list &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ./pre-migration-config.txt

&lt;span class="c"&gt;# Snapshot installed MCP servers, hooks, and slash commands&lt;/span&gt;
&lt;span class="c"&gt;# (these are usually the things teams forget they depend on)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Days 4–7: Pilot.&lt;/strong&gt; One team, one non-customer-facing repo, one week. Run the new tool in shadow mode — it suggests, a human reviews and commits. Capture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Acceptance rate (suggestions kept / suggestions made)&lt;/li&gt;
&lt;li&gt;Latency p50 and p95&lt;/li&gt;
&lt;li&gt;Network egress volume and destinations&lt;/li&gt;
&lt;li&gt;Hallucinated APIs or invented package names&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Days 8–14: Compare.&lt;/strong&gt; Score the pilot against the baseline. If acceptance is below ~70%, the tool isn't ready for broader rollout. If egress goes anywhere unexpected, that's a security finding regardless of who signed the vendor contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Days 15–30: Roll out.&lt;/strong&gt; Default the new tool for new code. Keep the old tool available for break-glass. Migrate existing code only when a human next touches it.&lt;/p&gt;

&lt;p&gt;The mistake most teams make is treating this as a procurement decision instead of an engineering one. You're not buying software. You're changing how code gets written. The plan should look like a refactor plan: incremental, reversible, measured.&lt;/p&gt;

&lt;h2&gt;
  
  
  The durable layer underneath the churn
&lt;/h2&gt;

&lt;p&gt;Here's the part that doesn't change when you swap Claude Code for a domestic alternative, or when Anthropic ships a patched release and the NVDB retracts the advisory: the components your users actually see.&lt;/p&gt;

&lt;p&gt;A button is a button. A form is a form. Whether the agent that wrote them was trained in San Francisco or Shenzhen, whether it phones home or doesn't, whether it costs $20 a month or nothing — the rendered output on the user's screen has to look and behave the same on web, iOS, and Android. That's the layer below the model churn, and it's the only layer the team fully owns.&lt;/p&gt;

&lt;p&gt;[[CONCEPT: the same component on web, iOS, and Android — the durable layer under the AI tool churn]]&lt;/p&gt;

&lt;p&gt;The AI coding tool you pick is a decision you'll revisit every 6–18 months. The component system underneath it is the thing you stop thinking about. Build that layer once, against an API your team controls, and the tool churn stops being a crisis every time a regulator — or a vendor — forces a swap.&lt;/p&gt;

&lt;h2&gt;
  
  
  Close
&lt;/h2&gt;

&lt;p&gt;The NVDB advisory is the first state-level vulnerability notice aimed squarely at an AI coding agent. It won't be the last, in China or elsewhere. Cai Peng is right that the strategic imperative is real, but the practical imperative — auditing what your tools send and where — is one every team should be running regardless of which side of any border they sit on.&lt;/p&gt;

&lt;p&gt;For Chinese developers and the companies that employ them, the shift is accelerating. For everyone else, the lesson is simpler: your AI toolchain is now a part of your security perimeter, and it deserves the same scrutiny you'd give a database or a deployment target.&lt;/p&gt;

&lt;p&gt;When the model changes, the durable thing — the component, the API contract, the rendered output — is what carries the team through. Build that once, and the rest is just configuration.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Claude Code's Mid-Year AI Boost: Enhanced Workflows and New Features</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 10:05:41 +0000</pubDate>
      <link>https://dev.to/davekurian/claude-codes-mid-year-ai-boost-enhanced-workflows-and-new-features-1e21</link>
      <guid>https://dev.to/davekurian/claude-codes-mid-year-ai-boost-enhanced-workflows-and-new-features-1e21</guid>
      <description>&lt;h2&gt;
  
  
  Claude Code's mid-year update: in-app browser, Sonnet 5, and the durable layer underneath the model churn
&lt;/h2&gt;

&lt;p&gt;An in-app browser shipping inside Claude Code is a quietly big deal. It's the difference between asking an agent "how does X work" and watching it open the actual doc page and read it for you. Same loop, different cost.&lt;/p&gt;

&lt;p&gt;The mid-year Claude Code update bundles three things, per the official summary: an in-app browser, the Sonnet 5 model, and a workflow-streamlining pass that adds setup diagnostics and upgraded safety features. All three are framed in the announcement as steps toward "continuous evolution" — a phrase that usually means nothing, except this time it maps onto something real. What it actually means is that the agent keeps getting more capable every release without you having to re-learn the surface area.&lt;/p&gt;

&lt;h2&gt;
  
  
  What shipped — and which piece actually matters
&lt;/h2&gt;

&lt;p&gt;Let me take each piece seriously before I get to the angle underneath.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The in-app browser is the genuinely impressive half.&lt;/strong&gt; Most coding agents have two contexts: the codebase in front of them and whatever was in your prompt at session start. The instant they need to verify a one-liner from a deprecation notice on a vendor's site, the loop breaks — you copy the URL, paste it, hand it back. An in-app browser collapses three round-trips into one. For anything that depends on fresh documentation (which is essentially every framework released in the last six months), this is the actual lever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sonnet 5 sits underneath the same agent loop.&lt;/strong&gt; No benchmark numbers were published in the mid-year summary, and I won't fabricate what the article didn't print. The honest framing is "newer weights, same surface area, expect the usual incremental lift in long-context reasoning and code generation." If you were hitting the ceiling on Sonnet 4, try 5 and see what changes; if you weren't, you're fine where you are. The point of a model bump is usually that the existing prompts keep working and the edge cases stop blowing up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The workflow-streamlining pass is the boring-sounding half that's actually underrated.&lt;/strong&gt; Setup diagnostics means the agent can self-check its own environment — paths, env vars, auth state, keychain presence — before it tells you "I can't find X." That class of error has wasted every Claude Code user's evening at least once. The diagnostic converts silent failures into loud ones, which is the whole game.&lt;/p&gt;

&lt;p&gt;The "enhanced safety features" line, without specifics in the summary, presumably runs the direction every vendor's safety work runs: less willingness to do destructive operations without confirmation, better handling of secrets in repos and commands, clearer refusal reasoning. Both reduce the "did it just do something I can't undo" surface. I'd rather Anthropic publish a concrete changelog of what changed than leave us guessing on vibes.&lt;/p&gt;

&lt;p&gt;[[COMPARE: Claude Code without in-app browser vs Claude Code with in-app browser]]&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually use this today
&lt;/h2&gt;

&lt;p&gt;The mid-year summary is light on exact commands, so I'll stay general where the article is general and specific where I can be.&lt;/p&gt;

&lt;p&gt;The basic loop, for anything that requires checking docs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# inside a project directory&lt;/span&gt;
claude

&lt;span class="c"&gt;# a research-shaped prompt — the kind that used to require alt-tabbing&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; what changed &lt;span class="k"&gt;in &lt;/span&gt;the React 19 useActionState API, and where does it differ from the old useFormState?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the in-app browser, the agent opens the React docs (or the migration guide, the GitHub PR, the Stack Overflow answer) and reads them itself. Without it, you'd be the one feeding it. The win compounds on every task that has a "look it up" step in the middle.&lt;/p&gt;

&lt;p&gt;Three categories that benefit disproportionately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Migrations.&lt;/strong&gt; Anything where the answer is "the API was renamed and the parameters moved." Claude Code + browser handles this in one turn.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Library spelunking.&lt;/strong&gt; New CLI flags, new config keys, new SDK methods. The browser pulls the changelog; the model summarizes the deltas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Debugging obscure framework errors.&lt;/strong&gt; Paste the error, let the agent search GitHub issues, ship the workaround.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# pick the model for tasks where reasoning matters more than latency&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /model sonnet-5

&lt;span class="c"&gt;# run setup diagnostics on first install, and after any major upgrade&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /setup-check
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'd flag that the exact slash command for diagnostics wasn't in the source — check &lt;code&gt;claude --help&lt;/code&gt; or &lt;code&gt;/help&lt;/code&gt; inside the session for the canonical spelling on your version. The capability is real; the exact string I just wrote might be off by a word, so verify before you script against it.&lt;/p&gt;

&lt;p&gt;For Sonnet 5 specifically: if your plan exposes model selection, use it for tasks where reasoning depth matters more than latency — multi-file refactors, ambiguous requirements, anything where you'd otherwise paste three Stack Overflow tabs into the prompt yourself. Drop back to the smaller / faster model for tight loops (rename this function across 80 files, run the formatter, commit) where the agent is more of a glorified macro.&lt;/p&gt;

&lt;p&gt;Run setup diagnostics on first install. Treat it like a linter for your environment. Ten seconds of cost, catches the "wrong Node version" or "missing API key" class of mistake that costs forty minutes of your evening. Diagnostics isn't magic; it's a check that fails loudly instead of silently. Loud failures are the whole job.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's missing from the announcement — and what I'd want before forming a strong opinion
&lt;/h2&gt;

&lt;p&gt;The summary is a feature index, not a benchmark paper. So a few honest gaps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No published speed or accuracy numbers for Sonnet 5 vs its predecessor&lt;/li&gt;
&lt;li&gt;No pricing change called out&lt;/li&gt;
&lt;li&gt;No public statement on what the "enhanced safety features" actually check for — secret scanning in-repo? Destructive-command gating? Policy enforcement on outbound HTTP? Both?&lt;/li&gt;
&lt;li&gt;No concrete timeline or roadmap beyond the "continuous evolution" framing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you're going to migrate a workflow onto this, those are the four numbers to ask for before you commit. Vendor marketing beats no data, but neither beats an independent benchmark. Ship against the announcement, not into it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that survives the model
&lt;/h2&gt;

&lt;p&gt;Here's the thing I keep coming back to with every Claude Code / Cursor / Copilot cycle: the agent loop is the exciting layer, and it changes every quarter. Sonnet 5 today, Sonnet 6 six months from now, something else after that. The thing underneath it — the components, the styling primitives, the auth model, the way your UI is actually structured — that's the part with a half-life measured in years.&lt;/p&gt;

&lt;p&gt;If you're shipping a product, the agent is your accelerator. The durable surface is the cross-platform component layer it touches: the same &lt;code&gt;Button&lt;/code&gt;, &lt;code&gt;Sheet&lt;/code&gt;, and &lt;code&gt;Card&lt;/code&gt; rendered identically on web, iOS, and Android from a single API surface. When the model swaps, the components don't care. When the agent loop changes shape next quarter — and it will — your UI surface is still the same surface, behaving the same on every device your user opens it on.&lt;/p&gt;

&lt;p&gt;That's the layer to invest in. Claude Code — and Sonnet 6 when it lands, and whatever replaces it after — is what builds into it. The agent stack churns. Your components shouldn't.&lt;/p&gt;

&lt;p&gt;Ship the part that survives the model. The model is fine.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>claude</category>
      <category>news</category>
    </item>
    <item>
      <title>Why Owning Your AI Agent's Context Is Crucial for Production</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 08:05:25 +0000</pubDate>
      <link>https://dev.to/davekurian/why-owning-your-ai-agents-context-is-crucial-for-production-3338</link>
      <guid>https://dev.to/davekurian/why-owning-your-ai-agents-context-is-crucial-for-production-3338</guid>
      <description>&lt;h2&gt;
  
  
  Cold open
&lt;/h2&gt;

&lt;p&gt;The single biggest enable of 2026 is that AI coding agents actually ship code now. Claude Code, Cursor, the new agentic loops — they don't just autocomplete, they read your repo, plan, and write multi-file diffs across dozens of files. That used to be a recorded demo. Now it's Tuesday. A junior dev with a good agent is producing what a senior dev with a good editor produced in 2023, in roughly half the keystrokes.&lt;/p&gt;

&lt;p&gt;Then they hit Cmd+Z on the session and the context dies.&lt;/p&gt;

&lt;h2&gt;
  
  
  The "demo to prod" gap is the actual blocker
&lt;/h2&gt;

&lt;p&gt;Every agent session builds up two layers of state. The first is the work product — the files it changed, the diff it shipped. That persists. The second is the &lt;em&gt;understanding&lt;/em&gt; — which file holds the auth flow, why the billing webhook signs payloads the way it does, why &lt;code&gt;Card&lt;/code&gt; is wrapped in &lt;code&gt;forwardRef&lt;/code&gt; but &lt;code&gt;Button&lt;/code&gt; is not. That second layer is what makes the agent effective inside the session. It is also what evaporates the second the session closes.&lt;/p&gt;

&lt;p&gt;So the next session — or the next dev, or the CI bot, or your teammate who clones the repo — starts cold. The agent re-derives every convention from scratch. It invents a new folder layout. It picks a different button variant. It writes a Stripe webhook handler that almost matches the one already in the codebase but doesn't, and you spend 40 minutes diffing them.&lt;/p&gt;

&lt;p&gt;I've watched a 30-file change ship in 8 minutes and then watched a teammate's agent spend 25 minutes reverse-engineering what the first agent did so it could extend it without breaking it. That reverse-engineering time is pure tax. It scales with every handoff: dev-to-dev, agent-to-agent, sandbox-to-CI, sandbox-to-prod.&lt;/p&gt;

&lt;p&gt;The thesis is one sentence: &lt;strong&gt;the agent is a consumer of your context, not the source of truth.&lt;/strong&gt; Anything you want the agent to do reliably across sessions has to live in the repo, not in the conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "context" actually means in a repo
&lt;/h2&gt;

&lt;p&gt;When the agent opens your codebase it needs four things, in order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Stack and entry points.&lt;/strong&gt; Language, framework, where the app boots, what runs in tests.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convention.&lt;/strong&gt; How files are named, where routes live, where DB schemas live, the import order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Component contracts.&lt;/strong&gt; Which UI primitives are blessed, what their props are, what variants exist, what to never use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tried-and-tested recipes.&lt;/strong&gt; For recurring tasks — adding a Stripe webhook, writing a migration, scaffolding a screen — what does the team-agreed version look like.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Most agents get (1) from &lt;code&gt;package.json&lt;/code&gt; and a glance at the entry file. (2) they infer from neighboring files, badly. (3) they guess, or they pick whatever library they were trained on most loudly. (4) they invent from scratch every single time.&lt;/p&gt;

&lt;p&gt;That last one is where the hour disappears. The agent doesn't know your team agreed webhook handlers go in &lt;code&gt;app/api/webhooks/&amp;lt;provider&amp;gt;/route.ts&lt;/code&gt;, with signature verification as the first line, with idempotency keys keyed on the provider's event id. So it writes the webhook somewhere else, in a different shape, and now the codebase has two of them.&lt;/p&gt;

&lt;p&gt;[[COMPARE: ephemeral agent session vs persistent repo context]]&lt;/p&gt;

&lt;h2&gt;
  
  
  Sandboxes are great until the session ends
&lt;/h2&gt;

&lt;p&gt;The sandboxed agents — the chat interfaces, the IDE sidebars, the web playgrounds — are brilliant for one-shot generation. They prove the model works. They demo well. They are not where production code lives.&lt;/p&gt;

&lt;p&gt;Production code lives in a repo, on a branch, behind a CI run, deployed by a script. None of those four things inherit the agent's conversation. The agent's "understanding" of your codebase is a residue that lives in token activations and gets thrown out with the session.&lt;/p&gt;

&lt;p&gt;That is fine for a 10-line proof of concept. It is fatal for a 200-file app that three people ship every day. The fix is not a better agent — those are improving on their own. The fix is moving the understanding out of the conversation and into the repo.&lt;/p&gt;

&lt;h2&gt;
  
  
  OTF's structured context — what ships in the box
&lt;/h2&gt;

&lt;p&gt;Every paid OTF kit ships with three layers of structured context that survive the session, the handoff, and the model swap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/strong&gt; at the repo root — the brief the agent reads first. Stack, conventions, component contracts, the things it must never do.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;.cursorrules&lt;/code&gt;&lt;/strong&gt; — Cursor-specific rules the agent picks up automatically on &lt;code&gt;cursor .&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;ai/prompts/&lt;/code&gt;&lt;/strong&gt; — 20+ tested prompt templates for the recurring tasks: add a Stripe webhook, scaffold a screen, write a migration, add a tRPC route, generate a typed client. Each one was run against Claude Code and Cursor and refined until the output matched the kit's conventions without a second pass.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the part that doesn't change when the model changes. Claude Code 4, GPT-5, whatever ships next quarter — they all read files. They all respect &lt;code&gt;.cursorrules&lt;/code&gt;. They all consume &lt;code&gt;CLAUDE.md&lt;/code&gt;. The prompts in &lt;code&gt;ai/prompts/&lt;/code&gt; are plain markdown; the model doesn't care which one runs them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a &lt;code&gt;CLAUDE.md&lt;/code&gt; actually looks like
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Kit: SaaS Dashboard&lt;/span&gt;
Stack: TypeScript, Next.js App Router, tRPC, Postgres, Better Auth, Stripe.

&lt;span class="gu"&gt;## Conventions&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; All routes live under &lt;span class="sb"&gt;`src/app/&amp;lt;route&amp;gt;/page.tsx`&lt;/span&gt;. Never in &lt;span class="sb"&gt;`pages/`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; DB schema is the source of truth — &lt;span class="sb"&gt;`drizzle/schema.ts`&lt;/span&gt;. Never write raw SQL in a route.
&lt;span class="p"&gt;-&lt;/span&gt; Server actions live next to the route in &lt;span class="sb"&gt;`actions.ts`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Components: import from &lt;span class="sb"&gt;`@otfdashkit/ui`&lt;/span&gt;, never from a UI library directly.
&lt;span class="p"&gt;-&lt;/span&gt; Button: always &lt;span class="sb"&gt;`&amp;lt;Button variant="primary|secondary|ghost"&amp;gt;`&lt;/span&gt;. Never &lt;span class="sb"&gt;`&amp;lt;button&amp;gt;`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Card: wrap in &lt;span class="sb"&gt;`forwardRef`&lt;/span&gt;. Never render raw &lt;span class="sb"&gt;`&amp;lt;div&amp;gt;`&lt;/span&gt;.

&lt;span class="gu"&gt;## Do not&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Don't add a state library. Zustand is the only one. It's in &lt;span class="sb"&gt;`lib/store.ts`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Don't write a new auth flow. Better Auth owns it. See &lt;span class="sb"&gt;`lib/auth.ts`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Don't scaffold a webhook by hand. Use &lt;span class="sb"&gt;`ai/prompts/stripe-webhook.md`&lt;/span&gt;.

&lt;span class="gu"&gt;## Tested recipes&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Add a Stripe webhook: &lt;span class="sb"&gt;`ai/prompts/stripe-webhook.md`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Scaffold a dashboard screen: &lt;span class="sb"&gt;`ai/prompts/dashboard-screen.md`&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Write a typed tRPC procedure: &lt;span class="sb"&gt;`ai/prompts/trpc-procedure.md`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That file is ~50 lines. It is the difference between an agent that extends your codebase and an agent that re-derives it. A teammate's agent, the CI bot, the dev who joins next month — they all start from the same brief. No reverse-engineering. No 25-minute tax.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tested prompts are the part that compounds
&lt;/h2&gt;

&lt;p&gt;A &lt;code&gt;CLAUDE.md&lt;/code&gt; describes the world. A prompt in &lt;code&gt;ai/prompts/&lt;/code&gt; describes a task. The two together are what makes the agent predictable. The way OTF tests them is mechanical:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Run a prompt against a fresh checkout and diff the output&lt;/span&gt;
otf test-prompt ai/prompts/stripe-webhook.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--against&lt;/span&gt; kits/saas-dashboard &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; claude-code-4 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--expect&lt;/span&gt; fixtures/stripe-webhook.expected.diff

&lt;span class="c"&gt;# Same prompt, different model — should produce the same shape&lt;/span&gt;
otf test-prompt ai/prompts/stripe-webhook.md &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--against&lt;/span&gt; kits/saas-dashboard &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--model&lt;/span&gt; gpt-5 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--expect&lt;/span&gt; fixtures/stripe-webhook.expected.diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the diff matches the expected fixture, the prompt ships. If it doesn't, the prompt gets refined until it does. By the time a kit ships to a customer, every recipe in &lt;code&gt;ai/prompts/&lt;/code&gt; has been run against at least two models and produces a consistent output. That's the "tested" in "tested prompts." It is the reason a teammate's agent, on day one, with no context, ships a webhook that fits the codebase — not one that almost fits.&lt;/p&gt;

&lt;h2&gt;
  
  
  The handoff that doesn't lose information
&lt;/h2&gt;

&lt;p&gt;When the first agent shipped the billing refactor, it left a &lt;code&gt;CLAUDE.md&lt;/code&gt; that said "billing webhook signs payloads with &lt;code&gt;STRIPE_WEBHOOK_SECRET&lt;/code&gt;; idempotency is keyed on &lt;code&gt;event.id&lt;/code&gt;; the route is &lt;code&gt;app/api/webhooks/stripe/route.ts&lt;/code&gt;." When the second agent picked up the next task, it read that file, looked at the route, and continued.&lt;/p&gt;

&lt;p&gt;That is not magic. It is the same agent, in a new session, reading the same repo. But because the repo carried the understanding, the second session was as productive as the first. The handoff cost dropped from 25 minutes to 30 seconds — the time to read the file.&lt;/p&gt;

&lt;p&gt;[[CONCEPT: the agent is a consumer, the repo is the source of truth]]&lt;/p&gt;

&lt;h2&gt;
  
  
  What this gets us
&lt;/h2&gt;

&lt;p&gt;The pitch for any new agent this year is the same: "we read your repo, we plan, we ship multi-file diffs." That is table stakes now. The actual production question is: when the model rolls over in three months — and it will — does your team's understanding survive?&lt;/p&gt;

&lt;p&gt;If the understanding lived in the session, no. You rewrite every brief, re-test every prompt, re-explain every convention.&lt;/p&gt;

&lt;p&gt;If the understanding lived in the repo, yes. You swap the model, you keep &lt;code&gt;CLAUDE.md&lt;/code&gt;, you keep &lt;code&gt;.cursorrules&lt;/code&gt;, you keep the tested prompts. The agent changes. The code keeps moving. The new hire on day three reads the same &lt;code&gt;CLAUDE.md&lt;/code&gt; the senior dev wrote on day one. The CI bot reads the same &lt;code&gt;.cursorrules&lt;/code&gt; the IDE reads. The mobile build script reads the same component contracts the web app reads.&lt;/p&gt;

&lt;p&gt;That is the durable layer. The agent is the tool. The repo is the product.&lt;/p&gt;

&lt;p&gt;[[IMG: clay character at a laptop shipping a multi-file diff, the same component rendered web + iOS + Android on a second monitor, the diff is clean and the agent is mid-keystroke]]&lt;/p&gt;

&lt;h2&gt;
  
  
  Use the agent. Make the repo hold the context.
&lt;/h2&gt;

&lt;p&gt;Claude Code and Cursor are genuinely good right now. Use them. Run them hard. Ship the multi-file diff in 8 minutes, not 8 hours. That part of 2026 is real.&lt;/p&gt;

&lt;p&gt;Then make sure the context that made the agent effective is not sitting in the conversation history. Move it into &lt;code&gt;CLAUDE.md&lt;/code&gt;. Move the conventions into &lt;code&gt;.cursorrules&lt;/code&gt;. Move the recurring recipes into &lt;code&gt;ai/prompts/&lt;/code&gt;. Test each one — run it, diff the output, refine it until it ships first-pass.&lt;/p&gt;

&lt;p&gt;That is the part that survives the model swap, the team change, the 2am incident, the new hire on day three. That is what makes the agent a teammate instead of a one-shot generator.&lt;/p&gt;

&lt;p&gt;The agent is a consumer of your context. Make the context good, and ship.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>ai</category>
      <category>productivity</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>Why File-System AI Agents Are the Key to Bridging the Gap to Production</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 05:05:00 +0000</pubDate>
      <link>https://dev.to/davekurian/why-file-system-ai-agents-are-the-key-to-bridging-the-gap-to-production-3e1a</link>
      <guid>https://dev.to/davekurian/why-file-system-ai-agents-are-the-key-to-bridging-the-gap-to-production-3e1a</guid>
      <description>&lt;h2&gt;
  
  
  File-system agents are the actual enable — and the gap to prod is closing
&lt;/h2&gt;

&lt;p&gt;Cursor and Claude Code are the first coding agents that feel like they live in your repo, not in a sandbox you can't reach into. That is a genuine leap, and it has changed what a solo dev can ship in a weekend.&lt;/p&gt;

&lt;p&gt;For the last eighteen months, the pattern that actually worked in practice was: open a file, write a prompt, watch the agent edit the file in place, run the test, iterate. No upload-to-sandbox, no copy-paste-out, no "your code lives on our servers." The agent has the same tools you have. It can &lt;code&gt;Read&lt;/code&gt;, &lt;code&gt;Edit&lt;/code&gt;, &lt;code&gt;Grep&lt;/code&gt;, &lt;code&gt;Bash&lt;/code&gt;. It sees your &lt;code&gt;tsconfig.json&lt;/code&gt;, your monorepo layout, your &lt;code&gt;package.json&lt;/code&gt; scripts. It uses them.&lt;/p&gt;

&lt;p&gt;That is the part nobody is talking about loudly enough. The conversation keeps drifting toward "AI can't ship to production" — which is true if you keep the AI on the wrong side of a sandbox. The interesting question is what happens when the agent works &lt;em&gt;inside&lt;/em&gt; the repo you will deploy.&lt;/p&gt;

&lt;p&gt;[[COMPARE: sandbox export at deploy time vs an owned repo the agent has been editing all along]]&lt;/p&gt;

&lt;h2&gt;
  
  
  The sandbox tax is real, and it shows up at deploy time
&lt;/h2&gt;

&lt;p&gt;Lovable, Bolt, v0, Rork — pick a sandboxed builder and run a small experiment. Build a working auth flow. Then try to ship it.&lt;/p&gt;

&lt;p&gt;What you'll find:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The "code" is a snapshot in their cloud. You can download it, but the conventions are whatever the model invented that afternoon.&lt;/li&gt;
&lt;li&gt;The DB schema lives on their Postgres. The auth lives on their identity provider. The components live in their component tree.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;.env&lt;/code&gt; keys are scattered across three dashboards. The deploy button is theirs, not yours.&lt;/li&gt;
&lt;li&gt;An hour of "shipping to production" becomes three days of disentangling from the sandbox.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not a Lovable problem or a Bolt problem. It is a &lt;em&gt;sandbox problem&lt;/em&gt;. The moment a model owns the filesystem, you cannot own the deploy. Cursor and Claude Code flipped this. They live inside &lt;em&gt;your&lt;/em&gt; filesystem. The code is yours from the first keystroke. The conventions are yours. The deploy is yours. There is no sandbox tax because there is no sandbox.&lt;/p&gt;

&lt;h2&gt;
  
  
  The README is dead. The convention file is the real product
&lt;/h2&gt;

&lt;p&gt;The single biggest predictor of an AI agent producing useful code is whether your repo tells it the conventions. Not "use TypeScript" — everyone knows that. The actual, specific, drudgery-level conventions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Forms use &lt;code&gt;zod&lt;/code&gt; schemas colocated with the route handler.&lt;/li&gt;
&lt;li&gt;Components go in &lt;code&gt;apps/web/components/&lt;/code&gt;. The &lt;code&gt;cn()&lt;/code&gt; helper lives in &lt;code&gt;lib/cn.ts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Auth is session-cookie based, server-validated on every API route. Don't write client-side auth checks.&lt;/li&gt;
&lt;li&gt;Every list view paginates with cursor-based pagination, not offset.&lt;/li&gt;
&lt;li&gt;Never name a component &lt;code&gt;index.tsx&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those rules live in a comment thread in someone's head, the agent will guess. It will guess wrong. You will burn two hours debugging a &lt;code&gt;&amp;lt;Button&amp;gt;&lt;/code&gt; it generated with the wrong variant prop.&lt;/p&gt;

&lt;p&gt;The convention file — &lt;code&gt;CLAUDE.md&lt;/code&gt; for Claude Code, &lt;code&gt;.cursorrules&lt;/code&gt; for Cursor, &lt;code&gt;AGENTS.md&lt;/code&gt; for everything else — is the new README. It is the contract between you and the agent.&lt;/p&gt;

&lt;p&gt;[[CONCEPT: a single tested convention file at the repo root that every agent reads before it edits]]&lt;/p&gt;

&lt;h2&gt;
  
  
  A convention file that actually holds up
&lt;/h2&gt;

&lt;p&gt;Here is what a real one looks like. Not a "be a good engineer" prompt — a repo-shaped document:&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="gh"&gt;# Engineering conventions — read first&lt;/span&gt;

&lt;span class="gu"&gt;## Stack&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; TypeScript strict, no &lt;span class="sb"&gt;`any`&lt;/span&gt; in new code, no &lt;span class="sb"&gt;`// @ts-ignore`&lt;/span&gt; without a comment.
&lt;span class="p"&gt;-&lt;/span&gt; Server: Hono routes under &lt;span class="sb"&gt;`apps/server/src/routes/`&lt;/span&gt;. API returns
  &lt;span class="sb"&gt;`{ data, error: { code, message } | null }`&lt;/span&gt; — every route, no exceptions.
&lt;span class="p"&gt;-&lt;/span&gt; Web: components in &lt;span class="sb"&gt;`apps/web/components/`&lt;/span&gt;. Tokens via &lt;span class="sb"&gt;`color.bg.canvas`&lt;/span&gt;,
  never raw hex.

&lt;span class="gu"&gt;## Auth&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Session-cookie, server-validated on every route via &lt;span class="sb"&gt;`requireSession()`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Never check auth in a client component.

&lt;span class="gu"&gt;## Pagination&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; Cursor-based, response shape: &lt;span class="sb"&gt;`{ items, nextCursor }`&lt;/span&gt;. Never &lt;span class="sb"&gt;`offset`&lt;/span&gt;.
&lt;span class="p"&gt;-&lt;/span&gt; Cursor is base64-encoded &lt;span class="sb"&gt;`{ createdAt, id }`&lt;/span&gt;.

&lt;span class="gu"&gt;## Naming&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; No &lt;span class="sb"&gt;`index.tsx`&lt;/span&gt;. No &lt;span class="sb"&gt;`utils.ts`&lt;/span&gt; without a domain prefix. No new top-level
  folders without an entry in this file.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That file goes at the repo root. The agent reads it on every session. When the rule changes, the file changes, and every future session picks it up. Reviewed in PRs like any other code.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to actually try this today
&lt;/h2&gt;

&lt;p&gt;You do not need a kit. The pattern is portable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# create the three files the major agents read&lt;/span&gt;
&lt;span class="nb"&gt;touch &lt;/span&gt;CLAUDE.md .cursorrules AGENTS.md
git add CLAUDE.md .cursorrules AGENTS.md
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"add convention files for coding agents"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run the agent against the repo. Ask it to add a Stripe webhook. Watch what comes back:&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;// app/api/webhooks/stripe/route.ts — written by the agent&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;stripe&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="s2"&gt;@/lib/stripe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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;db&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="s2"&gt;@/lib/db&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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;verifySession&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="s2"&gt;@/lib/auth/session&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&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;errorEnvelope&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="s2"&gt;@/lib/api/error&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&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;POST&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Request&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="nx"&gt;sig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stripe-signature&lt;/span&gt;&lt;span class="dl"&gt;"&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;sig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;errorEnvelope&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MISSING_SIG&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;400&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;stripe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;webhooks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;constructEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="nx"&gt;sig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;STRIPE_WEBHOOK_SECRET&lt;/span&gt;&lt;span class="o"&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;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;invoice.payment_failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;subscription&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;where&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;stripeCustomerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;customer&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;past_due&lt;/span&gt;&lt;span class="dl"&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;break&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="nx"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&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="na"&gt;received&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;p&gt;That is convention-shaped code. It knew the error envelope, the &lt;code&gt;Response.json&lt;/code&gt; shape, the &lt;code&gt;db&lt;/code&gt; import path, the rule about session validation on every route. It did not invent a new pattern. It matched what was already there. That is the whole game.&lt;/p&gt;

&lt;h2&gt;
  
  
  What OTF ships on top of the pattern
&lt;/h2&gt;

&lt;p&gt;If you do not want to write that convention file by hand, every kit we ship includes a tested one. Not generic — repo-shaped, covering the same six sections above, tuned for the kit you bought. The prompt library — twenty-plus files in &lt;code&gt;ai/prompts/&lt;/code&gt; — handles the common extension jobs: add a Stripe webhook, scaffold a new API route, generate a settings page, wire a new OAuth provider. Each one assumes the convention file is true.&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;// ai/prompts/stripe-webhook.md — first three lines, the rest is the prompt&lt;/span&gt;
&lt;span class="c1"&gt;// "Read app/api/webhooks/ for existing patterns. Use the error envelope&lt;/span&gt;
&lt;span class="c1"&gt;//  from lib/api/error.ts. Match the auth check from requireSession()."&lt;/span&gt;
&lt;span class="c1"&gt;// followed by the prompt body the agent consumes.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run an agent against a kit and it reads the convention file first, then the relevant prompt, then edits. The output is consistent with what is already in the repo, not whatever the model invented at 3am. That ordering — convention file → task prompt → edit — is the difference between an agent that extends your codebase and one that rewrites it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The deploy story: shipping without rewriting
&lt;/h2&gt;

&lt;p&gt;Owning the repo is half the equation. Owning the deploy is the other half. The convention file gets you code you trust. A one-line deploy script gets you code you actually serve.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# from a kit: wire a custom domain + DNS + TLS + ship&lt;/span&gt;
pnpm ship &lt;span class="nt"&gt;--domain&lt;/span&gt; app.example.com

&lt;span class="c"&gt;# or just the build-and-push, no domain&lt;/span&gt;
pnpm ship &lt;span class="nt"&gt;--push-only&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That single command runs the design checklist (a 24-item script that blocks the build if a component is missing a label, an icon button is missing an accessible name, or a color token is hardcoded), runs &lt;code&gt;tsc --noEmit&lt;/code&gt;, runs the tests, then wires the deploy. For mobile it kicks off the iOS/Android build with the right bundle ids and the keystore you already configured.&lt;/p&gt;

&lt;p&gt;The agent wrote the code against the convention file. The deploy script ships it. Nothing in between is a sandbox you have to escape from.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this gets us in practice
&lt;/h2&gt;

&lt;p&gt;Three concrete scenarios where the loop closes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solo dev shipping a SaaS this weekend. Convention file in the repo from day one. Agent writes features against your conventions. &lt;code&gt;pnpm ship&lt;/code&gt; puts it on a domain. Friday night, the product is live.&lt;/li&gt;
&lt;li&gt;Team handing a junior engineer a feature. The convention file is the onboarding doc. The agent is the pair. The junior ships a settings page that matches every other settings page on day one, because the file told the agent how to write it.&lt;/li&gt;
&lt;li&gt;Migrating from a sandbox builder. Download the code, drop it in a kit, write a convention file that captures the rules you wish it had followed, run the agent to bring the existing components up to the kit's contract. One PR at a time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The pattern is not "AI replaces the engineer." The pattern is "the agent edits the same repo the engineer edits, against the same rules, shipped by the same script." Every layer — the filesystem, the convention file, the prompt library, the deploy — is something you own. Nothing lives in someone else's cloud waiting to be migrated away from.&lt;/p&gt;

&lt;p&gt;The model will change again next quarter. The sandboxed builders will get better, or worse, or get acquired. Cursor will fork, Claude Code will fork, something new will appear. The parts that do not change are: the repo is yours, the conventions are tested, the deploy is one script, and the component is the same on web, iOS, and Android because you wrote it once. That is the durable layer. Build the rest on top of it.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Unify Your UI/UX Across Platforms with OTF's Component System</title>
      <dc:creator>Dave Kurian</dc:creator>
      <pubDate>Sat, 11 Jul 2026 04:05:20 +0000</pubDate>
      <link>https://dev.to/davekurian/unify-your-uiux-across-platforms-with-otfs-component-system-198m</link>
      <guid>https://dev.to/davekurian/unify-your-uiux-across-platforms-with-otfs-component-system-198m</guid>
      <description>&lt;p&gt;The button shipped on iOS at 9:47pm. By 9:51pm, three engineers had filed issues. Web had it grey. Android had it floating with the wrong radius. iOS — the one that looked right at first — had a press state that stuck after release.&lt;/p&gt;

&lt;p&gt;Three teams, three button components, three regressions.&lt;/p&gt;

&lt;p&gt;This is what platform drift actually costs. Not the abstract "inconsistent UX" line from a design system pitch deck — actual man-hours: the same button redrawn three times, themed three times, accessibility-checked three times, and still wrong in three different ways on the same Tuesday night.&lt;/p&gt;

&lt;p&gt;The naive fix is "one codebase to rule them all" — write once, run anywhere. Most attempts at that die on contact with platform reality. iOS wants one gesture model. Web wants another. Native wants a third. Each platform has its own accessibility tree, its own focus model, its own idiomatic motion. A component that pretends those don't exist looks fine in a demo and breaks the first time a screen reader user encounters it.&lt;/p&gt;

&lt;p&gt;So this isn't a "unified codebase" pitch. It's a tighter claim: &lt;strong&gt;the same component name, the same props, the same visual output — on every platform — without papering over the platform reality that makes each one usable.&lt;/strong&gt; When the abstraction respects the platform, you stop redrawing. When it doesn't, you get the Tuesday night.&lt;/p&gt;

&lt;h2&gt;
  
  
  The drift problem, named precisely
&lt;/h2&gt;

&lt;p&gt;"Cross-platform" usually splits into two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One runtime, two skins.&lt;/strong&gt; A web app shipped as a wrapped WebView. Same DOM, same CSS, same bugs in two app stores.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Two runtimes, two skins.&lt;/strong&gt; A native iOS app and a native Android app, plus a React web frontend. This is the common case, and the cost here is the original sin: every component is rebuilt three times.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first case is cheap on day one and punishing on day ninety — WebView keyboards, WebView accessibility, WebView pull-to-refresh that never feels right. The second is the one most teams actually live in, and the one that quietly bleeds the most hours.&lt;/p&gt;

&lt;p&gt;Three primitives are usually duplicated:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Component&lt;/th&gt;
&lt;th&gt;What's rebuilt&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Atoms (Button, Input, Card)&lt;/td&gt;
&lt;td&gt;Visual + interaction + a11y&lt;/td&gt;
&lt;td&gt;Multiplied across every screen&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Forms (validation, errors)&lt;/td&gt;
&lt;td&gt;Submission flows per platform&lt;/td&gt;
&lt;td&gt;Tests aren't portable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Navigation (stack, tab)&lt;/td&gt;
&lt;td&gt;Idioms differ per platform&lt;/td&gt;
&lt;td&gt;The hardest one to fake&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The days lost aren't glamorous. They're the Tuesday-night pixel-fix days, repeated across every release.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "the same component" actually has to mean
&lt;/h2&gt;

&lt;p&gt;The cheap version of "unified" is "it renders something." A button-shaped rectangle that submits the form. That's the floor, not the goal.&lt;/p&gt;

&lt;p&gt;The version that survives production has three properties, and all three have to hold at the same import:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Same name, same props.&lt;/strong&gt; A &lt;code&gt;Button&lt;/code&gt; accepts &lt;code&gt;variant&lt;/code&gt;, &lt;code&gt;size&lt;/code&gt;, &lt;code&gt;onPress&lt;/code&gt; (or &lt;code&gt;onClick&lt;/code&gt; on web), &lt;code&gt;disabled&lt;/code&gt;. Same names, same types, same semantic values across runtimes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same pixel output, given the same tokens.&lt;/strong&gt; Tap the design tool, copy the hex. The button on web, iOS, and Android renders that exact colour, at that exact radius, at that exact type scale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Same a11y contract.&lt;/strong&gt; A button is a button to VoiceOver, NVDA, and TalkBack. Role, label, focus ring, and disabled semantics — identical, not "close enough".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most cross-platform libraries clear one of those. Almost none clear all three at the same component, same import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "write once, run anywhere" usually means broken everywhere
&lt;/h2&gt;

&lt;p&gt;The pattern that fails is the one that hides the platform. A &lt;code&gt;View&lt;/code&gt; that "just works" on web and mobile has to make choices about touch vs mouse, flexbox vs Yoga, focus-visible vs focus-on-press. The moment the abstraction makes those choices for you, your designer files a ticket: "the web button has the wrong hover state." Your QA engineer files another: "Android focus order is reversed." Your accessibility lead files a third.&lt;/p&gt;

&lt;p&gt;The right rule is &lt;strong&gt;respect the platform at the boundary, then collapse everything above it.&lt;/strong&gt; The boundary is where the buttons actually live — a real DOM &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; on web, a real native view on mobile. Above the boundary, your team writes once.&lt;/p&gt;

&lt;p&gt;[[DIAGRAM: a screen built from platform-respecting primitives — the same component name and props render on a real DOM  in the web runtime, a real native UIButton on iOS, and a real clickable focusable view on Android; only the bottom layer adapts to the platform, everything above it is shared]]&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// web — same import path a screen already uses&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;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&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;@otfdashkit/ui&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;PricingRow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Plan&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="nc"&gt;Card&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;Stack&lt;/span&gt; &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md"&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;h3&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;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;h3&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;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt; &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Choose &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Stack&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;Card&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;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// native — same component name, same props, same visual output&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;Button&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Stack&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;@otfdashkit/ui-native&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;PricingRow&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;plan&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Plan&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="nc"&gt;Card&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;Stack&lt;/span&gt; &lt;span class="na"&gt;gap&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"md"&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;Text&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;h3&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;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;Text&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;variant&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"primary"&lt;/span&gt; &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"lg"&lt;/span&gt; &lt;span class="na"&gt;onPress&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          Choose &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;plan&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&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;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Stack&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;Card&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;p&gt;No translation layer. No prop-mapping utility. No "but on mobile it has to be..." comment in the PR. The screen file is identical except for the runtime-appropriate import and the platform-native event name. Convention beats configuration.&lt;/p&gt;
&lt;h2&gt;
  
  
  The token layer that holds it together
&lt;/h2&gt;

&lt;p&gt;Pixel parity across platforms dies the moment each runtime defines its own colours, spacing, and type scale in three different files. The fix is a single token source, fan-out per runtime, and a guard script that fails the build when a runtime drifts.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// tokens — runtime-agnostic, the only source of truth&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;primary&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#5B6CFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;600&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#4854E0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#FFFFFF&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#F7F8FA&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;radius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;space&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;xs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;sm&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;md&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;





&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# one script re-emits the token files per runtime&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm tokens:sync
  → apps/web/tokens.css        &lt;span class="o"&gt;(&lt;/span&gt;CSS variables &lt;span class="k"&gt;for &lt;/span&gt;the web runtime&lt;span class="o"&gt;)&lt;/span&gt;
  → apps/mobile/tokens.ts      &lt;span class="o"&gt;(&lt;/span&gt;typed consts &lt;span class="k"&gt;for &lt;/span&gt;the native runtimes&lt;span class="o"&gt;)&lt;/span&gt;
  → apps/landing/tokens.json   &lt;span class="o"&gt;(&lt;/span&gt;design-tool &lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The cold practical detail: when a designer bumps the primary to a new brand colour, you change one file and the three runtimes pick it up in the same commit. When someone adds a new colour in one runtime and forgets the others, the guard script catches it before it ships. Configuration beats three drift-prone duplications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The accessibility contract that doesn't ship as a footnote
&lt;/h2&gt;

&lt;p&gt;The single biggest reason "unified" usually means "broken for screen reader users" is that the abstraction hides the accessibility tree. Web exposes it. iOS exposes it. Android exposes it. An abstraction that pretends to unify all three usually maps to one and ignores the other two.&lt;/p&gt;

&lt;p&gt;The contract that holds in production:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role maps correctly per runtime.&lt;/strong&gt; A &lt;code&gt;Button&lt;/code&gt; is &lt;code&gt;role="button"&lt;/code&gt; on web, a real native button on iOS, a &lt;code&gt;clickable&lt;/code&gt; view with proper accessibility state on Android — but in all three cases, assistive tech reads it as a button.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus order is the source order.&lt;/strong&gt; No &lt;code&gt;tabIndex&lt;/code&gt; games on web, no &lt;code&gt;accessibilityElements&lt;/code&gt; reshuffling on iOS that differ from the visual order.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disabled disables the &lt;em&gt;right&lt;/em&gt; things.&lt;/strong&gt; Inert pointer events on web, &lt;code&gt;accessibilityState={{ disabled: true }}&lt;/code&gt; on native. Not just a faded look.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is enforceable, not aspirational. A 24-item checklist runs before any kit ships:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;pnpm kit:check
  ✓ every Button has an accessible name
  ✓ focus order matches &lt;span class="nb"&gt;source &lt;/span&gt;order on web
  ✓ disabled state propagates to a11y tree on all three runtimes
  ✗ Tab order drift &lt;span class="k"&gt;in &lt;/span&gt;PricingTable.tsx &lt;span class="o"&gt;(&lt;/span&gt;web&lt;span class="o"&gt;)&lt;/span&gt; — fix before shipping
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The last line is the point. The check runs in CI. A screen ships only when the contract holds across all three runtimes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why AI coding agents port screens instead of rewriting them
&lt;/h2&gt;

&lt;p&gt;Most of the Tuesday-night regressions don't start in code — they start in a copy-paste from a snippet an AI coding agent wrote six months ago. The agent didn't know the company had a &lt;code&gt;Button&lt;/code&gt; component. So it wrote a styled &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; with an &lt;code&gt;onClick&lt;/code&gt;. Now there are three of those, in three files, across two repos.&lt;/p&gt;

&lt;p&gt;The fix isn't "tell the AI to use the design system." That's a paragraph of guidance nobody reads. The fix is that the kit ships a recognised set of conventions the agent can extend without guessing:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- CLAUDE.md / .cursorrules — the same hint, two agents --&amp;gt;&lt;/span&gt;

The codebase exposes a cross-platform component kit. Components have
identical names and props across web (&lt;span class="sb"&gt;`@otfdashkit/ui`&lt;/span&gt;) and native
(&lt;span class="sb"&gt;`@otfdashkit/ui-native`&lt;/span&gt;). When porting a screen, change the runtime-
appropriate import and the event handler name (&lt;span class="sb"&gt;`onClick`&lt;/span&gt; → &lt;span class="sb"&gt;`onPress`&lt;/span&gt;).
Do not regenerate an existing component as a styled primitive —
extend the existing one. Design tokens are the single source of truth
in &lt;span class="sb"&gt;`/tokens`&lt;/span&gt;; never hard-code colours or spacing in a screen.
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;This is the part that doesn't burn on a Tuesday night. A junior dev, a contractor, or a coding agent picks up the kit and inherits the conventions. The "use this, not that" decision is encoded once, in a file the agent reads, instead of being repeated in code review forever.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this gets you on a Tuesday night
&lt;/h2&gt;

&lt;p&gt;The version of this story you actually want:&lt;/p&gt;

&lt;p&gt;It's 9:47pm. The button ships on all three runtimes. Web has it. iOS has it. Android has it. Press state releases on touch-up. The web focus ring matches the design. The accessibility tree is intact on every platform because the contract is checked in CI, not in QA.&lt;/p&gt;

&lt;p&gt;Total drift: zero. Tuesday night: free.&lt;/p&gt;

&lt;p&gt;The way to get there isn't a "unified runtime" pitch. It's a component kit where the name, the props, the pixels, and the a11y contract all hold across the three runtimes — and the convention is loud enough that everyone who touches the codebase (human or agent) extends instead of regenerates. That's the durable layer. The platforms underneath keep changing — that's their job. The interface your team writes against doesn't.&lt;/p&gt;



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