<?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: Mike Clarke</title>
    <description>The latest articles on DEV Community by Mike Clarke (@mike_clarke_50a95013f5c59).</description>
    <link>https://dev.to/mike_clarke_50a95013f5c59</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%2F3868865%2Ff368d9be-f55c-4ab9-a26e-a73625709b2b.jpg</url>
      <title>DEV Community: Mike Clarke</title>
      <link>https://dev.to/mike_clarke_50a95013f5c59</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mike_clarke_50a95013f5c59"/>
    <language>en</language>
    <item>
      <title>Your AI generated hundreds of pieces of content. None of it ever shipped.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Fri, 14 Aug 2026 14:00:06 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/your-ai-generated-hundreds-of-pieces-of-content-none-of-it-ever-shipped-4o44</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/your-ai-generated-hundreds-of-pieces-of-content-none-of-it-ever-shipped-4o44</guid>
      <description>&lt;p&gt;Your AI generated hundreds of pieces of content. None of it ever shipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A queue in a &lt;code&gt;ready&lt;/code&gt; state is not done. Done means consumed and delivered.&lt;/li&gt;
&lt;li&gt;Generation and publication are two separate stages. An automated bridge between them is not optional.&lt;/li&gt;
&lt;li&gt;Any queue without a consumer and a depth alert is a silent failure waiting to accumulate.&lt;/li&gt;
&lt;li&gt;"The work looks done" is the most dangerous state in an autonomous system.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;In ARIA — the autonomous content operations system we run at Elevare Digital — we watched two queues quietly fill up over time. Content was being generated. It was landing in the database in a &lt;code&gt;ready&lt;/code&gt; state. Every upstream metric looked fine.&lt;/p&gt;

&lt;p&gt;None of it ever reached the destination. Not a single item.&lt;/p&gt;

&lt;p&gt;The root cause was not a bug. It was a design assumption that never got questioned: publication was a manual step, and nobody ran it.&lt;/p&gt;




&lt;h2&gt;
  
  
  What the system looked like
&lt;/h2&gt;

&lt;p&gt;ARIA's content pipeline has two distinct stages. Stage one: generation. The agents do their work, write the content, and mark the row &lt;code&gt;ready&lt;/code&gt;. Stage two: publication. Something takes those &lt;code&gt;ready&lt;/code&gt; rows and pushes them out.&lt;/p&gt;

&lt;p&gt;Stage one was fully automated. Stage two was manual by design — a decision that made sense early on when we wanted a human in the loop before anything shipped publicly.&lt;/p&gt;

&lt;p&gt;The problem is that "manual by design" eventually became "never runs." There was no consumer process watching the queue. There was no alert watching the queue depth. The count just grew.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- What we were looking at in Supabase:&lt;/span&gt;
&lt;span class="c1"&gt;-- content_queue table, simplified&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;content_queue&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;content_id&lt;/span&gt; &lt;span class="n"&gt;uuid&lt;/span&gt; &lt;span class="k"&gt;REFERENCES&lt;/span&gt; &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="c1"&gt;-- statuses: pending | generating | ready | published | failed&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="n"&gt;timestamptz&lt;/span&gt; &lt;span class="k"&gt;DEFAULT&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- The generation side was automated and ran correctly.&lt;/span&gt;
&lt;span class="c1"&gt;-- Items moved from 'pending' -&amp;gt; 'generating' -&amp;gt; 'ready'.&lt;/span&gt;
&lt;span class="c1"&gt;-- Nothing moved them from 'ready' -&amp;gt; 'published'.&lt;/span&gt;
&lt;span class="c1"&gt;-- The query below returned a number that kept growing:&lt;/span&gt;

&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;content_queue&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ready'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That count was not a success metric. It was a backlog. We were reading it as one and ignoring the other.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is easy to miss
&lt;/h2&gt;

&lt;p&gt;When you look at a pipeline dashboard and see items flowing into &lt;code&gt;ready&lt;/code&gt;, it feels like progress. The agents are working. The generation numbers are up. The logs are clean.&lt;/p&gt;

&lt;p&gt;The gap between &lt;code&gt;ready&lt;/code&gt; and &lt;code&gt;published&lt;/code&gt; is invisible unless you specifically instrument it. We hadn't. There was no metric on queue depth over time, no alert threshold, nothing that would fire if &lt;code&gt;ready&lt;/code&gt; stopped draining.&lt;/p&gt;

&lt;p&gt;This is the specific failure mode:&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;// Pseudocode for what ARIA's generation side was doing — correctly:&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;generateContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;generating&lt;/span&gt;&lt;span class="dl"&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;content&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;runGenerationAgents&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;saveContent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;content&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- work stops here&lt;/span&gt;
  &lt;span class="c1"&gt;// Nothing downstream is listening. This is a dead end.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// What the publication side needed but didn't have:&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;publishConsumer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// This function did not exist as an automated process.&lt;/span&gt;
  &lt;span class="c1"&gt;// It existed as a manual script someone had to remember to run.&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;readyItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getItemsByStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ready&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;for &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;item&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;readyItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;publish&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;updateStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;item&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;published&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The generation function had no idea there was no consumer on the other end. It just kept doing its job.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: every queue gets a consumer and a depth alert
&lt;/h2&gt;

&lt;p&gt;The rule we applied after this: no queue exists without two things attached to it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An automated consumer that drains it.&lt;/li&gt;
&lt;li&gt;A depth alert that fires if items in a terminal-waiting state (like &lt;code&gt;ready&lt;/code&gt;) exceed a threshold for too long.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The consumer can be a Deno edge function on a schedule, a Supabase pg_cron job, a webhook trigger — the mechanism matters less than the guarantee that &lt;em&gt;something&lt;/em&gt; is watching and pulling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- pg_cron job that checks for stranded 'ready' items&lt;/span&gt;
&lt;span class="c1"&gt;-- and alerts if the queue hasn't drained&lt;/span&gt;

&lt;span class="c1"&gt;-- First, a view that makes the staleness visible:&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;VIEW&lt;/span&gt; &lt;span class="n"&gt;stranded_content&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;content_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;updated_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;time_in_state&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;content_queue&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ready'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;updated_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;INTERVAL&lt;/span&gt; &lt;span class="s1"&gt;'2 hours'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Then a function that pages someone if this view has rows:&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;alert_on_stranded_queue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
  &lt;span class="n"&gt;stranded_count&lt;/span&gt; &lt;span class="nb"&gt;integer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;stranded_count&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;stranded_content&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="n"&gt;stranded_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
    &lt;span class="c1"&gt;-- Call your alerting mechanism here.&lt;/span&gt;
    &lt;span class="c1"&gt;-- We use a Supabase edge function that posts to our ops channel.&lt;/span&gt;
    &lt;span class="n"&gt;PERFORM&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.alert_webhook_url'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s1"&gt;'message'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'%s items stranded in ready state'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stranded_count&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="s1"&gt;'severity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'warning'&lt;/span&gt;
      &lt;span class="p"&gt;)::&lt;/span&gt;&lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{"Content-Type": "application/json"}'&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Schedule it:&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'check-stranded-queue'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'*/30 * * * *'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;-- every 30 minutes&lt;/span&gt;
  &lt;span class="s1"&gt;'SELECT alert_on_stranded_queue()'&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The threshold and interval depend on your expected throughput. The point is that a human gets paged before the count becomes embarrassing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The lesson isn't about the bug
&lt;/h2&gt;

&lt;p&gt;This wasn't a bug in the traditional sense. The code did exactly what it was written to do. Generation worked. The &lt;code&gt;ready&lt;/code&gt; status was accurate. The manual publication step was documented.&lt;/p&gt;

&lt;p&gt;The failure was architectural: we treated "generated" as a proxy for "shipped" without building anything to enforce the difference.&lt;/p&gt;

&lt;p&gt;In an autonomous pipeline, every state transition needs an owner. If the transition from &lt;code&gt;ready&lt;/code&gt; to &lt;code&gt;published&lt;/code&gt; requires a human action, then the system needs to &lt;em&gt;demand&lt;/em&gt; that action — not wait quietly while the queue fills.&lt;/p&gt;

&lt;p&gt;A full queue is not progress. It's generated work that went nowhere. The two look identical from the outside until you add the one metric that matters: how long has this item been waiting, and who knows about it?&lt;/p&gt;




&lt;p&gt;&lt;em&gt;— Mike Clarke, founder of Elevare Digital.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Configuring Smart Traffic Systems: A Developer's Deep Dive</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Wed, 12 Aug 2026 06:00:21 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/configuring-smart-traffic-systems-a-developers-deep-dive-4i64</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/configuring-smart-traffic-systems-a-developers-deep-dive-4i64</guid>
      <description>&lt;p&gt;As developers, we often tackle complex problems, and few are as dynamic and critical as managing urban traffic. The constant ebb and flow of vehicles, pedestrians, and public transport presents a fascinating challenge. If you've ever stared at a gridlocked intersection and thought, "There has to be a better way," then you're already thinking like a smart traffic system architect. Today, we're diving into &lt;strong&gt;how to configure a smart traffic system&lt;/strong&gt; – not just the fancy AI, but the foundational logic that makes it tick.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Gridlock Dilemma: Why Smart Systems Matter
&lt;/h3&gt;

&lt;p&gt;Traditional traffic light systems are, for the most part, static. They operate on pre-defined timers, regardless of actual traffic density. This leads to frustrating scenarios: an empty main road gets a long green light while a dozen cars wait impatiently on a side street, or vice-versa. This inefficiency isn't just annoying; it costs time, wastes fuel, increases pollution, and can even delay emergency services.&lt;/p&gt;

&lt;p&gt;The goal of a smart traffic system is to move beyond these fixed schedules. It aims for dynamic, adaptive control, optimizing traffic flow in real-time. This isn't just about making commutes smoother; it's about building more efficient, sustainable, and responsive cities.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Concept: Sensors, Logic, and Actuation
&lt;/h3&gt;

&lt;p&gt;At its heart, a smart traffic system is a feedback loop. It observes, decides, and acts. Here's a breakdown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Sensing (Input)&lt;/strong&gt;: This is where data is collected. Inductive loops embedded in the road, cameras with computer vision capabilities, radar, and even connected vehicle data can tell the system about vehicle presence, speed, queue length, and pedestrian crossings.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Processing/Logic (Decision)&lt;/strong&gt;: This is the brain of the operation. Based on the data from the sensors, the system's algorithms decide the optimal traffic light phasing. Simple systems might use rule-based logic (e.g., "if queue on street A &amp;gt; 5 and queue on street B &amp;lt; 2, extend green for A"). More advanced systems employ machine learning models to predict traffic patterns or reinforce learning agents to dynamically learn optimal strategies.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Actuation (Output)&lt;/strong&gt;: Once a decision is made, the system controls the traffic signals. This involves sending commands to change light states (red, yellow, green) and their durations.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Configuring such a system involves defining these relationships, setting thresholds, and refining the algorithms. It's less about hard-coding every single scenario and more about building a flexible, adaptable framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pseudocode Snapshot: A Simple Adaptive Intersection
&lt;/h3&gt;

&lt;p&gt;Let's consider a basic 4-way intersection. Our goal is to dynamically adjust green light times based on detected traffic volume. We'll use a &lt;code&gt;TrafficLight&lt;/code&gt; object for each approach and a &lt;code&gt;Sensor&lt;/code&gt; object to detect vehicles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Define a simplified TrafficLight object
class TrafficLight:
    constructor(id, initial_state, min_green_time, max_green_time)
    method set_state(new_state)
    method get_current_state()

// Define a simplified Sensor object
class Sensor:
    constructor(location_id)
    method get_vehicle_count() // Returns number of vehicles detected
    method get_queue_length() // Returns estimated queue length

// Main Traffic Management System Logic
function configure_smart_intersection(intersection_id, approaches):
    // approaches: a map from approach_id (e.g., 'north_bound') to a tuple of (TrafficLight, Sensor)

    current_green_approach = 'north_bound' // Start with a default
    timer_for_current_approach = 0

    loop indefinitely:
        // 1. Get current traffic data
        traffic_data = {}
        for approach_id, (light, sensor) in approaches.items():
            traffic_data[approach_id] = {
                'vehicle_count': sensor.get_vehicle_count(),
                'queue_length': sensor.get_queue_length()
            }

        // 2. Apply Decision Logic
        current_light, current_sensor = approaches[current_green_approach]

        // Check if current green time has exceeded minimum or if other approaches demand attention
        if timer_for_current_approach &amp;gt;= current_light.min_green_time:
            // Look for approaches with significant queues that aren't currently green
            candidate_next_approach = null
            max_queue = 0

            for other_approach_id, (other_light, other_sensor) in approaches.items():
                if other_approach_id != current_green_approach:
                    if other_sensor.get_queue_length() &amp;gt; max_queue:
                        max_queue = other_sensor.get_queue_length()
                        candidate_next_approach = other_approach_id

            // If a significant queue is detected elsewhere OR max_green_time is reached
            if (candidate_next_approach != null and max_queue &amp;gt; THRESHOLD_FOR_SWITCH) or 
               timer_for_current_approach &amp;gt;= current_light.max_green_time:
                // Initiate switch sequence (e.g., yellow for current, then red, then green for next)
                // (Simplified for pseudocode)
                current_light.set_state('YELLOW')
                wait(YELLOW_DURATION)
                current_light.set_state('RED')

                current_green_approach = candidate_next_approach // Or pick based on priority
                next_light, _ = approaches[current_green_approach]
                next_light.set_state('GREEN')
                timer_for_current_approach = 0
            else:
                // Extend current green light
                timer_for_current_approach += TIME_STEP
        else:
            // Must complete minimum green time
            timer_for_current_approach += TIME_STEP

        wait(TIME_STEP) // Simulate time passing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pseudocode illustrates a basic reactive system. Real-world systems incorporate predictive models, coordination between multiple intersections, pedestrian detection, emergency vehicle preemption, and sophisticated optimization algorithms. The &lt;code&gt;THRESHOLD_FOR_SWITCH&lt;/code&gt; and &lt;code&gt;TIME_STEP&lt;/code&gt; would be configurable parameters crucial for fine-tuning performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Hands-On Practice is Non-Negotiable
&lt;/h3&gt;

&lt;p&gt;Understanding the concepts is one thing; making a system like this work in a dynamic environment is another. The real challenge lies in:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Parameter Tuning&lt;/strong&gt;: What's the optimal &lt;code&gt;THRESHOLD_FOR_SWITCH&lt;/code&gt;? How do &lt;code&gt;min_green_time&lt;/code&gt; and &lt;code&gt;max_green_time&lt;/code&gt; interact across multiple intersections?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Edge Cases&lt;/strong&gt;: What happens during peak hours, during an accident, or when sensors fail?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scalability&lt;/strong&gt;: How do you extend this logic from one intersection to an entire city?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Performance&lt;/strong&gt;: Ensuring real-time decisions without introducing latency.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are problems best solved by building, testing, and iterating. Reading about algorithms is great, but getting your hands dirty with a simulated environment lets you see the immediate impact of your configuration choices. It's where you learn the nuances of balancing flow, preventing deadlocks, and optimizing for various metrics.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready to Build Smarter Cities?
&lt;/h3&gt;

&lt;p&gt;Configuring smart traffic systems is a fantastic way to apply your development skills to a tangible, impactful problem. It combines elements of data processing, algorithms, and real-time control. Instead of just theorizing, imagine deploying your own adaptive traffic logic and seeing the results unfold.&lt;/p&gt;

&lt;p&gt;Practice this concept interactively on CodeCityApp — free trial at codecityapp.com&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://codecityapp.com" rel="noopener noreferrer"&gt;CodeCityApp&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>coding</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your pipeline was green for weeks. It shipped nothing.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Tue, 11 Aug 2026 14:00:05 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/your-pipeline-was-green-for-weeks-it-shipped-nothing-59l6</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/your-pipeline-was-green-for-weeks-it-shipped-nothing-59l6</guid>
      <description>&lt;p&gt;There is a category of production failure that monitoring tools are almost designed to miss. The system runs. The pings arrive. Every dashboard stays green. And somewhere behind that green, nothing is happening.&lt;/p&gt;

&lt;p&gt;We hit this with ARIA, the autonomous system we run at Elevare Digital. A pipeline had been silent for weeks. Not crashing — silent. The health checks came in on schedule. The function executed. And the work it existed to do was not getting done.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Liveness (did it run?) and function (did it accomplish anything?) are different properties. Most health checks only measure one.&lt;/li&gt;
&lt;li&gt;A cron that reschedules, retries, or re-checks the same item will emit healthy pings indefinitely while producing zero output.&lt;/li&gt;
&lt;li&gt;The fix is to make your health signal carry a payload: items processed, rows moved, work done.&lt;/li&gt;
&lt;li&gt;Alert on &lt;em&gt;absence of output&lt;/em&gt; over a window, not just on errors.&lt;/li&gt;
&lt;li&gt;A green heartbeat on a pipeline that produces nothing is a failure wearing a healthy costume.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What we were checking vs. what we needed to check
&lt;/h2&gt;

&lt;p&gt;The health check looked like this conceptually:&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;// What we had — liveness only&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;runPipeline&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// might do nothing&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;recordHealth&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="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&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;The function ran. It called &lt;code&gt;doWork()&lt;/code&gt;. It recorded &lt;code&gt;status: ok&lt;/code&gt;. Monitoring saw the ping. Everything looked fine.&lt;/p&gt;

&lt;p&gt;The problem: &lt;code&gt;doWork()&lt;/code&gt; had silently reduced itself to re-checking one item it had already processed. It found nothing new, did nothing, and returned without error. The health signal had no idea. It recorded that the function executed — which was true — and implied from that the pipeline was functioning — which was not.&lt;/p&gt;

&lt;p&gt;This distinction sounds obvious written out. It is easy to miss in practice because the failure mode produces no errors, no exceptions, no timeouts. It produces only silence, and your monitoring is not listening for silence.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mechanism in plain terms
&lt;/h2&gt;

&lt;p&gt;Cron fires. Function wakes up. Function checks for work. No new work is found (or the same old item keeps surfacing and getting skipped). Function exits cleanly. Health ping is recorded.&lt;/p&gt;

&lt;p&gt;Repeat. Every interval. For weeks.&lt;/p&gt;

&lt;p&gt;From the outside: healthy system.&lt;br&gt;
From the inside: a poster that clocks in, sits down, does nothing, clocks out, and files a timesheet marked "completed."&lt;/p&gt;


&lt;h2&gt;
  
  
  The fix: make the health signal carry evidence of work
&lt;/h2&gt;

&lt;p&gt;We changed the health record to include output metrics. Not just "did it run" but "what did it produce."&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;// What we changed to — function-level health&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;runPipeline&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;recordHealth&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="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;items_processed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// the actual payload&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;Then we added a check that looks at recent health records and alerts when a supposedly-active pipeline has emitted zero output over a window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Supabase: find active pipelines that reported no work over the last N intervals&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;pipeline_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items_processed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;total_output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;MAX&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;recorded_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;last_run&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pipeline_health&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;recorded_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'7 days'&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;pipeline_name&lt;/span&gt;
&lt;span class="k"&gt;HAVING&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;items_processed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;last_run&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This query has no interesting results on a healthy system. When it returns rows, a pipeline ran repeatedly and moved nothing — and that is worth an alert regardless of what the status field says.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this failure mode is common
&lt;/h2&gt;

&lt;p&gt;Most health check patterns are borrowed from web services, where liveness and function are closely coupled. If your API endpoint returns 200, it almost certainly did the thing it exists to do. The request-response cycle forces the work to happen before the response is emitted.&lt;/p&gt;

&lt;p&gt;Background pipelines are different. The work is decoupled from the signal. The function can complete — cleanly, successfully — and still have accomplished nothing. The health check fires after execution regardless of output.&lt;/p&gt;

&lt;p&gt;So when teams instrument a pipeline the same way they instrument an endpoint, they end up measuring the wrong thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to add to any pipeline health check
&lt;/h2&gt;

&lt;p&gt;Three fields that matter more than &lt;code&gt;status: ok&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PipelineHealthRecord&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;pipeline_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;recorded_at&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="c1"&gt;// These are the fields that actually tell you something&lt;/span&gt;
  &lt;span class="nl"&gt;items_processed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// work done this run&lt;/span&gt;
  &lt;span class="nl"&gt;items_available&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// work seen (optional but useful)&lt;/span&gt;
  &lt;span class="nl"&gt;error_detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&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;With &lt;code&gt;items_available&lt;/code&gt; you can distinguish between two very different situations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Available = 0, processed = 0: queue is empty, pipeline is idle. Probably fine.&lt;/li&gt;
&lt;li&gt;Available &amp;gt; 0, processed = 0: work exists, pipeline is not touching it. Not fine.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second case is what we had. The pipeline could see work (or thought it could), entered its processing loop, and exited without moving anything. Status: ok the whole time.&lt;/p&gt;




&lt;h2&gt;
  
  
  The alert logic
&lt;/h2&gt;

&lt;p&gt;Once the health records carry real output data, the alert becomes straightforward:&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkPipelineOutput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pipelineName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&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="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pipeline_health&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;items_processed, recorded_at&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="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pipeline_name&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pipelineName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;gte&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recorded_at&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;recorded_at&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;ascending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;data&lt;/span&gt; &lt;span class="o"&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;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&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="c1"&gt;// no runs at all — separate alert&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;totalOutput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;items_processed&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;runCount&lt;/span&gt; &lt;span class="o"&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;length&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="nx"&gt;runCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;totalOutput&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;sendAlert&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;pipeline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;pipelineName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Ran &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;runCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; times in the last 7 days. Produced zero output.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&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="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The threshold (&lt;code&gt;runCount &amp;gt; 3&lt;/code&gt;) is tunable. The point is that multiple runs with zero output is the signal. A single zero-output run might be a quiet period. Several in a row is the pipeline telling you something is wrong, if you are listening.&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest lesson
&lt;/h2&gt;

&lt;p&gt;We were not measuring the wrong thing by accident. We followed a normal health check pattern and it was simply insufficient for this class of job. The pattern works fine for services. It does not work for pipelines.&lt;/p&gt;

&lt;p&gt;Log the absence of work as loudly as the presence of errors. Zero output on a pipeline that should be producing is not a quiet success. It is an invisible failure, and the only difference between that and a noisy crash is that the noisy crash gets fixed.&lt;/p&gt;




&lt;p&gt;— Mike Clarke, founder of Elevare Digital.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>The cleanup code that ate every code block in our published articles</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Tue, 04 Aug 2026 14:00:21 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/the-cleanup-code-that-ate-every-code-block-in-our-published-articles-5620</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/the-cleanup-code-that-ate-every-code-block-in-our-published-articles-5620</guid>
      <description>&lt;p&gt;Liquid syntax error: Variable '{{% raw %}' was not properly terminated with regexp: /\}\}/&lt;/p&gt;
</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Smart Traffic Systems: Configuring Flow for a Smarter City</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Wed, 29 Jul 2026 06:00:20 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/smart-traffic-systems-configuring-flow-for-a-smarter-city-odd</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/smart-traffic-systems-configuring-flow-for-a-smarter-city-odd</guid>
      <description>&lt;h2&gt;
  
  
  Smart Traffic Systems: Configuring Flow for a Smarter City
&lt;/h2&gt;

&lt;p&gt;Ever found yourself stuck in a gridlocked intersection, wondering why the lights aren't cooperating? Or perhaps you've mused about the sheer inefficiency of fixed-timer traffic signals in a dynamic urban landscape. As developers, these aren't just annoyances; they're ripe problems begging for intelligent solutions. And that's where smart traffic systems come in.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Static Systems vs. Dynamic Reality
&lt;/h3&gt;

&lt;p&gt;Traditional traffic light systems operate on pre-programmed timings. They don't care if an ambulance needs to pass, if a bus is running behind schedule, or if one lane is bumper-to-bumper while another is empty. This static approach leads to congestion, increased travel times, higher fuel consumption, and crucially, frustrated citizens. We can do better.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Concept: Dynamic, Data-Driven Control
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How to configure a smart traffic system?&lt;/strong&gt; At its core, it's about shifting from reactive, static timing to proactive, data-driven optimization. A smart system continuously gathers data from various sources: loop detectors, cameras (for vehicle count and classification), GPS data from connected vehicles, even weather sensors. This data feeds into an intelligent agent, often leveraging AI/ML algorithms, to make real-time decisions about traffic light signalization, lane usage, pedestrian crossings, and even suggesting alternative routes.&lt;/p&gt;

&lt;p&gt;Think of it as a central brain that constantly analyzes the pulse of the city's arteries and adjusts flow accordingly. The goal is to minimize overall travel time, reduce congestion, prioritize emergency vehicles, and optimize public transport efficiency. It's a complex optimization problem, often involving multi-agent systems and reinforcement learning.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Glimpse Under the Hood (Pseudocode):
&lt;/h3&gt;

&lt;p&gt;Let's imagine a simplified scenario for a single intersection. Our smart system needs to decide which phase (e.g., North-South green, East-West red) to activate and for how long.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Initialize system components
TrafficLightController = new Controller()
SensorDataStream = new DataStreamService()
OptimalPhaseSelector = new MLModel()

// Main Loop - runs continuously
LOOP:
    // 1. Gather Real-time Data
    currentTrafficData = SensorDataStream.getLiveTrafficData(intersectionID)
    // currentTrafficData might include:
    //    - VehicleCountsPerLane
    //    - AverageWaitingTimesPerLane
    //    - PresenceOfEmergencyVehicles
    //    - PedestrianCrossingRequests

    // 2. Process Data and Determine Optimal Phase
    optimalPhaseResult = OptimalPhaseSelector.predictOptimalPhase(
        currentTrafficData,
        historicalTrafficPatterns,
        currentWeatherConditions
    )
    // optimalPhaseResult might contain:
    //    - recommendedPhase (e.g., 'NS_GREEN')
    //    - recommendedDuration (e.g., 45 seconds)
    //    - priorityFlags (e.g., 'EmergencyVehiclePresent')

    // 3. Command Traffic Lights
    IF optimalPhaseResult.priorityFlags.contains('EmergencyVehiclePresent'):
        TrafficLightController.triggerEmergencyPhase(optimalPhaseResult.recommendedPhase)
    ELSE:
        TrafficLightController.setPhase(optimalPhaseResult.recommendedPhase, optimalPhaseResult.recommendedDuration)

    // 4. Log and Monitor (for debugging and retraining)
    Log.event("Phase changed to " + optimalPhaseResult.recommendedPhase + " for " + optimalPhaseResult.recommendedDuration)
    Monitor.displayCurrentTrafficFlow()

    WAIT for short interval // Re-evaluate every few seconds
END LOOP

// --- Additional functions for OptimalPhaseSelector (simplified) ---

FUNCTION predictOptimalPhase(data, history, weather):
    // Example logic (highly simplified):
    IF data.EmergencyVehicles &amp;gt; 0:
        RETURN {recommendedPhase: 'ClearPathForEmergency', recommendedDuration: 10, priorityFlags: ['EmergencyVehiclePresent']}

    ELSE IF data.LaneTraffic['NorthBound'] &amp;gt; data.LaneTraffic['EastBound'] * 2 AND data.WaitingTimes['NorthBound'] &amp;gt; 60:
        RETURN {recommendedPhase: 'NS_GREEN', recommendedDuration: 60}

    ELSE IF data.PedestrianRequests['EastCross'] AND data.SafeToCross:
        RETURN {recommendedPhase: 'EW_PED_GREEN', recommendedDuration: 20}

    ELSE:
        // Fallback to a default or historical pattern if no strong signal
        RETURN {recommendedPhase: history.getDefaultPhaseForTimeOfDay(), recommendedDuration: 30}
END FUNCTION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pseudocode barely scratches the surface. A real-world system would involve complex state machines, robust fault tolerance, distributed sensing, and sophisticated machine learning models trained on vast datasets. The &lt;code&gt;OptimalPhaseSelector&lt;/code&gt; would likely be a black box of intricate algorithms deciding based on predicted future states, not just current ones.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Practice Matters in This Domain
&lt;/h3&gt;

&lt;p&gt;Configuring a smart traffic system isn't just about writing code; it's about understanding complex systems, optimizing for multiple conflicting objectives, and handling real-time data under pressure. There are countless edge cases, from sensor failures to unexpected traffic surges. Practicing these concepts – building simplified models, simulating various scenarios, and tweaking parameters – is crucial for developing the intuition needed to build robust, life-changing solutions.&lt;/p&gt;

&lt;p&gt;It's one thing to read about Reinforcement Learning; it's another to apply it to make a simulated city's traffic flow smoother. These are problems where your algorithms have a direct, tangible impact.&lt;/p&gt;

&lt;p&gt;Practice this concept interactively on CodeCityApp — free trial at &lt;a href="http://codecityapp.com" rel="noopener noreferrer"&gt;codecityapp.com&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://codecityapp.com" rel="noopener noreferrer"&gt;CodeCityApp&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>coding</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The upsert said it worked. It wrote zero rows. Every single run.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Tue, 28 Jul 2026 14:00:04 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/the-upsert-said-it-worked-it-wrote-zero-rows-every-single-run-47e1</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/the-upsert-said-it-worked-it-wrote-zero-rows-every-single-run-47e1</guid>
      <description>&lt;p&gt;Your scanner runs. It fetches data. It loops through rows, calls upsert, increments a success counter. It logs &lt;code&gt;✓ 42 rows processed&lt;/code&gt;. You check the table. Empty.&lt;/p&gt;

&lt;p&gt;That ran every day for a while before we caught it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ON CONFLICT (col)&lt;/code&gt; will &lt;strong&gt;not&lt;/strong&gt; use a partial unique index (&lt;code&gt;WHERE col IS NOT NULL&lt;/code&gt;) as the conflict arbiter. Postgres requires the predicate to be restated in the statement itself.&lt;/li&gt;
&lt;li&gt;PostgREST's upsert path can't restate that predicate. Every row throws a quiet error.&lt;/li&gt;
&lt;li&gt;A loop that only increments on success will report a perfectly healthy run that wrote absolutely nothing.&lt;/li&gt;
&lt;li&gt;Fix: replace the partial index with a plain, unconditional unique index.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The setup
&lt;/h2&gt;

&lt;p&gt;At Elevare Digital, ARIA runs data ingestion pipelines that pull from external sources and upsert into Postgres via Supabase. Standard stuff. One scanner's job was to fetch records and land them in a table, using a unique column as the conflict target so re-runs were idempotent.&lt;/p&gt;

&lt;p&gt;The index on that column had been created like this, probably by someone being careful about NULLs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;records_external_id_idx&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reasonable-looking. Partial indexes are useful. This one quietly broke everything.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Postgres actually requires
&lt;/h2&gt;

&lt;p&gt;When you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'abc-123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{...}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;
  &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Postgres needs to identify which unique constraint or index to use as the arbiter. For a &lt;strong&gt;full&lt;/strong&gt; unique index, &lt;code&gt;ON CONFLICT (external_id)&lt;/code&gt; is enough — Postgres finds it.&lt;/p&gt;

&lt;p&gt;For a &lt;strong&gt;partial&lt;/strong&gt; unique index, Postgres requires the conflict target to include the index predicate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'abc-123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'{...}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;  &lt;span class="c1"&gt;-- restate the predicate&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt;
  &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Without that &lt;code&gt;WHERE&lt;/code&gt; clause, Postgres cannot match the partial index. It doesn't fall back. It throws an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can verify this yourself in a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Create a table with a partial unique index&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;serial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="nb"&gt;text&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;demo_ext_id_partial&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- This fails — Postgres can't match the partial index&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'x1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- ERROR: there is no unique or exclusion constraint matching the ON CONFLICT specification&lt;/span&gt;

&lt;span class="c1"&gt;-- This works — predicate restated&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'x1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'a'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;external_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;
&lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;SET&lt;/span&gt; &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;EXCLUDED&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- INSERT 0 1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why PostgREST couldn't restate the predicate
&lt;/h2&gt;

&lt;p&gt;ARIA calls Supabase's upsert via PostgREST. The request looks something like:&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="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;records&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="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onConflict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;PostgREST translates &lt;code&gt;onConflict: 'external_id'&lt;/code&gt; into:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no interface in PostgREST's upsert path to append an arbitrary &lt;code&gt;WHERE&lt;/code&gt; predicate to the conflict target. So every row-level upsert hit the same error. Every row.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why the loop reported success
&lt;/h2&gt;

&lt;p&gt;The ingestion loop looked roughly 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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;successCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &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;row&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fetchedRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;records&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="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onConflict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external_id&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;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;successCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// error case: nothing. no log, no throw, no increment.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✓ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;successCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; rows processed`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When &lt;code&gt;error&lt;/code&gt; was non-null, the loop just... continued. No log. No counter. The success counter never moved, but &lt;code&gt;fetchedRows.length&lt;/code&gt; rows later, it logged a number that looked fine because the fetch itself succeeded.&lt;/p&gt;

&lt;p&gt;Actually — it logged &lt;code&gt;0&lt;/code&gt;. But nobody was watching for zero. Zero looks like "nothing to sync today."&lt;/p&gt;

&lt;p&gt;A counter that only moves on success will always look plausible. You need to also track failures:&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="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;successCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;errorCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;for &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;row&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;fetchedRows&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;records&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="nf"&gt;upsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;onConflict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;external_id&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="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;errorCount&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upsert failed:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;row&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;successCount&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="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`processed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;successCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ok, &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;errorCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; failed of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fetchedRows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; fetched`&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="nx"&gt;errorCount&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`ingestion completed with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;errorCount&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; errors`&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;Now a run that writes nothing will at least be loud about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix
&lt;/h2&gt;

&lt;p&gt;Drop the partial index. Create a plain one.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;DROP&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;records_external_id_idx&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;UNIQUE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;records_external_id_idx&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;records&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;external_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If NULLs are genuinely a concern, handle them at the application layer or with a &lt;code&gt;NOT NULL&lt;/code&gt; constraint on the column. A partial index to "exclude NULLs" is not worth the silent failure mode when you need conflict arbitration.&lt;/p&gt;

&lt;p&gt;After the index swap, the PostgREST upsert matched correctly and rows started landing.&lt;/p&gt;




&lt;h2&gt;
  
  
  The honest lesson
&lt;/h2&gt;

&lt;p&gt;Postgres did nothing wrong here. The docs describe this requirement. The error message is clear. The problem was that the error was being swallowed and nothing downstream cared that zero rows were written.&lt;/p&gt;

&lt;p&gt;Two independent things had to both be true for this to stay hidden:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A partial index being used as an &lt;code&gt;ON CONFLICT&lt;/code&gt; target without the predicate&lt;/li&gt;
&lt;li&gt;A loop that treated errors as a no-op&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Either one alone might have surfaced faster. Together, they produced a system that appeared healthy and did nothing.&lt;/p&gt;

&lt;p&gt;When you write an ingestion loop, the number that matters isn't "how many did I try" or "how many succeeded." It's the ratio. And if errors are silent, you'll never compute it.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;— Mike Clarke, founder of Elevare Digital.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>The leads were in the table. The automation never saw them.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Fri, 24 Jul 2026 14:00:12 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/the-leads-were-in-the-table-the-automation-never-saw-them-24o</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/the-leads-were-in-the-table-the-automation-never-saw-them-24o</guid>
      <description>&lt;p&gt;There were two groups of leads sitting in &lt;code&gt;contacted&lt;/code&gt; state. One group was getting follow-up emails, call tasks, the whole sequence. The other group was getting nothing — indefinitely.&lt;/p&gt;

&lt;p&gt;Same status. Same table. Different outcomes. No errors anywhere.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A "dead population path" is when one write route into a table skips a step that downstream automation assumes happened.&lt;/li&gt;
&lt;li&gt;Bulk imports are the most common source of this. They write directly to the final state, skipping every intermediate code path.&lt;/li&gt;
&lt;li&gt;The fix is not patching the import. It's a trigger that enforces enrollment regardless of how a row arrives.&lt;/li&gt;
&lt;li&gt;For every table your automation reads: list every way rows enter it, then verify each path runs the prerequisites downstream depends on.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What was actually happening
&lt;/h2&gt;

&lt;p&gt;The CRM pipeline in ARIA works like this: a lead gets contacted, the first-contact handler fires, it enrolls the lead in the follow-up sequence. That enrollment step lived exclusively inside the first-contact code path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User action / API call
  → first-contact handler
      → write lead to `contacted` state
      → enroll in follow-up sequence  ← this is the step
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Someone ran a bulk import. It wrote records straight to &lt;code&gt;contacted&lt;/code&gt;. No handler. No enrollment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Bulk import
  → write lead to `contacted` state
                                      ← nothing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The downstream automation that sends follow-ups reads the &lt;code&gt;leads&lt;/code&gt; table and looks for enrolled leads in &lt;code&gt;contacted&lt;/code&gt; state. The imported leads were in the right table, in the right state, but they were never enrolled. The automation skipped them every single cycle — correctly, by its own logic.&lt;/p&gt;

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

&lt;p&gt;The gap never closed because there was no reconciliation job. Nothing periodically asked "are there &lt;code&gt;contacted&lt;/code&gt; leads that aren't in the sequence?" The imported population just sat there.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is easy to miss
&lt;/h2&gt;

&lt;p&gt;The table looked fine. The leads looked fine. The automation looked fine.&lt;/p&gt;

&lt;p&gt;The bug was architectural: there were two write routes into one table, and only one of them ran the setup step the other parts of the system depended on. The system had no way to detect the discrepancy because it never compared "who should be enrolled" against "who is enrolled." It just processed whoever was already enrolled.&lt;/p&gt;

&lt;p&gt;This is what I'd call a dead population path. The rows exist. The automation is running. The rows are just invisible to it.&lt;/p&gt;

&lt;p&gt;Bulk imports create this constantly because they're designed to skip your application layer — that's literally their value proposition. The problem is your application layer is often where side effects live.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: enforce enrollment at the database layer
&lt;/h2&gt;

&lt;p&gt;The only reliable fix is moving the enrollment check to a place that runs regardless of how the row was written. Postgres triggers do this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- The trigger function&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;enroll_contacted_lead&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="c1"&gt;-- Only act on rows entering or already in 'contacted' state&lt;/span&gt;
  &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'contacted'&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
    &lt;span class="c1"&gt;-- Check whether this lead qualifies for enrollment&lt;/span&gt;
    &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigned_to&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
      &lt;span class="c1"&gt;-- Enroll if not already enrolled&lt;/span&gt;
      &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;follow_up_enrollments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrolled_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sequence_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequence_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;NOTHING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;ELSE&lt;/span&gt;
      &lt;span class="c1"&gt;-- Log why it was skipped so you can find these later&lt;/span&gt;
      &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;enrollment_skipped_log&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skipped_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;CASE&lt;/span&gt;
          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigned_to&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'no_assignee'&lt;/span&gt;
          &lt;span class="k"&gt;WHEN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt; &lt;span class="s1"&gt;'no_email'&lt;/span&gt;
          &lt;span class="k"&gt;ELSE&lt;/span&gt; &lt;span class="s1"&gt;'unknown'&lt;/span&gt;
        &lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;CONFLICT&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;DO&lt;/span&gt; &lt;span class="k"&gt;NOTHING&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- Attach it to both INSERT and UPDATE&lt;/span&gt;
&lt;span class="c1"&gt;-- so it catches bulk imports (INSERT) and status changes (UPDATE) alike&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;trg_enroll_contacted_lead&lt;/span&gt;
&lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;UPDATE&lt;/span&gt; &lt;span class="k"&gt;OF&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;leads&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt;
&lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;enroll_contacted_lead&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth noting about this implementation:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;ON CONFLICT DO NOTHING&lt;/code&gt;&lt;/strong&gt; on the enrollment insert means existing correctly-enrolled leads are untouched. Safe to run on a table that already has good data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The skip log&lt;/strong&gt; is not optional. If a lead doesn't qualify, you want to know why. Without it, you're back to silent failures. The log gives you a query you can run to find every lead that got skipped and the reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;AFTER INSERT OR UPDATE OF status&lt;/code&gt;&lt;/strong&gt; covers both paths. The bulk import fires &lt;code&gt;INSERT&lt;/code&gt;. A status change through the application fires &lt;code&gt;UPDATE&lt;/code&gt;. Both paths now run the same enrollment logic.&lt;/p&gt;

&lt;p&gt;In Supabase, you apply this in the SQL editor or through a migration file. No special Supabase API needed — it's just Postgres.&lt;/p&gt;




&lt;h2&gt;
  
  
  Backfilling the existing gap
&lt;/h2&gt;

&lt;p&gt;The trigger handles new rows. But the affected leads already in the table needed a one-time backfill.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Enroll any existing contacted leads that slipped through&lt;/span&gt;
&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;follow_up_enrollments&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrolled_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sequence_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sequence_id&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;leads&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;follow_up_enrollments&lt;/span&gt; &lt;span class="n"&gt;fe&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt;
  &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'contacted'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;fe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lead_id&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;           &lt;span class="c1"&gt;-- not already enrolled&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;assigned_to&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;    &lt;span class="c1"&gt;-- qualifies&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="k"&gt;IS&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;         &lt;span class="c1"&gt;-- qualifies&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run this once after deploying the trigger. Then the trigger maintains the invariant going forward.&lt;/p&gt;




&lt;h2&gt;
  
  
  The question to ask about every table your automation reads
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;What are all the ways rows get into this table, and does each one run the steps downstream assumes happened?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For most tables the answer is one or two paths and they're all through your application code. Fine.&lt;/p&gt;

&lt;p&gt;But the moment you add a bulk import, a migration script, a database-level upsert from an external tool, or a direct admin write — you've added a path that bypasses your application layer. Any side effects that lived there are now missing for those rows.&lt;/p&gt;

&lt;p&gt;The fix isn't to never import. It's to stop trusting that write path determines setup steps. Push the invariants down to the database, where they apply to every write regardless of origin.&lt;/p&gt;




&lt;h2&gt;
  
  
  Honest lesson
&lt;/h2&gt;

&lt;p&gt;This was a silent failure that looked like working software. The table had data. The automation was running. The logs showed no errors. The only way to notice was to ask why two groups with the same status were having different outcomes — and actually go find the answer.&lt;/p&gt;

&lt;p&gt;AI systems like ARIA that run autonomously are more exposed to this class of bug, not less. There's no human checking each lead. The automation either processes them or it doesn't, and if it doesn't, you need something in the system that catches the gap. A trigger logging skipped records is a simple form of that. A reconciliation job that periodically diffs expected vs actual enrollment is a stronger one.&lt;/p&gt;

&lt;p&gt;Both beat finding out months later when a customer asks why no one followed up.&lt;/p&gt;




&lt;p&gt;— Mike Clarke, founder of Elevare Digital.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>The table looked general-purpose. The schema disagreed.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Tue, 21 Jul 2026 14:00:09 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/the-table-looked-general-purpose-the-schema-disagreed-9h9</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/the-table-looked-general-purpose-the-schema-disagreed-9h9</guid>
      <description>&lt;p&gt;A CHECK constraint is documentation with teeth.&lt;/p&gt;

&lt;p&gt;That's the whole lesson. But here's what it looks like when you learn it at runtime instead of at read-time.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CHECK constraint encodes scope assumptions the column name never will&lt;/li&gt;
&lt;li&gt;A constraint violation inside a trigger aborts the parent transaction — silently, from the caller's perspective&lt;/li&gt;
&lt;li&gt;Before writing to a table you inherited, run &lt;code&gt;\d tablename&lt;/code&gt; or query &lt;code&gt;information_schema.check_constraints&lt;/code&gt;. Read what's there.&lt;/li&gt;
&lt;li&gt;Gate on allowed values in code before the insert. Let the constraint be a backstop, not the first line of defense.&lt;/li&gt;
&lt;li&gt;Skip-and-log beats throw-and-abort when the parent write is more important than the child record&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What happened
&lt;/h2&gt;

&lt;p&gt;We run ARIA, an autonomous CRM and nurture automation system at Elevare Digital. New contact records enroll into follow-up sequences automatically — no human queues the work.&lt;/p&gt;

&lt;p&gt;At some point, new records stopped enrolling. The parent write (creating the contact) was aborting entirely. No sequence. No contact. No error surfaced to the caller in a useful way.&lt;/p&gt;

&lt;p&gt;The culprit was a &lt;code&gt;region&lt;/code&gt; column with a CHECK constraint that looked roughly like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;enrollment_tracker&lt;/span&gt;
  &lt;span class="k"&gt;ADD&lt;/span&gt; &lt;span class="k"&gt;CONSTRAINT&lt;/span&gt; &lt;span class="n"&gt;region_allowed&lt;/span&gt;
  &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'north'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'south'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'east'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'west'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'central'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The table had been built for one specific program covering five regions. Later, code started treating it as a general enrollment tracker and writing region values the constraint never anticipated. Postgres rejected the insert. Because that insert happened inside a trigger, the whole parent transaction rolled back.&lt;/p&gt;

&lt;p&gt;The column was named &lt;code&gt;region&lt;/code&gt;. Nothing in that name says "only these five values are valid." The constraint said it. Nobody read the constraint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why a trigger makes this worse
&lt;/h2&gt;

&lt;p&gt;If you insert directly into a table and violate a CHECK, you get an error back immediately. Annoying, but contained.&lt;/p&gt;

&lt;p&gt;When the insert is inside a trigger on a &lt;em&gt;different&lt;/em&gt; table, the error propagates up and aborts the statement that fired the trigger. The caller sees their write fail. They may have no idea a trigger was involved, let alone which constraint fired inside it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Trigger fires on INSERT to contacts&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;enroll_contact&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="c1"&gt;-- This insert can blow up the parent INSERT INTO contacts&lt;/span&gt;
  &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;enrollment_tracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contact_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrolled_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TRIGGER&lt;/span&gt; &lt;span class="n"&gt;trg_enroll&lt;/span&gt;
&lt;span class="k"&gt;AFTER&lt;/span&gt; &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;contacts&lt;/span&gt;
&lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;EACH&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;EXECUTE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;enroll_contact&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now do this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;contacts&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gen_random_uuid&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'Acme Corp'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'southeast'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;-- ERROR: new row for relation "enrollment_tracker" violates&lt;/span&gt;
&lt;span class="c1"&gt;-- check constraint "region_allowed"&lt;/span&gt;
&lt;span class="c1"&gt;-- DETAIL: Failing row contains (..., southeast, ...).&lt;/span&gt;
&lt;span class="c1"&gt;-- The contact was NOT created.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;southeast&lt;/code&gt; is a perfectly valid business concept. The table just never knew about it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: gate before you insert
&lt;/h2&gt;

&lt;p&gt;Once we understood the constraint, the fix was straightforward. Check the allowed values in code (or in the trigger function itself) before attempting the insert. If the value isn't allowed, skip the enrollment record and log it. The parent write completes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="k"&gt;REPLACE&lt;/span&gt; &lt;span class="k"&gt;FUNCTION&lt;/span&gt; &lt;span class="n"&gt;enroll_contact&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="k"&gt;RETURNS&lt;/span&gt; &lt;span class="k"&gt;trigger&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="k"&gt;DECLARE&lt;/span&gt;
  &lt;span class="n"&gt;allowed_regions&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ARRAY&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'north'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'south'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'east'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'west'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'central'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="k"&gt;BEGIN&lt;/span&gt;
  &lt;span class="n"&gt;IF&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;ANY&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;allowed_regions&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;THEN&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;enrollment_tracker&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contact_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;enrolled_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;ELSE&lt;/span&gt;
    &lt;span class="c1"&gt;-- Log it; don't abort the parent write&lt;/span&gt;
    &lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;enrollment_skipped&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;contact_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;skipped_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s1"&gt;'region not in enrollment_tracker allowed list'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;END&lt;/span&gt; &lt;span class="n"&gt;IF&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;RETURN&lt;/span&gt; &lt;span class="k"&gt;NEW&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;END&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt;$$&lt;/span&gt; &lt;span class="k"&gt;LANGUAGE&lt;/span&gt; &lt;span class="n"&gt;plpgsql&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternately, you can query the constraint definition directly rather than hardcoding the list — which is useful if the allowed values might expand:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Pull allowed values from the constraint definition at runtime&lt;/span&gt;
&lt;span class="c1"&gt;-- (useful for visibility; hardcoding is fine if values are stable)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;consrc&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;pg_constraint&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;conname&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'region_allowed'&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;conrelid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'enrollment_tracker'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regclass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Returns something like &lt;code&gt;(region = ANY (ARRAY['north'::text, 'south'::text, ...]))&lt;/code&gt;. Not the cleanest parse, but it tells you exactly what the schema intended.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to read the constraints you inherited
&lt;/h2&gt;

&lt;p&gt;Before writing to any table you didn't build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- psql shortcut&lt;/span&gt;
&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt; &lt;span class="n"&gt;enrollment_tracker&lt;/span&gt;

&lt;span class="c1"&gt;-- Or query directly&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;constraint_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;constraint_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_clause&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;table_constraints&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt;
&lt;span class="k"&gt;LEFT&lt;/span&gt; &lt;span class="k"&gt;JOIN&lt;/span&gt; &lt;span class="n"&gt;information_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;check_constraints&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;constraint_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;constraint_name&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;tc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;table_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'enrollment_tracker'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Constraints you'll find this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;CHECK&lt;/code&gt; — allowed values, ranges, cross-column rules&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;UNIQUE&lt;/code&gt; — uniqueness you may not have assumed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NOT NULL&lt;/code&gt; — columns the schema considers required&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;FOREIGN KEY&lt;/code&gt; — referential dependencies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is hidden. It's just rarely read.&lt;/p&gt;




&lt;h2&gt;
  
  
  The actual lesson
&lt;/h2&gt;

&lt;p&gt;The table name was &lt;code&gt;enrollment_tracker&lt;/code&gt;. That sounds general. It wasn't — it was built for a specific program with five regions, and the CHECK constraint was the only place that scope was written down.&lt;/p&gt;

&lt;p&gt;When later code treated it as a general tracker, it imported an assumption it never knew was there. The schema surfaced that assumption at write time, inside a trigger, in a way that took down the parent record.&lt;/p&gt;

&lt;p&gt;Schema constraints are the closest thing to binding documentation that most databases have. They don't drift. They don't get outdated and left in a wiki. They're enforced.&lt;/p&gt;

&lt;p&gt;Read them before you write. Not after.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;— Mike Clarke, founder of Elevare Digital.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your AI agent checked its queue, found nothing, and went back to sleep. The queue was full.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Fri, 17 Jul 2026 14:00:09 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/your-ai-agent-checked-its-queue-found-nothing-and-went-back-to-sleep-the-queue-was-full-1m4</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/your-ai-agent-checked-its-queue-found-nothing-and-went-back-to-sleep-the-queue-was-full-1m4</guid>
      <description>&lt;p&gt;Postgres Row-Level Security doesn't raise an error when it blocks you. It returns zero rows. Your query succeeds. Your agent sees an empty queue. Your agent idles. Your jobs pile up.&lt;/p&gt;

&lt;p&gt;This is what happened to ARIA, our autonomous AI system at Elevare Digital.&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Postgres RLS silently filters rows on &lt;code&gt;SELECT&lt;/code&gt; — a blocked read and a genuinely empty table look identical to the caller&lt;/li&gt;
&lt;li&gt;An orchestrator that reads its own queue gets no exception, no warning, no non-200 status code when RLS blocks it&lt;/li&gt;
&lt;li&gt;A healthy heartbeat on an idle agent tells you nothing about whether the idle is real&lt;/li&gt;
&lt;li&gt;The fix: add a canary count that verifies you &lt;em&gt;can&lt;/em&gt; read, not just that you &lt;em&gt;did&lt;/em&gt; read&lt;/li&gt;
&lt;li&gt;Treat an empty read as a question, not an answer&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the agent saw
&lt;/h2&gt;

&lt;p&gt;The orchestrator polls a work queue table. Normal operation:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Query for pending jobs&lt;/li&gt;
&lt;li&gt;If results → process them&lt;/li&gt;
&lt;li&gt;If empty → log idle heartbeat, sleep&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The logs showed healthy idle heartbeats. Monitoring showed the agent alive and polling. From the outside, everything looked fine.&lt;/p&gt;

&lt;p&gt;Jobs were not being processed.&lt;/p&gt;




&lt;h2&gt;
  
  
  What actually happened
&lt;/h2&gt;

&lt;p&gt;Someone added a Row-Level Security policy to the queue table, scoped to &lt;code&gt;auth.uid()&lt;/code&gt;. Correct for user-facing reads. But the orchestrator connects under the service role — and no service-role bypass was added to the policy.&lt;/p&gt;

&lt;p&gt;Here's the policy as it was written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Added for user-facing queue visibility&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"users_see_own_jobs"&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;work_queue&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And RLS was enabled on the table:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;work_queue&lt;/span&gt; &lt;span class="n"&gt;ENABLE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No bypass for the service role. No &lt;code&gt;FOR ALL&lt;/code&gt; escape. The orchestrator's &lt;code&gt;SELECT&lt;/code&gt; now matched zero rows — because RLS filtered every row out before returning results.&lt;/p&gt;

&lt;p&gt;Postgres does not raise. It does not warn. The query completes with status 200 and an empty array.&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;// Orchestrator poll — looks completely normal&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;work_queue&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&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="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// error is null. jobs is []. Agent concludes: nothing to do.&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;jobs&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;recordIdleHeartbeat&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;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;error&lt;/code&gt; is null. The &lt;code&gt;jobs&lt;/code&gt; array is empty. The agent's logic is correct for the data it received. The data it received was wrong in a way that produced no signal.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is hard to catch
&lt;/h2&gt;

&lt;p&gt;If RLS blocks a write, you often get an error — the row you tried to insert violates a policy, or returns nothing when you expected a rowcount. Writes are easier to notice.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SELECT&lt;/code&gt; under RLS is different. The design intent is that users should not be able to distinguish "this row doesn't exist" from "this row exists but you can't see it." That's a security feature. It's also exactly the wrong behavior for an orchestrator that needs to know the difference between "queue is empty" and "I am blind."&lt;/p&gt;

&lt;p&gt;The service role in Supabase bypasses RLS by default — but only if you're using the service-role key on the client. If your edge function or backend is using the anon key, or if it's operating in a context where &lt;code&gt;auth.uid()&lt;/code&gt; resolves to null, RLS applies and silently filters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- This is what the orchestrator needed, but wasn't there&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"service_role_bypass"&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;work_queue&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'service_role'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Or, simpler: grant the service role explicit bypass at the table level&lt;/span&gt;
&lt;span class="k"&gt;ALTER&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;work_queue&lt;/span&gt; &lt;span class="k"&gt;FORCE&lt;/span&gt; &lt;span class="k"&gt;ROW&lt;/span&gt; &lt;span class="k"&gt;LEVEL&lt;/span&gt; &lt;span class="k"&gt;SECURITY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;-- applies to all&lt;/span&gt;
&lt;span class="c1"&gt;-- and then in your Supabase client: use the service-role key, which bypasses RLS automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The actual bypass mechanism in Supabase is that the service-role JWT includes &lt;code&gt;"role": "service_role"&lt;/code&gt;, and Supabase's Postgres config grants that role &lt;code&gt;BYPASSRLS&lt;/code&gt;. If your client is initialized with the service-role key, you're fine. If it's not, RLS applies — no error, just silence.&lt;/p&gt;




&lt;h2&gt;
  
  
  The fix: a canary count
&lt;/h2&gt;

&lt;p&gt;The real repair has two parts: fix the RLS policy, and add a check that will catch this failure mode again.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Part 1 — Fix the policy:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Preserve user-facing policy&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"users_see_own_jobs"&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;work_queue&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;SELECT&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- Add explicit service-role access&lt;/span&gt;
&lt;span class="c1"&gt;-- (Or: initialize your orchestrator client with the service-role key,&lt;/span&gt;
&lt;span class="c1"&gt;--  which bypasses RLS automatically via BYPASSRLS grant)&lt;/span&gt;
&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="n"&gt;POLICY&lt;/span&gt; &lt;span class="nv"&gt;"orchestrator_full_access"&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;work_queue&lt;/span&gt;
  &lt;span class="k"&gt;FOR&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;
  &lt;span class="k"&gt;USING&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'service_role'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="k"&gt;CHECK&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;role&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'service_role'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Part 2 — The canary check:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The orchestrator now runs a canary query before trusting an empty result. The canary queries a row it knows exists — a sentinel row inserted specifically for this purpose, or a count from an unrestricted table the orchestrator owns.&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;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pollQueue&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jobs&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="nx"&gt;queueError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;work_queue&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&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="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending&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="nx"&gt;queueError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;alertOpsChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queue_poll_error&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;queueError&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;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Jobs came back — process normally&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobs&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;jobs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processJobs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jobs&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;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Empty result — but is it genuinely empty, or are we blind?&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;canaryOk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;verifyReadAccess&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;canaryOk&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// This is the case we were missing before&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;alertOpsChannel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;queue_read_access_lost&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;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Orchestrator received empty queue result but canary check failed. Possible RLS or permission regression.&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;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Do NOT record idle heartbeat — we don't know the real state&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Canary passed — empty really means empty&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;recordIdleHeartbeat&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&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;verifyReadAccess&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Query a sentinel row that always exists in a known state&lt;/span&gt;
  &lt;span class="c1"&gt;// This could be a dedicated canary table, or a system-health row&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;orchestrator_canary&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;heartbeat-sentinel&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="nf"&gt;single&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="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// We expected exactly one row. Getting nothing means access is broken.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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="kc"&gt;true&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 canary table is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;orchestrator_canary&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;orchestrator_canary&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;VALUES&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'heartbeat-sentinel'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;-- No RLS on this table — it exists only so the orchestrator can verify it can read&lt;/span&gt;
&lt;span class="c1"&gt;-- If you want RLS: add only a service-role policy, nothing user-facing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the orchestrator has three states instead of two:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Jobs found → process&lt;/li&gt;
&lt;li&gt;No jobs, canary ok → genuinely idle&lt;/li&gt;
&lt;li&gt;No jobs, canary failed → alert, do not idle&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The third state was always real. We just had no way to observe it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The deeper issue with autonomous agents and silent failures
&lt;/h2&gt;

&lt;p&gt;Human-in-the-loop systems fail loudly. A user tries to load their queue, sees nothing, and files a ticket. An autonomous agent has no user. It reads, decides, acts. If the read is silently wrong, the decision is wrong, and nothing complains.&lt;/p&gt;

&lt;p&gt;This failure mode matters more as systems get more autonomous. ARIA processes queued work without someone watching every poll cycle. The assumption baked into the polling loop was: &lt;em&gt;an empty read means there is nothing to do.&lt;/em&gt; That assumption held until it didn't, and the system had no way to question it.&lt;/p&gt;

&lt;p&gt;The fix is to make the orchestrator skeptical of its own empty results. Not paranoid — just skeptical enough to verify the precondition that makes "empty" meaningful.&lt;/p&gt;




&lt;h2&gt;
  
  
  What to check in your own system
&lt;/h2&gt;

&lt;p&gt;If you're running any kind of queue worker against Postgres or Supabase:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Check which key your worker is using.&lt;/strong&gt; Supabase service-role key bypasses RLS. Anon key does not. Confirm which one is in your edge function or backend environment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;List your RLS policies and check for missing bypasses.&lt;/strong&gt; &lt;code&gt;SELECT * FROM pg_policies WHERE tablename = 'your_queue_table';&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Look at your idle heartbeats.&lt;/strong&gt; If idle logging increased around the time you last modified permissions or added RLS, that's worth investigating.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Add a canary.&lt;/strong&gt; Takes an hour. Catches this entire class of problem permanently.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;An empty queue result is not evidence of an empty queue. It's evidence that the query ran. Those are different things, and in autonomous systems, the difference matters.&lt;/p&gt;

&lt;p&gt;— Mike Clarke, founder of Elevare Digital.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>The function was deployed, healthy, and never ran once</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Tue, 14 Jul 2026 14:00:05 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/the-function-was-deployed-healthy-and-never-ran-once-1hgd</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/the-function-was-deployed-healthy-and-never-ran-once-1hgd</guid>
      <description>&lt;p&gt;There's a category of bug where nothing is broken. The dashboard is green. The deployment succeeded. The function shows a recent update timestamp. And it has done zero work since you shipped it.&lt;/p&gt;

&lt;p&gt;We hit this in ARIA, our autonomous agent system. A scheduled function was silently doing nothing — not erroring, not timing out, just... absent.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Supabase edge function can be perfectly deployed and still never execute&lt;/li&gt;
&lt;li&gt;JWT verification happens at the gateway, before your handler runs&lt;/li&gt;
&lt;li&gt;Gateway-level 401s produce no application logs, no error rows, no telemetry inside the function&lt;/li&gt;
&lt;li&gt;The only signal is the absence of invocation-log entries&lt;/li&gt;
&lt;li&gt;Functions called by &lt;code&gt;pg_cron&lt;/code&gt; need &lt;code&gt;verify_jwt=false&lt;/code&gt; if they authenticate via service-role key in the body&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What we saw
&lt;/h2&gt;

&lt;p&gt;ARIA runs a multi-agent scheduler. Several edge functions are invoked on a schedule via &lt;code&gt;pg_cron&lt;/code&gt;. One of them showed up in the Supabase dashboard as deployed, healthy, recently updated. We had no alerts firing.&lt;/p&gt;

&lt;p&gt;What we didn't have: any invocation log rows. Not failed ones. Not slow ones. Zero rows.&lt;/p&gt;

&lt;p&gt;Everything looked fine. Nothing was running.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mechanism
&lt;/h2&gt;

&lt;p&gt;Supabase edge functions have a &lt;code&gt;verify_jwt&lt;/code&gt; flag. When it's set to &lt;code&gt;true&lt;/code&gt; (the default), the gateway validates the bearer token on every request before the function body executes.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;pg_cron&lt;/code&gt; job was calling the function with a service-role bearer token — correct credentials, correct format. But the gateway's JWT verification was rejecting it and returning a &lt;code&gt;401&lt;/code&gt; before the handler ever ran.&lt;/p&gt;

&lt;p&gt;The request never reached the function. The function had nothing to log.&lt;/p&gt;

&lt;p&gt;Here's what the pg_cron invocation looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;cron&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;schedule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="s1"&gt;'run-agent-task'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'* * * * *'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="err"&gt;$$&lt;/span&gt;
  &lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="n"&gt;net&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;http_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'https://&amp;lt;project&amp;gt;.supabase.co/functions/v1/agent-task'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonb_build_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s1"&gt;'Content-Type'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Bearer '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;current_setting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app.service_role_key'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'{}'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;jsonb&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="err"&gt;$$&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And here's the function config that caused the silent failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="c"&gt;# supabase/functions/agent-task/config.toml&lt;/span&gt;
&lt;span class="nn"&gt;[functions.agent-task]&lt;/span&gt;
&lt;span class="py"&gt;verify_jwt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;  &lt;span class="c"&gt;# &amp;lt;-- gateway rejects the cron call here, handler never runs&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is one line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[functions.agent-task]&lt;/span&gt;
&lt;span class="py"&gt;verify_jwt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;  &lt;span class="c"&gt;# gateway passes the request through; function handles auth itself&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With &lt;code&gt;verify_jwt=false&lt;/code&gt;, you move the authentication check inside the function body, where you can actually see what happens:&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;serve&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;https://deno.land/std@0.168.0/http/server.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &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="o"&gt;=&amp;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;authHeader&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="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="c1"&gt;// Validate against your service role key inside the handler&lt;/span&gt;
  &lt;span class="c1"&gt;// where failures are visible in your logs&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;Deno&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="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="s1"&gt;SUPABASE_SERVICE_ROLE_KEY&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized request to agent-task&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Unauthorized&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;401&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// actual work happens here&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;agent-task executing&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ok&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;200&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;Now when auth fails, you get a log line. You get an invocation row. You get something to look at.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why this is hard to catch
&lt;/h2&gt;

&lt;p&gt;The normal debugging loop assumes that if something failed, there's a record of the failure. You look at logs, you find the error, you fix it.&lt;/p&gt;

&lt;p&gt;Gateway-level rejections break that loop. The 401 happens in infrastructure you don't control and don't have direct log access to. From the function's perspective, the request never arrived. From the cron job's perspective, it fired successfully — &lt;code&gt;net.http_post&lt;/code&gt; queued the request and moved on.&lt;/p&gt;

&lt;p&gt;The only diagnostic signal is the shape of what's missing: no invocation rows in the function's log table, for a function that should be running every minute.&lt;/p&gt;

&lt;p&gt;If you're not actively checking for expected invocations, you won't notice.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we added after
&lt;/h2&gt;

&lt;p&gt;Beyond fixing the flag, we added a simple liveness check: a separate monitor queries the invocation log and alerts if a scheduled function hasn't produced a row in longer than two of its scheduled intervals. Not sophisticated, but it catches the "deployed and doing nothing" class of failure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- Example: check that agent-task has run in the last 3 minutes&lt;/span&gt;
&lt;span class="c1"&gt;-- (for a function scheduled every minute)&lt;/span&gt;
&lt;span class="k"&gt;select&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt;
    &lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="k"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'3 minutes'&lt;/span&gt;
    &lt;span class="k"&gt;then&lt;/span&gt; &lt;span class="s1"&gt;'STALLED'&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s1"&gt;'OK'&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;function_invocation_log&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="n"&gt;function_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'agent-task'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The specific table structure depends on how you surface invocation logs in your setup — the point is to query for expected evidence of execution, not just absence of errors.&lt;/p&gt;




&lt;h2&gt;
  
  
  The thing worth internalizing
&lt;/h2&gt;

&lt;p&gt;Deployment success and runtime success are different things. A function can pass every health check and still never execute a single line of your code.&lt;/p&gt;

&lt;p&gt;For any function invoked by infrastructure (cron, queues, webhooks) rather than by a user, you need positive confirmation of execution — not just the absence of errors. Absence of errors and absence of execution look identical from the outside.&lt;/p&gt;

&lt;p&gt;Check your cron-invoked functions. If &lt;code&gt;verify_jwt=true&lt;/code&gt; and you're calling with a service-role token, check the invocation log. The rows might not be there.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;— Mike Clarke, founder of Elevare Digital.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Your cron logged success every day for weeks. It approved nothing.</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Sun, 12 Jul 2026 00:10:11 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/your-cron-logged-success-every-day-for-weeks-it-approved-nothing-1llg</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/your-cron-logged-success-every-day-for-weeks-it-approved-nothing-1llg</guid>
      <description>&lt;p&gt;Your cron logged success every day for weeks. It approved nothing.&lt;/p&gt;




&lt;p&gt;We run ARIA, an autonomous content pipeline at Elevare Digital. Agents generate drafts, a daily approver cron reviews them, approved content moves to publishing. Fully automated, gated by a human-review layer, self-healing in most places.&lt;/p&gt;

&lt;p&gt;For several weeks, the approver cron ran on schedule, logged &lt;code&gt;{ status: 'success' }&lt;/code&gt;, and approved zero drafts. Nineteen drafts sat in the queue. Nobody was alerted. The system looked healthy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A cron that processes zero rows and a cron that processes zero rows &lt;em&gt;because there's nothing to process&lt;/em&gt; produce identical logs.&lt;/li&gt;
&lt;li&gt;PostgREST inner joins silently exclude rows when the join condition matches nothing — no error, no warning, just an empty result set.&lt;/li&gt;
&lt;li&gt;Producer/consumer type drift is invisible until you instrument the gap between "rows waiting" and "rows processed."&lt;/li&gt;
&lt;li&gt;Alert on &lt;code&gt;pending &amp;gt; 0 AND processed === 0&lt;/code&gt;, not just on thrown errors.&lt;/li&gt;
&lt;li&gt;Autonomous systems fail most dangerously in the space between errors and correctness.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What the system was doing
&lt;/h2&gt;

&lt;p&gt;ARIA's generator agents write content and store drafts as rows in a &lt;code&gt;content_opportunities&lt;/code&gt; table. Each row has an &lt;code&gt;opportunity_type&lt;/code&gt; — in practice, the generator was producing &lt;code&gt;'article'&lt;/code&gt; and &lt;code&gt;'listing'&lt;/code&gt; types.&lt;/p&gt;

&lt;p&gt;The approver cron queries that table via Supabase / PostgREST and processes any pending drafts it finds. Here's the shape of what it was doing:&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;// approver-cron/index.ts (Deno edge function)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opportunities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content_opportunities&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    id,
    title,
    status,
    content_threads!inner(
      id,
      body
    )
  `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending_review&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="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;opportunity_type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;thread&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- the problem&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Approver query failed&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;error&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;500&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Approver run complete. Processed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opportunities&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// ... process opportunities&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things combined to create the silent failure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;opportunity_type = 'thread'&lt;/code&gt; — the generator never wrote rows with this type. It wrote &lt;code&gt;'article'&lt;/code&gt; and &lt;code&gt;'listing'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;content_threads!inner(...)&lt;/code&gt; — PostgREST inner join syntax. If no matching rows exist in &lt;code&gt;content_threads&lt;/code&gt;, the parent rows are excluded entirely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result: &lt;code&gt;opportunities&lt;/code&gt; was always an empty array. No error was thrown. &lt;code&gt;opportunities.length&lt;/code&gt; logged as &lt;code&gt;0&lt;/code&gt;. The function returned &lt;code&gt;200 OK&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why this is the worst kind of failure
&lt;/h2&gt;

&lt;p&gt;Most failures announce themselves. A network timeout throws. A missing env var crashes on startup. A malformed query returns a PostgREST error object.&lt;/p&gt;

&lt;p&gt;This failure looked like rest.&lt;/p&gt;

&lt;p&gt;In a queue system, there's a legitimate state where the consumer runs and finds nothing to do — because the queue is empty. That's healthy-idle. It logs &lt;code&gt;Processed: 0&lt;/code&gt; and exits cleanly.&lt;/p&gt;

&lt;p&gt;Our broken state also logged &lt;code&gt;Processed: 0&lt;/code&gt; and exited cleanly.&lt;/p&gt;

&lt;p&gt;From the outside, from a log aggregator, from a dashboard: identical.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Healthy idle — queue is empty
Approver run complete. Processed: 0

// Broken — 19 drafts waiting, filter excludes all of them  
Approver run complete. Processed: 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only way to tell them apart is to check whether the queue &lt;em&gt;actually had pending rows&lt;/em&gt; when the consumer ran.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix: broaden scope, instrument the gap
&lt;/h2&gt;

&lt;p&gt;The immediate fix was to remove the incorrect type filter and fix the join. The approver should consume all pending drafts, not just ones of a type the generator never produces:&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;// Fixed: consume what the producer actually writes&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;opportunities&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content_opportunities&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`
    id,
    title,
    status,
    opportunity_type
  `&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending_review&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="c1"&gt;// No type filter. No inner join restricting to non-existent rows.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the more important fix was the alert. After each consumer run, we now check whether there are pending rows that weren't touched:&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;// After the approver processes its batch:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opportunities&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;stillPending&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;supabase&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;content_opportunities&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="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;id&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;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;head&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;eq&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;status&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pending_review&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="nx"&gt;processed&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stillPending&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Consumer ran, touched nothing, but the queue has rows.&lt;/span&gt;
  &lt;span class="c1"&gt;// This is the danger zone.&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;triggerAlert&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;consumer_drift&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Approver processed 0 rows but queue has &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stillPending&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; pending drafts.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;severity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;high&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="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Processed: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;. Still pending: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;stillPending&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This check doesn't care &lt;em&gt;why&lt;/em&gt; the consumer touched zero rows. It cares that the queue had work and the consumer didn't do it. The reason might be a bad filter, a broken join, a type mismatch, or something else we haven't thought of yet. The alert fires regardless and forces a human to look.&lt;/p&gt;

&lt;h2&gt;
  
  
  The underlying pattern: producer/consumer drift
&lt;/h2&gt;

&lt;p&gt;The generator and approver were written at different times. At some point, the generator's output types and the approver's filter drifted apart. Neither system knew about the other's contract. There was no shared schema validation between them, no test that asserted "the approver can actually see what the generator writes."&lt;/p&gt;

&lt;p&gt;This is a normal way systems decay. The producer evolves. The consumer doesn't. Or vice versa. In a synchronous API call, this kind of mismatch usually causes a visible failure. In a queue-based async pipeline, it just causes the queue to grow while the consumer reports success.&lt;/p&gt;

&lt;p&gt;Autonomous systems make this worse because there's no human in the loop watching the queue depth during normal operation. The whole point is that it runs without supervision. Which means the instrumentation &lt;em&gt;is&lt;/em&gt; the supervision.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we'd do differently
&lt;/h2&gt;

&lt;p&gt;A few things that would have caught this earlier:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Log scanned vs. processed separately.&lt;/strong&gt; If your consumer query returns zero rows, log that distinctly from "query returned rows and we processed them." &lt;code&gt;scanned: 0, processed: 0&lt;/code&gt; vs &lt;code&gt;scanned: 5, processed: 5&lt;/code&gt; are different states.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Track queue depth over time.&lt;/strong&gt; If pending rows are accumulating across multiple cron runs, that's a signal even without a consumer error. A simple check: if the queue depth at the end of a run is higher than at the start, something isn't working.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test the consumer against real producer output.&lt;/strong&gt; Not just "does the function run without throwing" but "given a row the producer actually writes, does the consumer find and process it."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Name your contracts.&lt;/strong&gt; If the producer writes &lt;code&gt;opportunity_type: 'article'&lt;/code&gt; and the consumer filters on &lt;code&gt;opportunity_type: 'thread'&lt;/code&gt;, that's a broken contract. Treat it like one — define it explicitly, validate it, test it.&lt;/p&gt;




&lt;p&gt;The honest summary: a filter we forgot about, combined with an inner join that excluded everything, turned our approver cron into a machine that ran on schedule, logged success, and accomplished nothing for weeks. The fix was straightforward. The lesson is that in autonomous pipelines, success-on-empty is a failure mode you have to design against explicitly, because the logs will never tell you on their own.&lt;/p&gt;

&lt;p&gt;Alert on the gap between what the queue holds and what the consumer touches. Not just on errors.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;— Mike Clarke, founder of Elevare Digital.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>coding</category>
      <category>webdev</category>
      <category>learning</category>
    </item>
    <item>
      <title>Smart Traffic Systems: Understanding and Configuring the Core Logic</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Wed, 01 Jul 2026 06:00:19 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/smart-traffic-systems-understanding-and-configuring-the-core-logic-8hp</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/smart-traffic-systems-understanding-and-configuring-the-core-logic-8hp</guid>
      <description>&lt;h3&gt;
  
  
  Stuck in Traffic? Let's Build a Smarter Way Forward.
&lt;/h3&gt;

&lt;p&gt;We've all been there: staring at a red light, no cross-traffic in sight, and wondering &lt;em&gt;why&lt;/em&gt; it's taking so long. Traditional, fixed-time traffic light systems are notoriously inefficient. They don't adapt to real-world conditions. But what if we could make them smarter? What if our traffic infrastructure could &lt;em&gt;think&lt;/em&gt;? &lt;/p&gt;

&lt;p&gt;That's where smart traffic systems come in. As developers, we have the power to build these adaptive, intelligent solutions that promise smoother commutes, reduced congestion, and lower emissions. But how do you &lt;em&gt;configure&lt;/em&gt; one?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Brains Behind the Green Light: What Drives a Smart Traffic System?
&lt;/h3&gt;

&lt;p&gt;At its heart, a smart traffic system is a dynamic control loop. It observes, analyzes, decides, and then acts. Here's a breakdown of the key components you'll be configuring:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Ingestion:&lt;/strong&gt; This is your system's eyes and ears. We're talking about real-time input from diverse sources: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Vehicle Sensors:&lt;/strong&gt; Inductive loops, radar, lidar, cameras – detecting vehicle presence, speed, and count.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pedestrian Sensors:&lt;/strong&gt; Infrared, pressure plates, computer vision for safe pedestrian crossings.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Environmental Data:&lt;/strong&gt; Time of day, weather conditions, historical traffic patterns.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Traffic Flow Analysis &amp;amp; Prediction:&lt;/strong&gt; Once you have the data, you need to make sense of it. This involves:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Queue Length Estimation:&lt;/strong&gt; How many cars are waiting at each intersection approach?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Travel Time Prediction:&lt;/strong&gt; How long will it take to clear the intersection?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Pattern Recognition:&lt;/strong&gt; Identifying recurring congestion points or anomalies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decision-Making Engine (The Optimization Algorithm):&lt;/strong&gt; This is the core intelligence. Based on the analyzed data, the algorithm determines the optimal light phasing and timing. Common approaches include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Reinforcement Learning:&lt;/strong&gt; The system learns optimal strategies through trial and error over time.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Fuzzy Logic:&lt;/strong&gt; Handling imprecise or uncertain data (e.g., 'heavy traffic').&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Heuristic Algorithms:&lt;/strong&gt; Rule-based systems designed to achieve specific goals (e.g., minimize wait time, maximize throughput).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actuation:&lt;/strong&gt; Finally, the system sends commands to the traffic light controllers to change their state (red, yellow, green) and duration.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Glimpse Under the Hood: Pseudocode for a Basic Adaptive System
&lt;/h3&gt;

&lt;p&gt;Let's consider a simplified scenario: a single intersection with four approaches. We want to minimize average wait time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FUNCTION ConfigureSmartTrafficSystem(intersectionID):
    // 1. Initialize Sensors &amp;amp; Data Streams
    sensors = GetSensorDataStreams(intersectionID)
    historical_data = LoadHistoricalTrafficPatterns(intersectionID)

    // 2. Define Optimization Goals (e.g., minimize average wait time, maximize throughput)
    optimization_goal = MINIMIZE_AVG_WAIT_TIME
    constraints = { MIN_GREEN_TIME: 10_seconds, MAX_GREEN_TIME: 60_seconds, YELLOW_TIME: 3_seconds }

    // 3. Main Control Loop
    LOOP indefinitely:
        current_vehicle_counts = ReadSensorData(sensors)
        current_pedestrian_counts = ReadPedestrianSensorData(sensors)
        current_time_of_day = GetCurrentTime()

        // 4. Analyze Traffic State
        queue_lengths = CalculateQueueLengths(current_vehicle_counts)
        demand_per_approach = EstimateDemand(current_vehicle_counts, historical_data, current_time_of_day)

        // 5. Decision-Making (using a simplified heuristic)
        next_phase_durations = DetermineOptimalPhaseDurations(
            queue_lengths,
            demand_per_approach,
            optimization_goal,
            constraints
        )

        // Example heuristic: Prioritize approach with most vehicles, but ensure fairness
        FUNCTION DetermineOptimalPhaseDurations(queues, demand, goal, constraints):
            priorities = CalculatePriorities(queues, demand) // e.g., higher queue means higher priority
            selected_phase = SelectPhaseBasedOnPriorities(priorities) 

            // Calculate green time for selected phase
            green_time = CalculateDynamicGreenTime(queues[selected_phase], constraints.MIN_GREEN_TIME, constraints.MAX_GREEN_TIME)

            RETURN { selected_phase: green_time, other_phases: constraints.YELLOW_TIME }

        // 6. Actuate Traffic Lights
        ApplyTrafficLightPhasing(intersectionID, next_phase_durations)

        WAIT(sum(next_phase_durations)) // Wait for the current cycle to complete
END FUNCTION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configuring this system involves fine-tuning your sensor interpretation, refining your prediction models, and, critically, selecting and optimizing your decision-making algorithm. Are you prioritizing throughput, fairness, emergency vehicle preemption, or perhaps a blend? Each choice impacts the system's behavior.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Practice Matters: Beyond the Pseudocode
&lt;/h3&gt;

&lt;p&gt;Understanding the concepts is one thing; making them work in a complex, dynamic environment is another. Real-world traffic data is noisy, sensors can fail, and unexpected events (accidents, sudden surges in traffic) will constantly challenge your system. You'll need to consider:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Robustness:&lt;/strong&gt; How does your system handle missing or erroneous data?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Scalability:&lt;/strong&gt; Can it manage hundreds or thousands of intersections?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Latency:&lt;/strong&gt; Can it react quickly enough to changing conditions?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ethical Considerations:&lt;/strong&gt; Are your algorithms fair across different districts or demographic areas?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This isn't just theory; it's about building resilient, impactful software. The best way to grasp these complexities is by doing. Simulating different scenarios, tweaking algorithms, and observing their outcomes is crucial.&lt;/p&gt;

&lt;p&gt;Practice this concept interactively on CodeCityApp — free trial at codecityapp.com&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on &lt;a href="https://codecityapp.com" rel="noopener noreferrer"&gt;CodeCityApp&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>coding</category>
      <category>programming</category>
      <category>beginners</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
