<?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>Mastering the Flow: How to Configure a Smart Traffic System</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Wed, 16 Sep 2026 06:00:18 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/mastering-the-flow-how-to-configure-a-smart-traffic-system-321e</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/mastering-the-flow-how-to-configure-a-smart-traffic-system-321e</guid>
      <description>&lt;p&gt;Hey folks!&lt;/p&gt;

&lt;p&gt;Ever found yourself stuck in a gridlocked city, wishing someone had a better handle on the traffic lights? Or maybe you've worked on a distributed system and realized that managing resource contention is a lot like optimizing traffic flow. In either case, the problem is real: &lt;strong&gt;how to configure a smart traffic system&lt;/strong&gt; to keep things moving efficiently, prevent bottlenecks, and adapt to changing conditions.&lt;/p&gt;

&lt;p&gt;Traditional traffic light systems are often time-based, cycling through greens and reds without much regard for actual vehicle presence or density. Smart traffic systems, on the other hand, are dynamic. They leverage sensors, data analytics, and often AI/ML to make real-time decisions, optimizing flow, reducing emissions, and improving safety. For us developers, this isn't just about civic planning; it's a fascinating challenge in distributed systems, real-time data processing, and intelligent control.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Core Concept: Dynamic Optimization
&lt;/h3&gt;

&lt;p&gt;At its heart, configuring a smart traffic system involves creating an intelligent agent that can observe traffic conditions, predict future states, and issue commands to traffic signals. Think of it as a control loop:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Observe:&lt;/strong&gt; Gather data from sensors (e.g., inductive loops, cameras, lidar) at intersections, detecting vehicle presence, speed, and queue length.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Analyze &amp;amp; Predict:&lt;/strong&gt; Process this data. Are there long queues on one road? Is an emergency vehicle approaching? Use algorithms to predict future traffic density and potential congestion points.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Decide:&lt;/strong&gt; Based on analysis, determine the optimal signal timings for each phase at an intersection, or even coordinate multiple intersections. This might involve optimizing for minimal waiting time, maximum throughput, or priority for certain vehicles.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Act:&lt;/strong&gt; Send commands to the traffic light controllers to adjust their patterns.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This cycle repeats continuously, adapting to the ebb and flow of urban life. The 'smart' part comes from the complexity of the decision-making logic, often involving techniques like reinforcement learning, genetic algorithms, or even simple heuristic-based rules. The challenge is balancing conflicting demands – giving green to one direction often means red for another. It's a zero-sum game that needs careful arbitration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pseudocode: A Glimpse into the Brain
&lt;/h3&gt;

&lt;p&gt;Let's sketch out a very simplified conceptual model for a single intersection with four approaches (North, East, South, West):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;CLASS TrafficController:
    ATTRIBUTES:
        sensors: Map&amp;lt;Direction, List&amp;lt;SensorData&amp;gt;&amp;gt;  // Vehicle counts, speeds per lane
        signals: Map&amp;lt;Direction, TrafficSignal&amp;gt; // Green, Yellow, Red states
        current_phase: TrafficPhase // e.g., N-S Green, E-W Red
        phase_timer: Timer
        min_green_time: Integer
        max_green_time: Integer

    METHOD initialize():
        SET current_phase = default_phase
        START phase_timer with min_green_time

    METHOD update_traffic_data(new_sensor_data):
        sensors.update(new_sensor_data)

    METHOD decide_next_phase():
        IF phase_timer.elapsed() &amp;gt;= min_green_time THEN
            // Calculate priority scores for each potential next phase
            priority_scores = calculate_priority_for_all_phases(sensors.data)

            // Example: Prioritize phase with highest queue length
            next_phase_candidate = find_phase_with_highest_priority(priority_scores)

            IF next_phase_candidate != current_phase AND 
               phase_timer.elapsed() &amp;gt;= calculate_optimal_duration(current_phase, sensors.data) THEN
                INITIATE transition_to_phase(next_phase_candidate)
            ELSE IF phase_timer.elapsed() &amp;gt;= max_green_time THEN
                // Force transition if max green time reached
                INITIATE transition_to_phase(next_phase_candidate)
            END IF
        END IF

    METHOD transition_to_phase(new_phase):
        signals.set_yellow(current_phase) // Short yellow transition
        WAIT for yellow_duration
        signals.set_red(current_phase)
        signals.set_green(new_phase)
        SET current_phase = new_phase
        RESTART phase_timer

    METHOD calculate_priority_for_all_phases(data):
        // This is where the 'smart' algorithms go:
        // - Count vehicles waiting for each phase
        // - Consider emergency vehicle alerts
        // - Factor in pedestrian requests
        // - Apply weighted heuristics or ML model predictions
        RETURN map_of_phase_to_priority_score

    METHOD calculate_optimal_duration(current_phase, data):
        // Determine how long the current green should extend
        // - Based on current queue reduction
        // - Anticipated incoming traffic
        RETURN duration_in_seconds
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pseudocode scratches the surface. A real system would involve networking, fault tolerance, multi-intersection coordination, and a robust data pipeline. But it illustrates the core decision-making loop.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Practice Matters in Smart Systems
&lt;/h3&gt;

&lt;p&gt;Reading about smart traffic systems is one thing; actually designing and configuring one is another. The devil is in the details: sensor accuracy, latency in communication, the subtle tuning of algorithms to avoid oscillation (e.g., lights flipping too quickly), and handling edge cases like sensor failures or sudden spikes in traffic. You need to understand how your decisions impact the system's behavior, often in non-obvious ways.&lt;/p&gt;

&lt;p&gt;Experimenting with different configuration parameters, testing various algorithms, and seeing their impact on simulated traffic flow is crucial. It’s where you truly internalize the trade-offs and complexities involved in making a system &lt;em&gt;truly&lt;/em&gt; smart and resilient. This kind of hands-on experience builds intuition that no amount of theoretical reading can replace.&lt;/p&gt;

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

&lt;p&gt;The journey from concept to a functioning smart traffic system is filled with intriguing challenges. It's a perfect playground for applying your programming and problem-solving skills to real-world, impactful problems. Get your hands dirty, tweak some parameters, and see the digital cars flow!&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>Smart Traffic Configuration: Your Guide to Smarter City Systems</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Wed, 09 Sep 2026 06:00:17 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/smart-traffic-configuration-your-guide-to-smarter-city-systems-5eda</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/smart-traffic-configuration-your-guide-to-smarter-city-systems-5eda</guid>
      <description>&lt;h1&gt;
  
  
  Smart Traffic Configuration: Your Guide to Smarter City Systems
&lt;/h1&gt;

&lt;p&gt;Hey fellow developers,&lt;/p&gt;

&lt;p&gt;Ever found yourself stuck in a gridlock, wishing the traffic lights just &lt;em&gt;knew&lt;/em&gt; what was going on? Or maybe you've tried to navigate a city during peak hour and felt like the urban planning was stuck in the last century. That's not just a personal frustration; it's a massive, real-world problem that smart traffic systems aim to solve. And as developers, we're the ones building them.&lt;/p&gt;

&lt;p&gt;Today, we're diving into the nitty-gritty of &lt;strong&gt;how to configure the smart traffic system&lt;/strong&gt; – not just the theoretical 'what if', but the practical 'how-to' that moves us closer to dynamic, responsive urban infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Gridlock Problem: Why Smart Traffic Matters
&lt;/h2&gt;

&lt;p&gt;Traditional traffic systems are often static, time-based, or rely on simple loop detectors. They don't adapt well to unexpected events like accidents, sudden influxes of vehicles due to a concert, or even just daily fluctuations in commute patterns. The result? Wasted fuel, increased pollution, stressed commuters, and lost productivity.&lt;/p&gt;

&lt;p&gt;Smart traffic systems, on the other hand, leverage data, algorithms, and real-time communication to optimize traffic flow. They're about making decisions on the fly, reducing congestion, and improving safety. From intelligent signal timing to adaptive route guidance, these systems are the brains behind a smoother, greener city.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the Core Concepts: What Are We Configuring?
&lt;/h2&gt;

&lt;p&gt;At its heart, configuring a smart traffic system involves setting up rules, parameters, and algorithms that dictate how traffic lights, signs, and even autonomous vehicles interact. Here are the key components we typically configure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Sensor Data Ingestion:&lt;/strong&gt; This is the input. We're talking about data from cameras (vehicle counts, speed, occupancy), inductive loops, GPS trackers, and even mobile device data. The system needs to know &lt;em&gt;what's happening now&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Traffic Light Control Logic:&lt;/strong&gt; This is the brain. Instead of fixed timers, we implement dynamic algorithms. These might be based on machine learning models predicting congestion, optimization algorithms like greedy approaches, or even reinforcement learning agents that 'learn' optimal signal timings.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Prioritization Rules:&lt;/strong&gt; Think emergency vehicles, public transport, or high-volume thoroughfares. We configure priorities to ensure critical movements are facilitated.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Network-wide Optimization:&lt;/strong&gt; It's not just about one intersection; it's about optimizing flow across an entire urban grid. This involves coordinating signals between adjacent intersections to create 'green waves' and prevent blockages downstream.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Anomaly Detection &amp;amp; Response:&lt;/strong&gt; Configuration includes rules for identifying incidents (accidents, stalled vehicles) and triggering appropriate responses, such as adjusting signal timings, displaying alerts on variable message signs, or dispatching emergency services.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Simulation &amp;amp; Testing Parameters:&lt;/strong&gt; Before deploying anything live, we simulate various scenarios. Configuring these simulation environments and tuning parameters is crucial for validation.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Pseudocode: A Glimpse into Dynamic Signal Control
&lt;/h2&gt;

&lt;p&gt;Let's consider a simplified pseudocode example for dynamic signal timing at a single intersection based on vehicle counts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// System Configuration Variables
SET MIN_GREEN_TIME = 15 // seconds
SET MAX_GREEN_TIME = 60 // seconds
SET DETECTION_INTERVAL = 5 // seconds, how often sensors are read
SET CONGESTION_THRESHOLD = 20 // vehicles, for a lane

// Intersection State
DECLARE current_phase = 'NORTH_SOUTH'
DECLARE north_south_vehicle_count = 0
DECLARE east_west_vehicle_count = 0
DECLARE phase_timer = 0

// Main Loop for Traffic Light Controller
FUNCTION run_traffic_controller():
  WHILE TRUE:
    // Read sensor data
    north_south_vehicle_count = GET_SENSOR_DATA('NORTH_SOUTH_APPROACH')
    east_west_vehicle_count = GET_SENSOR_DATA('EAST_WEST_APPROACH')

    // Increment phase timer
    phase_timer = phase_timer + DETECTION_INTERVAL

    IF current_phase == 'NORTH_SOUTH':
      IF phase_timer &amp;gt;= MIN_GREEN_TIME:
        IF east_west_vehicle_count &amp;gt; CONGESTION_THRESHOLD AND north_south_vehicle_count &amp;lt; (east_west_vehicle_count / 2):
          // Switch to East-West if heavy congestion there and NS is light
          TRIGGER_YELLOW_LIGHT('NORTH_SOUTH')
          WAIT(3) // Yellow light duration
          current_phase = 'EAST_WEST'
          phase_timer = 0
        ELSE IF phase_timer &amp;gt;= MAX_GREEN_TIME:
          // Force switch if max green time reached
          TRIGGER_YELLOW_LIGHT('NORTH_SOUTH')
          WAIT(3)
          current_phase = 'EAST_WEST'
          phase_timer = 0
      END IF
      SET_LIGHTS('NORTH_SOUTH', 'GREEN')
      SET_LIGHTS('EAST_WEST', 'RED')

    ELSE IF current_phase == 'EAST_WEST':
      IF phase_timer &amp;gt;= MIN_GREEN_TIME:
        IF north_south_vehicle_count &amp;gt; CONGESTION_THRESHOLD AND east_west_vehicle_count &amp;lt; (north_south_vehicle_count / 2):
          // Switch to North-South
          TRIGGER_YELLOW_LIGHT('EAST_WEST')
          WAIT(3)
          current_phase = 'NORTH_SOUTH'
          phase_timer = 0
        ELSE IF phase_timer &amp;gt;= MAX_GREEN_TIME:
          // Force switch
          TRIGGER_YELLOW_LIGHT('EAST_WEST')
          WAIT(3)
          current_phase = 'NORTH_SOUTH'
          phase_timer = 0
      END IF
      SET_LIGHTS('EAST_WEST', 'GREEN')
      SET_LIGHTS('NORTH_SOUTH', 'RED')

    WAIT(DETECTION_INTERVAL)
  END WHILE
END FUNCTION
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This pseudocode illustrates a basic reactive system. Real-world systems are far more complex, incorporating predictive models, multi-intersection coordination, and robust error handling. But the fundamental idea is to use data to dynamically adjust parameters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Practice Configuration Matters
&lt;/h2&gt;

&lt;p&gt;Reading about &lt;code&gt;MIN_GREEN_TIME&lt;/code&gt; or &lt;code&gt;CONGESTION_THRESHOLD&lt;/code&gt; is one thing; actually seeing how changes to these values impact traffic flow is another. Misconfiguring a smart traffic system can have disastrous consequences – creating new bottlenecks, increasing accident risk, or even bringing a city to a standstill. That's why hands-on practice, experimentation in a safe environment, and understanding the nuances of parameter tuning are absolutely essential for any developer working in this domain.&lt;/p&gt;

&lt;p&gt;Experimenting with different algorithms, tweaking thresholds, and observing the system's response in various simulated scenarios builds intuition and expertise that no amount of theoretical reading can provide. It prepares you for the complex, ever-changing demands of urban environments.&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>Demystifying Smart Traffic: How to Configure an Intelligent System</title>
      <dc:creator>Mike Clarke</dc:creator>
      <pubDate>Wed, 02 Sep 2026 06:00:15 +0000</pubDate>
      <link>https://dev.to/mike_clarke_50a95013f5c59/demystifying-smart-traffic-how-to-configure-an-intelligent-system-blc</link>
      <guid>https://dev.to/mike_clarke_50a95013f5c59/demystifying-smart-traffic-how-to-configure-an-intelligent-system-blc</guid>
      <description>&lt;p&gt;Hey fellow devs!&lt;/p&gt;

&lt;p&gt;Ever found yourself stuck in a gridlock, wishing the traffic lights just &lt;em&gt;knew&lt;/em&gt; what was going on? That’s not just a pipe dream; it's the very problem we're solving with &lt;strong&gt;smart traffic systems&lt;/strong&gt;. Configuring one isn't about simply setting timers; it's about building an intelligent, adaptive network that responds to real-time conditions. This isn't just theory – it's a fascinating challenge that blends data science, IoT, and control systems.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Problem: Static Traffic Control is Obsolete
&lt;/h3&gt;

&lt;p&gt;Traditional traffic light systems operate on fixed cycles, often optimized for peak hours or historical averages. But what happens when there's an accident, a sudden surge in pedestrian traffic, or a special event? These static systems fail spectacularly, leading to congestion, increased pollution, and frustrated commuters. As cities grow, the inefficiency compounds, making a strong case for dynamic, intelligent solutions.&lt;/p&gt;

&lt;p&gt;Our mission, should we choose to accept it, is to build a system that can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Sense:&lt;/strong&gt; Gather real-time data from various sources.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Analyze:&lt;/strong&gt; Process that data to understand current traffic flow and predict future trends.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Adapt:&lt;/strong&gt; Dynamically adjust traffic signals and other controls to optimize flow.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Learn:&lt;/strong&gt; Improve its performance over time through machine learning.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The Concept: An Adaptive Ecosystem
&lt;/h3&gt;

&lt;p&gt;At its core, configuring a smart traffic system means orchestrating a symphony of sensors, data pipelines, decision-making algorithms, and actuators. Think of it like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Ingestion (The Eyes and Ears):&lt;/strong&gt; This is where real-time information streams in. We're talking about magnetic loop detectors embedded in the road, cameras performing vehicle counting and classification (potentially even recognizing emergency vehicles), GPS data from connected vehicles, pedestrian sensors, and even weather reports.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Data Processing &amp;amp; Fusion (The Brain's Cortex):&lt;/strong&gt; Raw sensor data is noisy and disparate. We need to clean, normalize, and combine it. This might involve spatial temporal analysis to understand traffic density across junctions, identifying queues, and calculating average speeds. Edge computing can play a vital role here, processing data closer to the source to reduce latency.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Decision-Making Engine (The Logic Core):&lt;/strong&gt; This is where the 'smart' truly happens. Algorithms, ranging from simple rule-based systems to complex Reinforcement Learning (RL) agents, take the processed data and decide the optimal course of action. Should the green light duration on Main Street be extended? Should a turn lane be prioritized? This engine aims to minimize overall travel time, reduce idle time, and manage congestion.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Actuation &amp;amp; Feedback (The Hands and Mouth):&lt;/strong&gt; Once a decision is made, commands are sent to the traffic signal controllers, variable message signs, or even integrated public transport systems. The system then continuously monitors the impact of its decisions, feeding new data back into the cycle for continuous improvement.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Glimpse into the Code: The Decision-Making Loop (Pseudocode)
&lt;/h3&gt;

&lt;p&gt;Let’s imagine a simplified decision engine for a single intersection. This pseudocode demonstrates the iterative nature of the system:&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():
    INITIALIZE trafficSignalControllers
    INITIALIZE dataAggregator
    INITIALIZE decisionEngine
    INITIALIZE learningModule

    WHILE systemIsRunning:
        // 1. Gather Real-time Data
        trafficData = dataAggregator.collectSensorData()

        // 2. Process and Analyze Data
        processedTrafficState = decisionEngine.analyzeTrafficState(trafficData)

        // 3. Make a Decision
        IF learningModule.isReadyForAdvancedDecision():
            // Use RL or predictive models for complex scenarios
            newSignalPlan = learningModule.predictOptimalSignalPlan(processedTrafficState)
        ELSE:
            // Fallback to rule-based or simpler algorithms
            newSignalPlan = decisionEngine.calculateRuleBasedSignalPlan(processedTrafficState)
        END IF

        // 4. Actuate Control
        trafficSignalControllers.applySignalPlan(newSignalPlan)

        // 5. Monitor and Learn (asynchronously or in background)
        performanceMetrics = trafficSignalControllers.getPerformanceMetrics()
        learningModule.updateModel(processedTrafficState, newSignalPlan, performanceMetrics)

        WAIT_FOR_NEXT_CYCLE_OR_EVENT()
    END WHILE
END FUNCTION

// Example of a dataAggregator method
CLASS DataAggregator:
    METHOD collectSensorData():
        sensorReadings = {}
        FOR EACH sensor IN listOfSensors:
            sensorReadings[sensor.id] = sensor.readData()
        END FOR
        RETURN sensorReadings
END CLASS

// Example of a simple rule-based decision engine method
CLASS DecisionEngine:
    METHOD calculateRuleBasedSignalPlan(state):
        IF state.majorRoadQueueLength &amp;gt; state.minorRoadQueueLength * 2:
            RETURN { 'majorRoadGreenDuration': 60, 'minorRoadGreenDuration': 15 }
        ELSE IF state.pedestrianCrossingsRequested:
            RETURN { 'pedestrianCrossWalkTime': 20, 'vehicleLightsRed': True }
        ELSE:
            RETURN { 'majorRoadGreenDuration': 30, 'minorRoadGreenDuration': 30 }
        END IF
    END METHOD
END CLASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This isn't a full solution, but it illustrates the logical flow: sense, process, decide, act, and learn. Each component can be a complex system in itself, using everything from Kafka streams for data ingestion to TensorFlow for deep reinforcement learning.&lt;/p&gt;

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

&lt;p&gt;Understanding these concepts abstractly is one thing; actually seeing how data flows, how algorithms react to changing conditions, and how different configurations impact system performance is another. Traffic simulation environments are invaluable here. They allow you to experiment with different sensor placements, test various decision algorithms (e.g., comparing fixed-time, actuated, and AI-driven approaches), and observe the emergent behavior of the system under diverse traffic patterns without deploying a single sensor in the real world.&lt;/p&gt;

&lt;p&gt;This hands-on approach helps you grasp the nuances of latency, data integrity, algorithm tuning, and the sheer complexity of optimizing for multiple, often conflicting, objectives (e.g., minimizing average wait time vs. ensuring emergency vehicle priority).&lt;/p&gt;

&lt;p&gt;Configuring a smart traffic system is a multidisciplinary engineering feat. It's challenging, but incredibly rewarding, as you're directly contributing to more efficient, sustainable, and livable cities.&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 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>
  </channel>
</rss>
