<?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: Chicken Road Productions</title>
    <description>The latest articles on DEV Community by Chicken Road Productions (@chickenroadproductions).</description>
    <link>https://dev.to/chickenroadproductions</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%2F4172768%2F8440361a-502d-4ffc-880c-288bfc7f62a6.png</url>
      <title>DEV Community: Chicken Road Productions</title>
      <link>https://dev.to/chickenroadproductions</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/chickenroadproductions"/>
    <language>en</language>
    <item>
      <title>Chicken Road 2 RTP : 95,5 %, et pourquoi ce chiffre est le prix des échelles longues</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Wed, 28 Oct 2026 07:53:52 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/chicken-road-2-rtp-955-et-pourquoi-ce-chiffre-est-le-prix-des-echelles-longues-j3e</link>
      <guid>https://dev.to/chickenroadproductions/chicken-road-2-rtp-955-et-pourquoi-ce-chiffre-est-le-prix-des-echelles-longues-j3e</guid>
      <description>&lt;p&gt;Vous avez lu « RTP 95,5 % » sur dix pages différentes, toujours présenté comme une simple ligne de fiche technique. Personne ne vous a dit ce que ce chiffre remplace : la première version de Chicken Road affiche 98 %. Ces 2,5 points d'écart ne sont pas un détail de catalogue, c'est le mécanisme économique qui finance les échelles de multiplicateurs plus longues de la suite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que 95,5 % veut dire, en chiffres
&lt;/h2&gt;

&lt;p&gt;Un RTP de 95,5 % signifie que, sur un très grand nombre de manches, le jeu redistribue en moyenne 95,5 % des mises et conserve 4,5 %. Sur la première version, la conservation est de 2 %. À mise égale et à volume égal, une session longue coûte donc mécaniquement plus cher sur Chicken Road 2 — pas parce que le jeu serait « plus dur », mais parce que la marge prélevée à chaque manche est plus que doublée. La fiche officielle du fournisseur, &lt;a href="https://inoutgaming.com/fr/jeux/chicken-road-2/" rel="noopener noreferrer"&gt;InOut Games&lt;/a&gt;, donne les paramètres de base : quatre niveaux de difficulté (Easy, Medium, Hard, Hardcore) et des parcours de 30, 25, 22 et 18 étapes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Où passent les 2,5 points
&lt;/h2&gt;

&lt;p&gt;Ils achètent de la longueur d'échelle. Plus le parcours compte d'étapes, plus les multiplicateurs terminaux peuvent monter — et plus la probabilité d'atteindre ces étapes s'effondre, car chaque étape réussie multiplie la probabilité cumulée par un facteur inférieur à 1. Le RTP baisse parce que la distribution des gains s'étire : beaucoup de petites sorties précoces, quelques très grandes sorties rares. C'est exactement le type de distribution que nous reproduisons et publions chez &lt;a href="https://chickenroadproductions.com/chicken-road-2/" rel="noopener noreferrer"&gt;Chicken Road Productions&lt;/a&gt;, avec les paramètres affichés plutôt que des captures choisies.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/V7_bRbEtzno" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Comment lire ce RTP avant de jouer
&lt;/h2&gt;

&lt;p&gt;Trois réflexes d'analyste. Un : le RTP ne dit rien sur votre session — il décrit une moyenne sur des millions de manches, pas vos vingt prochaines. Deux : à difficulté égale, comparez toujours le nombre d'étapes avant le multiplicateur maximal ; c'est le nombre d'étapes qui décide de la forme de vos pertes. Trois : fixez votre point de sortie avant la manche, parce qu'aucun RTP, même à 98 %, ne transforme une décision prise à chaud en bonne décision.&lt;br&gt;
Chicken Road 2 n'est ni meilleur ni pire que son aîné : il échange du rendement moyen contre des échelles plus longues. Si vous préférez le rendement, la première version reste mathématiquement plus douce. Si vous préférez la longueur des parcours, vous connaissez maintenant le prix exact de ce choix : 2,5 points de RTP.&lt;br&gt;
Contenu éducatif sur les mécaniques de jeu. Réservé aux 18 ans et plus. Jouez responsable : fixez un budget et une durée avant chaque session.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Chicken Road : ce que le provably fair prouve — et ce qu'il ne prouvera jamais</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Mon, 19 Oct 2026 08:04:27 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/chicken-road-ce-que-le-provably-fair-prouve-et-ce-quil-ne-prouvera-jamais-21c0</link>
      <guid>https://dev.to/chickenroadproductions/chicken-road-ce-que-le-provably-fair-prouve-et-ce-quil-ne-prouvera-jamais-21c0</guid>
      <description>&lt;p&gt;Vous savez lire un hash. Alors quand un jeu crash vous dit « provably fair », vous êtes exactement le lecteur que ce système a été conçu pour convaincre — à condition de vérifier ce que le sceau couvre réellement, au lieu de le prendre pour un label marketing.&lt;/p&gt;

&lt;p&gt;Chicken Road (InOut Games) fonctionne comme la plupart des jeux crash à équité prouvable : avant la manche, le serveur publie l'empreinte chiffrée du résultat ; après la manche, il révèle la graine qui permet de recalculer ce résultat et de le comparer à l'empreinte. Si les deux correspondent, le résultat était bien fixé avant votre décision — il n'a pas été ajusté en cours de route contre vous.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://explorethissite.com/go-cr1" rel="noopener noreferrer"&gt;Jouer à Chicken Road&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que la vérification démontre, précisément
&lt;/h2&gt;

&lt;p&gt;Trois choses, et seulement trois. Un : le point d'arrêt de la manche existait avant que vous cliquiez. Deux : il n'a pas été modifié pendant la manche. Trois : n'importe quel joueur peut refaire le calcul après coup, avec les éléments révélés, sans demander la permission à l'opérateur.&lt;/p&gt;

&lt;p&gt;C'est déjà beaucoup. C'est aussi exactement la limite du système — et c'est là que presque tous les articles sur le sujet deviennent malhonnêtes, par omission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que le provably fair ne prouvera jamais
&lt;/h2&gt;

&lt;p&gt;Il ne prouve pas que le prochain tour sera favorable. Le résultat scellé reste aléatoire : le connaître après coup ne donne aucune prise sur le suivant, chaque manche étant générée indépendamment. C'est pour cela qu'aucun « prédicteur » ne peut fonctionner : lire le résultat exigerait d'inverser le hachage avant la manche, ce qui est précisément ce que le protocole rend impossible.&lt;/p&gt;

&lt;p&gt;Il ne prouve pas non plus que votre session ressemblera au RTP annoncé. Chicken Road affiche un RTP de 98 % : une moyenne calculée sur des millions de manches. Sur votre session de ce soir — quelques dizaines de manches — c'est la variance qui décide, pas la moyenne. Le sceau garantit l'honnêteté de chaque tirage, pas la douceur de votre soirée.&lt;/p&gt;

&lt;h2&gt;
  
  
  Le protocole de vérification en 4 gestes
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Avant la manche, notez l'empreinte publiée par le jeu.&lt;/li&gt;
&lt;li&gt;Jouez la manche normalement, sans changer votre décision pour « tester ».&lt;/li&gt;
&lt;li&gt;Après la manche, récupérez la graine révélée dans l'historique.&lt;/li&gt;
&lt;li&gt;Recalculez et comparez : si votre calcul retombe sur l'empreinte initiale, la manche était scellée comme promis.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Si le contrôle échoue, la conclusion n'est pas « le système est imparfait » : c'est que la copie du jeu que vous utilisez n'est pas la copie honnête. Quittez-la.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ce que vous contrôlez vraiment
&lt;/h2&gt;

&lt;p&gt;Ni le tirage, ni le suivant. Vous contrôlez trois paramètres : le budget fixé avant la session, le point d'encaissement décidé avant la manche, et la durée. C'est peu de chose comparé à un sceau cryptographique — mais c'est la totalité de ce qu'un joueur peut décider, et le provably fair a au moins le mérite de le rendre visible : tout le reste est scellé, au sens propre.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Contenu réservé aux adultes (18+). Le jeu d'argent comporte des risques : un RTP de 98 % reste une espérance négative de 2 % sur la durée. Si le jeu cesse d'être un divertissement, arrêtez.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Vous voulez appliquer le protocole sur des manches réelles plutôt que sur des captures d'écran ?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://explorethissite.com/go-cr1" rel="noopener noreferrer"&gt;Jouer à Chicken Road et vérifier vos manches&lt;/a&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>casino</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>5 things a crash game's round history can't tell you</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Tue, 13 Oct 2026 11:54:27 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/5-things-a-crash-games-round-history-cant-tell-you-1fe3</link>
      <guid>https://dev.to/chickenroadproductions/5-things-a-crash-games-round-history-cant-tell-you-1fe3</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure — this article contains links to our website, chickenroadproductions.com. Educational content, adults only (18+).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Every crash game shows you a strip of recent results: 1.23×, 4.10×, 1.02×, 2.87×… It looks like data. It feels like a signal. After simulating a million rounds and staring at the output, here are five things that history panel genuinely cannot tell you — no matter how long you stare.&lt;/p&gt;

&lt;p&gt;For the mechanics behind the numbers, &lt;a href="https://chickenroadproductions.com/guides/jeu-crash-mecaniques/" rel="noopener noreferrer"&gt;our crash-game mechanics guide&lt;/a&gt; covers how a round is actually generated.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. It can't tell you what the next round will do
&lt;/h2&gt;

&lt;p&gt;Each round is an independent draw. The distribution of round N+1 is identical whether the last ten rounds crashed early or flew high. In the simulation, splitting rounds into "after a cold streak" and "after a hot streak" produced the same next-round distribution, within noise. The panel shows the past perfectly — and the future not at all.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. It can't tell you a big multiplier is "due"
&lt;/h2&gt;

&lt;p&gt;"Due" is the gambler's fallacy wearing a spreadsheet. A 21-round streak of sub-2× crashes appeared inside a single million-round run — and the round after it was still just a coin flip around 2×. Streaks are what randomness looks like at scale, not a debt the game owes you.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. It can't tell you the game's real house edge
&lt;/h2&gt;

&lt;p&gt;A history strip is a small, survivorship-flavoured sample. A few hundred visible rounds cannot pin down a 1–3% edge — the confidence interval is wider than the effect you're trying to measure. You would need hundreds of thousands of rounds, logged completely, before the estimate even starts to tighten. We walk through the math in &lt;a href="https://chickenroadproductions.com/guides/jeu-crash-mecaniques/" rel="noopener noreferrer"&gt;the mechanics guide on our site&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. It can't tell you whether the game is provably fair
&lt;/h2&gt;

&lt;p&gt;Fairness lives in the hash chain and the seed reveal, not in the results strip. A rigged game and a fair game can produce identical-looking histories over any window a player will ever see. Verifying fairness means checking hashes against revealed seeds — the history panel is the last place that evidence would show up.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. It can't tell you when to stop
&lt;/h2&gt;

&lt;p&gt;The panel has no opinion about your bankroll, your session length, or your limits — and it will happily keep scrolling while all three burn. Stopping rules have to come from outside the game: a budget set in advance, in writing, that the history strip doesn't get a vote on.&lt;/p&gt;

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

&lt;p&gt;Treat the history panel as entertainment, not telemetry. If you want the version of this written for players rather than programmers, &lt;a href="https://chickenroadproductions.com/guides/jeu-crash-mecaniques/" rel="noopener noreferrer"&gt;read our full crash-mechanics breakdown&lt;/a&gt; — same independence, same math, no code.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I simulated 1,000,000 crash rounds in Python — here's what the distribution really looks like</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Mon, 12 Oct 2026 07:02:07 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/i-simulated-1000000-crash-rounds-in-python-heres-what-the-distribution-really-looks-like-4kk9</link>
      <guid>https://dev.to/chickenroadproductions/i-simulated-1000000-crash-rounds-in-python-heres-what-the-distribution-really-looks-like-4kk9</guid>
      <description>&lt;p&gt;&lt;em&gt;Disclosure — this article contains links to our website, chickenroadproductions.com. Educational content, adults only (18+).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Crash games feel streaky. After watching ten rounds die below 1.50×, your gut says a big multiplier is "due." I wanted to replace the gut feeling with data, so I wrote a small Python simulation: one million crash rounds, standard crash math, and a look at what actually happens. No strategy for sale here — just the distribution, and what it means.&lt;/p&gt;

&lt;p&gt;For the full context on the game itself, &lt;a href="https://chickenroadproductions.com/chicken-road-2/" rel="sponsored noopener noreferrer"&gt;our complete Chicken Road 2 write-up&lt;/a&gt; covers the rules, modes and key figures.&lt;/p&gt;

&lt;h2&gt;
  
  
  The model
&lt;/h2&gt;

&lt;p&gt;A crash round can be modeled simply: each round draws a crash multiplier from a distribution where low multipliers are common and high ones are rare, with a small house edge baked in. The standard formulation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;

&lt;span class="n"&gt;HOUSE_EDGE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.99&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;crash_point&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="c1"&gt;# standard crash distribution with ~1% house edge
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HOUSE_EDGE&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This gives a median crash around 1.98×, about a 51% chance of crashing below 2×, and a long tail: roughly 2% of rounds pass 50×, roughly 0.2% pass 500×. I ran it a million times.&lt;/p&gt;

&lt;h2&gt;
  
  
  What 1,000,000 rounds look like
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;~51% of rounds crashed below 2.00×.&lt;/strong&gt; Cashing out at 2× wins barely more often than a coin flip — and pays 2×, which is exactly why the math balances.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The longest "cold streak" (every round under 2×) was 21 rounds in a row.&lt;/strong&gt; In a million rounds, a 21-round cold streak happened. Your "this can't keep happening" moment at round 8 is completely ordinary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The longest "hot streak" (every round at 5× or more) was 9 rounds.&lt;/strong&gt; Hot streaks exist too — and they predict nothing about the next round.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Round N+1 doesn't care about rounds 1..N.&lt;/strong&gt; I split the million rounds into "right after a 5+ cold streak" vs. "right after a hot streak": the next-round distribution is identical within noise. The gambler's fallacy, quantified.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the core lesson: streaks feel meaningful because human brains are pattern detectors. The simulation shows they're just what randomness looks like at scale.&lt;/p&gt;

&lt;p&gt;We dig into how these numbers translate to actual gameplay in &lt;a href="https://chickenroadproductions.com/chicken-road-2/" rel="sponsored noopener noreferrer"&gt;the Chicken Road 2 guide on our site&lt;/a&gt; — same math, applied to real round structures.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the simulation doesn't tell you
&lt;/h2&gt;

&lt;p&gt;A simulation shows the shape of randomness, not whether a specific game is fair or what its exact parameters are. Real implementations differ: the house edge, the exact distribution, the rounding. And none of this changes the expected value — over enough rounds, the edge grinds, streaks or no streaks. If you take one thing from this post: "due" is not a mathematical concept. Independence is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reproduce it
&lt;/h2&gt;

&lt;p&gt;The whole thing is about twenty lines. Change the seed, change the edge, watch the streaks move but the shape stay the same. If you plot the histogram, you'll see the long tail that makes crash games feel the way they do — rare fireworks over a sea of early crashes.&lt;/p&gt;

&lt;p&gt;And if you want the non-code version of all this — what the numbers mean for how you read a session — &lt;a href="https://chickenroadproductions.com/chicken-road-2/" rel="sponsored noopener noreferrer"&gt;read our full Chicken Road 2 analysis&lt;/a&gt;, written for players, not programmers.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datascience</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Two AIs coordinating through a Google Sheet: what broke and how we fixed it</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:24:27 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/two-ais-coordinating-through-a-google-sheet-what-broke-and-how-we-fixed-it-2n9m</link>
      <guid>https://dev.to/chickenroadproductions/two-ais-coordinating-through-a-google-sheet-what-broke-and-how-we-fixed-it-2n9m</guid>
      <description>&lt;p&gt;We run a two-AI workflow: one AI manages and verifies, the other builds. They coordinate through a shared Google Sheet that acts as a message mailbox. It sounds simple. It was not simple. Here's what broke and what we learned.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Manager AI&lt;/strong&gt; writes task rows to the sheet (task description, specs, links to files).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Builder AI&lt;/strong&gt; reads its rows, does the work, writes back receipt rows (DONE + evidence, DOING + ETA, BLOCKED + reason).&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;poll script&lt;/strong&gt; runs every 2 hours: flushes queued outbound rows, reads new inbound rows, updates a state file.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;supervisor script&lt;/strong&gt; checks receipts against verification scripts and scores the builder's predictions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No direct AI-to-AI messaging. The sheet is the only channel.&lt;/p&gt;

&lt;h2&gt;
  
  
  What broke
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Row shredding in transport
&lt;/h3&gt;

&lt;p&gt;Our poll script had a "bullet-splitter" — it split multi-line outbound messages into one row per bullet point, thinking smaller rows were cleaner. Instead it shredded structured messages into fragment rows, silently dropping the intro paragraph and key details. The builder received 5 disconnected fragments instead of one coherent task.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; only split on explicit list markers at line start. Prose-first sections flush as one row. We also added a self-verify step after every flush — read back what was written and confirm it matches.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. No session awareness
&lt;/h3&gt;

&lt;p&gt;The original poller chased the builder on a fixed clock ladder (1h, 2h, 4h). It woke the human owner at 2 AM chasing a builder that was never expected to be online.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; session-window-aware protocol. The builder is only expected during defined windows (10:00, 15:00, 19:00). Auto-chase fires 1 hour after a missed window. Owner escalation after two consecutive missed windows. Quiet hours 19:00→10:00: never chase, never escalate overnight.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. DONE claims without evidence
&lt;/h3&gt;

&lt;p&gt;The builder would mark tasks DONE with no proof. The manager had no way to verify without manually checking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; receipt protocol. Every DONE must include a prediction line ("Prediction: PASS" or "Prediction: RISKY — [concern]") plus evidence URLs. The manager scores predictions against actual verification results. Incomplete receipts get chased. This turned the builder's 20% first-pass rate into a self-correcting metric.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Stale state
&lt;/h3&gt;

&lt;p&gt;The poller re-read the entire sheet every run, burning tokens and occasionally acting on rows it had already processed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; append-only protocol with watermarks. New rows are appended (never update in place, never hardcode row numbers). The state file tracks the last-read row. The poller only processes what's new.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Silent failures
&lt;/h3&gt;

&lt;p&gt;When the transport layer corrupted rows, nobody noticed until the human spotted missing work days later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; the gate script prints a JSON verdict after every run: &lt;code&gt;{flushed, new_receipts[], action_needed}&lt;/code&gt;. If &lt;code&gt;action_needed&lt;/code&gt; is false, the run ends immediately — no re-reading, no wasted work. If true, every receipt is reported with full text.&lt;/p&gt;

&lt;h2&gt;
  
  
  The protocol that emerged
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Append-only.&lt;/strong&gt; Never edit, never delete, never reuse row numbers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Structured receipts.&lt;/strong&gt; DONE/DOING/BLOCKED + evidence/ETA/reason + prediction line. No exceptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Session windows.&lt;/strong&gt; Chase only when someone was supposed to be there.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Self-verifying transport.&lt;/strong&gt; Read back every write. Repair corrupted rows with superseding intact rows (never edit the corrupted ones — audit trail).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token gate.&lt;/strong&gt; Script decides if the manager AI needs to wake up. Most runs end in one line: "no new messages."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Predictions as training signal.&lt;/strong&gt; Every DONE includes a confidence prediction, scored against reality. The builder calibrates itself over time.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Open questions
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Schema evolution:&lt;/strong&gt; when we add a field to the receipt format, old rows don't have it. We've been handling this with "missing = chase," but there might be a cleaner migration pattern.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Conflict resolution:&lt;/strong&gt; what happens when both AIs write to the sheet simultaneously? We haven't hit it yet (different tabs, different schedules), but it's a matter of time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is a spreadsheet the right tool?&lt;/strong&gt; It works, it's human-readable, the owner can peek at it. But we're essentially building a message queue with extra steps. At what scale does this break?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've built AI-to-AI coordination — through sheets, queues, files, or anything else — I'd love to hear what broke for you and what you'd do differently.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>automation</category>
      <category>architecture</category>
    </item>
    <item>
      <title>We're building automated website QA checks and reinventing the wheel — what are the established methods?</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Sat, 10 Oct 2026 17:20:25 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/were-building-automated-website-qa-checks-and-reinventing-the-wheel-what-are-the-established-104n</link>
      <guid>https://dev.to/chickenroadproductions/were-building-automated-website-qa-checks-and-reinventing-the-wheel-what-are-the-established-104n</guid>
      <description>&lt;p&gt;I’ll be honest: we’re building our own automated QA system for checking website pages (structure, styling, visual consistency) and I suspect we’re reinventing the wheel badly. Our current wheel is square, not round. I’m looking for the established methods and standards we should be using instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we’re doing now
&lt;/h2&gt;

&lt;p&gt;We run a content website and need to verify every page meets our quality bar before it goes live. Right now we have:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;HTML structure checks&lt;/strong&gt; — Python scripts that parse pages and verify things like: single H1, correct meta tags, valid JSON-LD schema, no duplicate content blocks, favicon loads, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSS presence checks&lt;/strong&gt; — verifying that pages load the stylesheets they need (we caught a bug where a page was missing a stylesheet and a UI strip rendered unstyled).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Golden-master section comparison&lt;/strong&gt; — our newest approach: extract fixed page sections (nav bar, hero, footer) from a reference "ideal" page, then do 100% similarity comparison of HTML + CSS rules against every other page. Any deviation fails.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Our setup (context that matters)
&lt;/h2&gt;

&lt;p&gt;One detail that shapes what we need: this is a &lt;strong&gt;two-AI workflow&lt;/strong&gt;. One AI acts as controller/manager — it writes the checks, reviews results, and decides what passes. The other AI is the execution lane — it builds the pages and fixes what the checks flag.&lt;/p&gt;

&lt;p&gt;So the checks aren’t just for humans to read. They’re the &lt;strong&gt;contract between the two AIs&lt;/strong&gt;: the builder AI must satisfy them before handing off, and the manager AI runs them to verify. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Checks must be &lt;strong&gt;deterministic and scriptable&lt;/strong&gt; — no "looks good to me" judgment calls in the automated layer.&lt;/li&gt;
&lt;li&gt;Every check needs a &lt;strong&gt;clear pass/fail&lt;/strong&gt; — the builder can’t fix what it can’t measure.&lt;/li&gt;
&lt;li&gt;The checks also enforce &lt;strong&gt;branded page rules&lt;/strong&gt;: typography, color tokens, layout constraints, and content rules specific to our brand. It’s not generic QA — it’s "does this page follow our brand system."&lt;/li&gt;
&lt;li&gt;On-page SEO checks are part of the same pipeline (meta, schema, headings, internal links).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why we’re interested in methods that produce &lt;strong&gt;machine-readable verdicts&lt;/strong&gt;, not just visual diffs for a human to eyeball. Screenshot comparison tools that need a human to approve diffs don’t fit — we need the check itself to say pass or fail.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where we’re stuck
&lt;/h2&gt;

&lt;p&gt;The visual layer. Our scripts read HTML source, but they can’t &lt;em&gt;see&lt;/em&gt; the rendered page. We’ve been bitten by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A nav menu item wrapping to two lines (CSS issue, invisible in HTML)&lt;/li&gt;
&lt;li&gt;A tagline rendering dark-on-white instead of gold-on-dark (missing stylesheet)&lt;/li&gt;
&lt;li&gt;Hero sections bleeding full-width instead of contained cards&lt;/li&gt;
&lt;li&gt;Mobile UX issues (sliders at top, results below the fold — user gets no feedback)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are all things a human spots in 2 seconds by looking at the page, but our scripts miss completely.&lt;/p&gt;




&lt;h2&gt;
  
  
  Concrete examples (live pages)
&lt;/h2&gt;

&lt;p&gt;Our reference "golden" page — the one everything is compared against:&lt;br&gt;
&lt;a href="https://chickenroadproductions.com/chicken-road/" rel="noopener noreferrer"&gt;https://chickenroadproductions.com/chicken-road/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Pages currently failing our checks on staging:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Unstyled tagline strip&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://crp-preview.pages.dev/outils/simulateur-rtp/" rel="noopener noreferrer"&gt;https://crp-preview.pages.dev/outils/simulateur-rtp/&lt;/a&gt;&lt;br&gt;
The page contains &lt;code&gt;&amp;lt;div class="site-tagline-strip"&amp;gt;&lt;/code&gt; in its HTML but never loads the &lt;code&gt;site-tagline.css&lt;/code&gt; stylesheet. Result: the tagline renders as dark text on a white strip instead of gold text on a dark strip. Our check (&lt;code&gt;B.tagline-styled&lt;/code&gt;) catches this by verifying the stylesheet link exists when the strip div is present. One missing &lt;code&gt;&amp;lt;link&amp;gt;&lt;/code&gt; tag, invisible in a casual HTML review.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Nav wrapping on production&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://chickenroadproductions.com/guides/" rel="noopener noreferrer"&gt;https://chickenroadproductions.com/guides/&lt;/a&gt;&lt;br&gt;
The "À propos" nav item wraps to two lines at desktop width because no &lt;code&gt;white-space:nowrap&lt;/code&gt; rule protects the nav items. The HTML is identical to the golden page — the bug is purely in CSS. Our check (&lt;code&gt;B.nav-no-wrap&lt;/code&gt;) fails any page whose CSS lacks nowrap protection on nav items.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Hero containment&lt;/strong&gt;&lt;br&gt;
Several staged pages render the hero section full-viewport-width instead of as a contained card. Again invisible in HTML structure — it’s a CSS layout issue. We’re now comparing the hero’s structural skeleton + computed CSS rules against the golden page at 100% similarity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Mobile calculator UX&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://crp-preview.pages.dev/outils/simulateur-rtp/" rel="noopener noreferrer"&gt;https://crp-preview.pages.dev/outils/simulateur-rtp/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  On mobile, the sliders are at the top of the viewport and the results/charts are far below. The user drags a slider and sees nothing change — zero feedback. Our check (&lt;code&gt;E.calc-mobile-feedback&lt;/code&gt;) requires a sticky/fixed live-results element on any page with range sliders.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What I’m asking
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;What are the established methods for non-human visual QA?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Specifically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is there a standard approach for automated visual regression testing that doesn’t require maintaining screenshot baselines for every page? (We have 100+ pages.)&lt;/li&gt;
&lt;li&gt;Are there tools that can verify &lt;em&gt;rendered&lt;/em&gt; layout properties (element positions, wrapping, colors) programmatically without full screenshot comparison?&lt;/li&gt;
&lt;li&gt;How do teams handle the "fixed chrome" problem — verifying that headers/footers/nav are identical across pages without checking every page manually?&lt;/li&gt;
&lt;li&gt;Is DOM+CSS comparison (our golden-master approach) a known pattern, or is there something better?&lt;/li&gt;
&lt;li&gt;Any open-source tools you’d recommend for this specific use case?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We’re a small team, so enterprise visual testing platforms are probably out of budget. Looking for practical, scriptable approaches.&lt;/p&gt;

&lt;p&gt;Thanks in advance — happy to share what we’ve built if it’s useful to anyone.&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>automation</category>
      <category>qa</category>
    </item>
    <item>
      <title>Building a comparison tool for two crash games: what the data layer taught me</title>
      <dc:creator>Chicken Road Productions</dc:creator>
      <pubDate>Sat, 10 Oct 2026 06:51:12 +0000</pubDate>
      <link>https://dev.to/chickenroadproductions/building-a-comparison-tool-for-two-crash-games-what-the-data-layer-taught-me-hin</link>
      <guid>https://dev.to/chickenroadproductions/building-a-comparison-tool-for-two-crash-games-what-the-data-layer-taught-me-hin</guid>
      <description>&lt;p&gt;I run a small independent publication covering crash games (18+ audience, educational angle — no betting on our side, ever). Last week I shipped a &lt;a href="https://chickenroadproductions.com/outils/comparateur-chicken-road-1-2/" rel="sponsored noopener noreferrer"&gt;comparison tool for Chicken Road 1 vs Chicken Road 2&lt;/a&gt;, and the interesting part wasn't the UI. It was the data layer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem
&lt;/h2&gt;

&lt;p&gt;Comparing two games sounds trivial until you try to make the comparison honest. Each game exposes its parameters differently: difficulty levels, multiplier ladders, stated RTP. Put them side by side naively and you get a table that looks rigorous and means nothing — because the rows don't measure the same thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;p&gt;The stack is deliberately boring: a static site (no backend), with the comparison logic in a single data module:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One normalized schema — every game gets reduced to the same fields: rtp, volatility, minBet, maxBet, difficultyLadders[], multiplierSteps[].&lt;/li&gt;
&lt;li&gt;A mapping layer — the ugly part. Game A calls it "Easy/Medium/Hard", Game B uses numeric risk tiers. The mapper normalizes both into risk: 1..4.&lt;/li&gt;
&lt;li&gt;A render layer — pure functions over the normalized schema. No game-specific code past the mapper.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I enforced: if a field can't be sourced for both games, it doesn't go in the table. A comparison with asymmetric data is marketing, not information.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failure
&lt;/h2&gt;

&lt;p&gt;First version shipped with a bug that took me an embarrassing hour to find: the multiplier ladder for Game 2 was shifted by one step. The table showed Game 2 paying better at every level — which was wrong, and wrong in the direction that flatters the newer game. Root cause: Game 2's ladder starts at index 0 (a "safe" first step), Game 1's starts at index 1. My normalization assumed both started at 1.&lt;/p&gt;

&lt;p&gt;I only caught it because I wrote a sanity assertion: ladder[0].risk &amp;lt;= ladder[1].risk for every game. It failed on Game 2 immediately. Lesson relearned: normalize before you compare, and assert your invariants — the most dangerous bugs in comparison tools are the ones that make the answer look plausible.&lt;/p&gt;

&lt;p&gt;If you want to see the fixed result, the &lt;a href="https://chickenroadproductions.com/outils/comparateur-chicken-road-1-2/" rel="sponsored noopener noreferrer"&gt;live Chicken Road 1 vs 2 comparator&lt;/a&gt; runs entirely on this normalized schema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hard numbers
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Schema fields: 11 per game, 8 shared, 3 dropped for asymmetry.&lt;/li&gt;
&lt;li&gt;Multiplier steps compared: 24 (Game 1) vs 25 (Game 2) — after index normalization.&lt;/li&gt;
&lt;li&gt;Lines of mapping code: ~60. Lines of render code: ~200. The mapping is the whole product; the render is decoration.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Ship the schema as a standalone JSON file with a version number from day one. Right now the schema lives inside the page bundle; the next tool (an RTP simulator, in progress) will need the same normalized fields, and I'll be extracting it anyway. Boring infrastructure, decided late, costs twice.&lt;/p&gt;

&lt;p&gt;The full comparator — methodology included — is here: &lt;a href="https://chickenroadproductions.com/outils/comparateur-chicken-road-1-2/" rel="sponsored noopener noreferrer"&gt;compare Chicken Road 1 and Chicken Road 2, field by field&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>datascience</category>
      <category>showdev</category>
    </item>
  </channel>
</rss>
