<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Ai-Example on Ocarina — manual</title>
    <link>https://mojo-molotov.github.io/from-ocarina-to-igor/series/ai-example/</link>
    <description>Recent content in Ai-Example on Ocarina — manual</description>
    <generator>Hugo</generator>
    <language>en-US</language>
    <lastBuildDate>Sat, 30 May 2026 15:59:37 +0200</lastBuildDate>
    <atom:link href="https://mojo-molotov.github.io/from-ocarina-to-igor/series/ai-example/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>08.01 — Code: 99% Claude, 1% Igor, intelligence: 50-50</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/01-ai-manifesto/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/01-ai-manifesto/</guid>
      <description>&lt;h1 id=&#34;0801code-99-claude-1-igor-intelligence-50-50&#34;&gt;08.01 — Code: 99% Claude, 1% Igor, intelligence: 50-50&lt;a class=&#34;anchor&#34; href=&#34;#0801code-99-claude-1-igor-intelligence-50-50&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;readme&#34;&gt;README&lt;a class=&#34;anchor&#34; href=&#34;#readme&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;h2 id=&#34;a-note-to-the-reader&#34;&gt;A note to the reader&lt;a class=&#34;anchor&#34; href=&#34;#a-note-to-the-reader&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;p&gt;This project is an experiment in AI-driven test engineering. In the interest of honesty about how it was built:&lt;/p&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;&lt;/th&gt;&#xA;          &lt;th&gt;Claude Code&lt;/th&gt;&#xA;          &lt;th&gt;Human&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Code written&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;99%&lt;/td&gt;&#xA;          &lt;td&gt;1%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Intelligence&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;50%&lt;/td&gt;&#xA;          &lt;td&gt;50%&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Almost every line was machine-written. The judgement behind it — what to test, what to distrust, when to dig and when to stop — was shared.&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.02 — CURA Healthcare (SUT)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/02-sut-cura/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/02-sut-cura/</guid>
      <description>&lt;h1 id=&#34;0802cura-healthcare-sut&#34;&gt;08.02 — CURA Healthcare (SUT)&lt;a class=&#34;anchor&#34; href=&#34;#0802cura-healthcare-sut&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;h2 id=&#34;ocarinas-first-victim&#34;&gt;Ocarina&amp;rsquo;s first victim&lt;a class=&#34;anchor&#34; href=&#34;#ocarinas-first-victim&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Hosting&lt;/strong&gt;: &lt;a href=&#34;https://katalon-demo-cura.herokuapp.com/&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://katalon-demo-cura.herokuapp.com/&lt;/a&gt;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Source&lt;/strong&gt;: &lt;a href=&#34;https://github.com/katalon-studio/katalon-demo-cura&#34; target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;https://github.com/katalon-studio/katalon-demo-cura&lt;/a&gt; (PHP, open source, MIT)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Host&lt;/strong&gt;: Heroku eco-dyno (sleeps after inactivity)&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Credentials&lt;/strong&gt;: &lt;code&gt;John Doe&lt;/code&gt; / &lt;code&gt;ThisIsNotAPassword&lt;/code&gt; (public, hardcoded on the login page)&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;perimeter&#34;&gt;Perimeter&lt;a class=&#34;anchor&#34; href=&#34;#perimeter&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Page&lt;/th&gt;&#xA;          &lt;th&gt;Function&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;/&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Marketing homepage&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;/profile.php#login&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Login&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;/appointment.php&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Booking form (facility, programs, hospital_readmission, visit_date, comment)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;/confirmation.php&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Booking confirmation&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;/history.php&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;The user&amp;rsquo;s booking history&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;/profile.php#profile&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Profile page (empty placeholder — gap §G-SPEC-2)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;why-target-cura&#34;&gt;Why target CURA&lt;a class=&#34;anchor&#34; href=&#34;#why-target-cura&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Open source&lt;/strong&gt; — we can read the PHP to understand why a given behavior exists.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Plenty of gaps&lt;/strong&gt; — rich territory for findings.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Public demo&lt;/strong&gt; — no authorization needed. Stable (maintained by Katalon).&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;There&amp;rsquo;s also a tacit reason: Katalon is a competing test-framework vendor (Katalon Studio). It&amp;rsquo;s poetic to test their demo app with Ocarina.&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.03 — Documentation</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/03-canonical-documents/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/03-canonical-documents/</guid>
      <description>&lt;h1 id=&#34;0803documentation&#34;&gt;08.03 — Documentation&lt;a class=&#34;anchor&#34; href=&#34;#0803documentation&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;The AI project is the most documented in the Ocarina ecosystem.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;listing&#34;&gt;Listing&lt;a class=&#34;anchor&#34; href=&#34;#listing&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Document&lt;/th&gt;&#xA;          &lt;th&gt;Status&lt;/th&gt;&#xA;          &lt;th&gt;Audience&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Claude ↔ project working contract&lt;/td&gt;&#xA;          &lt;td&gt;Claude + any contributor&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CURA_FRD.md&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Reconstructed spec&lt;/td&gt;&#xA;          &lt;td&gt;Maintainers, stakeholders&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;CURA_TEST_STRATEGY.md&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Test strategy&lt;/td&gt;&#xA;          &lt;td&gt;Maintainers&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;IDENTIFIED_GAPS.md&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Technical inventory of gaps&lt;/td&gt;&#xA;          &lt;td&gt;Maintainers&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;claudemd&#34;&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#claudemd&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Key documents&lt;/strong&gt;: points to &lt;code&gt;README.md&lt;/code&gt;, &lt;code&gt;CURA_FRD.md&lt;/code&gt;, &lt;code&gt;CURA_TEST_STRATEGY.md&lt;/code&gt;, &lt;code&gt;IDENTIFIED_GAPS.md&lt;/code&gt;, &lt;code&gt;CLAUDE.local.md&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Project&lt;/strong&gt;: 2 sentences on the project (CURA, Python 3.14+, demo creds).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Project philosophy&lt;/strong&gt;: &amp;ldquo;&lt;em&gt;No tricks or hacks. Teach the pattern, not the symptom.&lt;/em&gt;&amp;rdquo;&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;&lt;code&gt;CLAUDE.local.md&lt;/code&gt; template&lt;/strong&gt;: what the gitignored file should contain.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Layout&lt;/strong&gt;: full &lt;code&gt;src/&lt;/code&gt; tree, audited (shell command that verifies &lt;code&gt;CLAUDE.md&lt;/code&gt; and the real tree stay in sync).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Ocarina hierarchy&lt;/strong&gt;: reminder &lt;code&gt;Test → Suite → Campaign → Cycle&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Test strategy&lt;/strong&gt;: cycle structure, smoke gate, taxonomy.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Running tests&lt;/strong&gt;: CLI commands.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Reports and screenshots&lt;/strong&gt;: &amp;ldquo;one screenshot per drive_page&amp;rdquo; rule.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Scenario fragments&lt;/strong&gt;: when to extract a fragment.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Data-driven tests&lt;/strong&gt;: when to use the pattern, naming conventions.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Shared mixins&lt;/strong&gt;: &lt;code&gt;SeleniumBackAndForwardNavigationMixin&lt;/code&gt;.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Conventions&lt;/strong&gt;: 12 concrete rules (TYPE_CHECKING guard, log factories, etc.).&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Hard-won rules&lt;/strong&gt;: rules drawn from experience, with past examples.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;hard-won-rules&#34;&gt;Hard-won rules&lt;a class=&#34;anchor&#34; href=&#34;#hard-won-rules&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;verify-sut-behaviourdon&#34;&gt;&amp;ldquo;&lt;em&gt;Verify SUT behaviour — don&amp;rsquo;t theorise&lt;/em&gt;&amp;rdquo;&lt;a class=&#34;anchor&#34; href=&#34;#verify-sut-behaviourdon&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;CURA is open source. Before building on a server-side claim, read the PHP:&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.04 — Test strategy</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/04-test-strategy/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/04-test-strategy/</guid>
      <description>&lt;h1 id=&#34;0804test-strategy&#34;&gt;08.04 — Test strategy&lt;a class=&#34;anchor&#34; href=&#34;#0804test-strategy&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Six &lt;strong&gt;test types&lt;/strong&gt;, six distinct roles. Documented in &lt;code&gt;CURA_TEST_STRATEGY.md&lt;/code&gt; §3.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;test-types&#34;&gt;Test types&lt;a class=&#34;anchor&#34; href=&#34;#test-types&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Type&lt;/th&gt;&#xA;          &lt;th&gt;Expected status&lt;/th&gt;&#xA;          &lt;th&gt;Authoritative source&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Happy path&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PASS&lt;/td&gt;&#xA;          &lt;td&gt;Nominal flows described from the FRDs&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Unhappy path&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PASS (the test passes when the app correctly refuses)&lt;/td&gt;&#xA;          &lt;td&gt;Existing validations work&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Edge case / boundary&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PASS or FAIL&lt;/td&gt;&#xA;          &lt;td&gt;Depending on the test, often accompanied by a note added to the FRDs&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Business logic vulnerability / gap test&lt;/strong&gt; (functional tests trying to &amp;ldquo;break&amp;rdquo; the product and identify gaps)&lt;/td&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Intentional FAIL&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Behaviors a system should prevent but CURA doesn&amp;rsquo;t&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Exploratory / observed-behaviour&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PASS&lt;/td&gt;&#xA;          &lt;td&gt;Documents current behavior when the spec doesn&amp;rsquo;t cover it&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;strong&gt;Permanent security regression fixture&lt;/strong&gt;&lt;/td&gt;&#xA;          &lt;td&gt;PASS (= security holds)&lt;/td&gt;&#xA;          &lt;td&gt;Verifies a security fix holds (fixes that stay permanently in the test heritage)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;happy-path&#34;&gt;Happy path&lt;a class=&#34;anchor&#34; href=&#34;#happy-path&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Exercises the nominal flow with valid inputs and an authenticated user. Verifies that the system produces the correct output (confirmation, history entry, etc.).&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.05 — Security gaps (G-SEC-1 to G-SEC-3)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/05-security-gaps/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/05-security-gaps/</guid>
      <description>&lt;h1 id=&#34;0805security-gaps-g-sec-1-to-g-sec-3&#34;&gt;08.05 — Security gaps (G-SEC-1 to G-SEC-3)&lt;a class=&#34;anchor&#34; href=&#34;#0805security-gaps-g-sec-1-to-g-sec-3&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Three documented security gaps. Not actively exploited (cf. the rule &amp;ldquo;&lt;em&gt;security testing is functional and static, never active&lt;/em&gt;&amp;rdquo;), but observed via functional tests.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;g-sec-1no-csrf-token-on-the-front-end-forms&#34;&gt;G-SEC-1 — No CSRF token on the front-end forms&lt;a class=&#34;anchor&#34; href=&#34;#g-sec-1no-csrf-token-on-the-front-end-forms&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;symptom&#34;&gt;Symptom&lt;a class=&#34;anchor&#34; href=&#34;#symptom&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;The &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; rendered on Heroku &lt;strong&gt;does not contain&lt;/strong&gt; a CSRF token.&lt;/p&gt;&#xA;&lt;h3 id=&#34;source&#34;&gt;Source&lt;a class=&#34;anchor&#34; href=&#34;#source&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;views/page_appointment.php&lt;/code&gt; in the GitHub repo &lt;em&gt;calls&lt;/em&gt; &lt;code&gt;$antiCSRF-&amp;gt;insertHiddenToken()&lt;/code&gt; before the submit button. But on the &lt;strong&gt;deployed&lt;/strong&gt; version, this token does &lt;strong&gt;not&lt;/strong&gt; appear in the HTML.&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.06 — Data gaps (G-DATA-1, G-DATA-2)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/06-data-gaps/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/06-data-gaps/</guid>
      <description>&lt;h1 id=&#34;0806data-gaps-g-data-1-g-data-2&#34;&gt;08.06 — Data gaps (G-DATA-1, G-DATA-2)&lt;a class=&#34;anchor&#34; href=&#34;#0806data-gaps-g-data-1-g-data-2&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Two major gaps on data integrity.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;g-data-1no-server-side-validation-of-visit_date&#34;&gt;G-DATA-1 — No server-side validation of &lt;code&gt;visit_date&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#g-data-1no-server-side-validation-of-visit_date&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;source&#34;&gt;Source&lt;a class=&#34;anchor&#34; href=&#34;#source&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;// appointment.php&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;array_diff_key(&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$_POST&lt;/span&gt;, &lt;span style=&#34;color:#ff79c6&#34;&gt;array&lt;/span&gt;(&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;facility&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;hospital_readmission&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;programs&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;visit_date&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;,&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;comment&amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#ff79c6&#34;&gt;=&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This checks for key presence only. It &lt;strong&gt;never&lt;/strong&gt; validates the value of &lt;code&gt;visit_date&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;h3 id=&#34;symptom&#34;&gt;Symptom&lt;a class=&#34;anchor&#34; href=&#34;#symptom&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;strong&gt;Empty visit_date&lt;/strong&gt;: if you strip the HTML5 &lt;code&gt;required&lt;/code&gt; attribute via JS and submit with an empty date → booking accepted.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Past visit_date&lt;/strong&gt;: if you send a backdated booking → booking accepted.&lt;/li&gt;&#xA;&lt;li&gt;The booking is added to history as-is.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h3 id=&#34;related-tests&#34;&gt;Related tests&lt;a class=&#34;anchor&#34; href=&#34;#related-tests&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Test&lt;/th&gt;&#xA;          &lt;th&gt;Method&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Appointment - Server accepts empty date when client bypass applied&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Strip the &lt;code&gt;required&lt;/code&gt; attribute, send with empty booking date → expects rejection by the system (FAIL)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;&lt;code&gt;Appointment - Past date booking accepted&lt;/code&gt;&lt;/td&gt;&#xA;          &lt;td&gt;Send a booking request for yesterday → expects rejection by the system (FAIL)&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;p&gt;Both tests &lt;strong&gt;fail intentionally&lt;/strong&gt; — they document the gap.&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.07 — Spec gaps (G-SPEC-1 to G-SPEC-3)</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/07-spec-gaps/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/07-spec-gaps/</guid>
      <description>&lt;h1 id=&#34;0807spec-gaps-g-spec-1-to-g-spec-3&#34;&gt;08.07 — Spec gaps (G-SPEC-1 to G-SPEC-3)&lt;a class=&#34;anchor&#34; href=&#34;#0807spec-gaps-g-spec-1-to-g-spec-3&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Three gaps on business behavior vs the &lt;em&gt;expected&lt;/em&gt; spec of a healthcare system.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;g-spec-1history-sorted-by-submission-order-not-by-date&#34;&gt;G-SPEC-1 — History sorted by submission order, not by date&lt;a class=&#34;anchor&#34; href=&#34;#g-spec-1history-sorted-by-submission-order-not-by-date&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;source&#34;&gt;Source&lt;a class=&#34;anchor&#34; href=&#34;#source&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-php&#34; data-lang=&#34;php&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#6272a4&#34;&gt;// views/page_history.php&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;foreach&lt;/span&gt; (&lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$_SESSION&lt;/span&gt;[&lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#39;history&amp;#39;&lt;/span&gt;] &lt;span style=&#34;color:#ff79c6&#34;&gt;as&lt;/span&gt; &lt;span style=&#34;color:#8be9fd;font-style:italic&#34;&gt;$appointment&lt;/span&gt;) {&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#6272a4&#34;&gt;// ... render ...&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;No &lt;code&gt;usort&lt;/code&gt;. No sort. No sort step exists anywhere.&lt;/p&gt;&#xA;&lt;p&gt;&lt;code&gt;appointment.php&lt;/code&gt; pushes with &lt;code&gt;array_push($_SESSION[&#39;history&#39;], $_POST)&lt;/code&gt; — append to the end (it&amp;rsquo;s a &lt;em&gt;pushback&lt;/em&gt;).&lt;/p&gt;&#xA;&lt;h3 id=&#34;unmet-spec&#34;&gt;Unmet spec&lt;a class=&#34;anchor&#34; href=&#34;#unmet-spec&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;REQ-HIST-3&lt;/code&gt; from the FRD:&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.08 — Chrome BFCache (B-BROWSER-1) &#43; environment artifacts</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/08-bfcache/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/08-bfcache/</guid>
      <description>&lt;h1 id=&#34;0808chrome-bfcache-b-browser-1--environment-artifacts&#34;&gt;08.08 — Chrome BFCache (B-BROWSER-1) + environment artifacts&lt;a class=&#34;anchor&#34; href=&#34;#0808chrome-bfcache-b-browser-1--environment-artifacts&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Chrome restores a &lt;code&gt;no-store&lt;/code&gt; page after logout, from the BFCache, even though it&amp;rsquo;s a page protected by authentication.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;b-browser-1chromes-bfcache-is-a-pain&#34;&gt;B-BROWSER-1 — Chrome&amp;rsquo;s BFCache is a pain&lt;a class=&#34;anchor&#34; href=&#34;#b-browser-1chromes-bfcache-is-a-pain&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;symptom&#34;&gt;Symptom&lt;a class=&#34;anchor&#34; href=&#34;#symptom&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;After logout, clicking the back button to be redirected to a page that was protected by authentication (e.g. &lt;code&gt;history.php&lt;/code&gt;):&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;&lt;strong&gt;Chrome&lt;/strong&gt;: the page &lt;em&gt;stays displayed&lt;/em&gt; with its contents as shown to a still-authenticated user. The URL remains &lt;code&gt;history.php&lt;/code&gt;, and the server isn&amp;rsquo;t re-contacted to validate or invalidate access.&lt;/li&gt;&#xA;&lt;li&gt;&lt;strong&gt;Firefox&lt;/strong&gt;: a new request goes to the server, which sees the session is dead and redirects.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h3 id=&#34;very-annoying-gap&#34;&gt;Very annoying gap&lt;a class=&#34;anchor&#34; href=&#34;#very-annoying-gap&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;p&gt;&lt;code&gt;history.php&lt;/code&gt; correctly sends:&lt;/p&gt;</description>
    </item>
    <item>
      <title>08.09 — CI: ai_proof_ci.yml &#43; ai_proof_e2e.yml</title>
      <link>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/09-ci-matrix/</link>
      <pubDate>Wed, 20 May 2026 00:00:00 +0000</pubDate>
      <guid>https://mojo-molotov.github.io/from-ocarina-to-igor/08-ai-example/09-ci-matrix/</guid>
      <description>&lt;h1 id=&#34;0809ci-ai_proof_ciyml--ai_proof_e2eyml&#34;&gt;08.09 — CI: &lt;code&gt;ai_proof_ci.yml&lt;/code&gt; + &lt;code&gt;ai_proof_e2e.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#0809ci-ai_proof_ciyml--ai_proof_e2eyml&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h1&gt;&#xA;&lt;blockquote class=&#39;book-hint &#39;&gt;&#xA;&lt;p&gt;Two workflows. Fast PR (lint+typecheck). Manual e2e with a &lt;strong&gt;Firefox/Chrome matrix&lt;/strong&gt; + Heroku warm-up + ChromeDriver stacktrace filtering.&lt;/p&gt;&#xA;&lt;/blockquote&gt;&lt;h2 id=&#34;ai_proof_ciyml&#34;&gt;&lt;code&gt;ai_proof_ci.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ai_proof_ciyml&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: ai_proof CI&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;on&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;push&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;branches&lt;/span&gt;: [main]&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;pull_request&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;workflow_dispatch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;defaults&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;shell&lt;/span&gt;: bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;jobs&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;lint-and-typecheck&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;runs-on&lt;/span&gt;: ubuntu-latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;steps&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/checkout@v4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/setup-python@v5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;with&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;python-version&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;3.14&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;allow-prereleases&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/cache@v4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;with&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;path&lt;/span&gt;: .venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;key&lt;/span&gt;: venv-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles(&amp;#39;pyproject.toml&amp;#39;) }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Install dependencies&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: steps.cache-venv.outputs.cache-hit != &amp;#39;true&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          python -m venv .venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          .venv/bin/pip install . ruff mypy mypy-extensions typing-extensions pre-commit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Ruff format check&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: .venv/bin/ruff format src/ --check&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Ruff lint&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: .venv/bin/ruff check src/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Mypy&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: .venv/bin/mypy src/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;&#xA;  &lt;thead&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;th&gt;Step&lt;/th&gt;&#xA;          &lt;th&gt;Cmd&lt;/th&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/thead&gt;&#xA;  &lt;tbody&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Ruff format check&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ruff format src/ --check&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Ruff lint&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;ruff check src/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;      &lt;tr&gt;&#xA;          &lt;td&gt;Mypy&lt;/td&gt;&#xA;          &lt;td&gt;&lt;code&gt;mypy src/&lt;/code&gt;&lt;/td&gt;&#xA;      &lt;/tr&gt;&#xA;  &lt;/tbody&gt;&#xA;&lt;/table&gt;&#xA;&lt;h2 id=&#34;ai_proof_e2eyml&#34;&gt;&lt;code&gt;ai_proof_e2e.yml&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#ai_proof_e2eyml&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: ai_proof e2e&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;on&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;workflow_dispatch&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;defaults&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;shell&lt;/span&gt;: bash&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;jobs&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;e2e&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: e2e (${{ matrix.browser }})&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;runs-on&lt;/span&gt;: ubuntu-latest&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;env&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;FORCE_JAVASCRIPT_ACTIONS_TO_NODE24&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;WAIT_TIMEOUT&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;15&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;strategy&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;fail-fast&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#ff79c6&#34;&gt;matrix&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;include&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#ff79c6&#34;&gt;browser&lt;/span&gt;: firefox&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;driver&lt;/span&gt;: geckodriver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          - &lt;span style=&#34;color:#ff79c6&#34;&gt;browser&lt;/span&gt;: chrome&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#ff79c6&#34;&gt;driver&lt;/span&gt;: chromedriver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;steps&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/checkout@v4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/setup-python@v5&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;with&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;python-version&lt;/span&gt;: &lt;span style=&#34;color:#f1fa8c&#34;&gt;&amp;#34;3.14&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;allow-prereleases&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/cache@v4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;with&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;path&lt;/span&gt;: .venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;key&lt;/span&gt;: venv-${{ runner.os }}-py${{ steps.setup-python.outputs.python-version }}-${{ hashFiles(&amp;#39;pyproject.toml&amp;#39;) }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Install dependencies&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: steps.cache-venv.outputs.cache-hit != &amp;#39;true&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          python -m venv .venv&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          .venv/bin/pip install . ruff mypy mypy-extensions typing-extensions pre-commit&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#6272a4&#34;&gt;# --- Firefox ---&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Install geckodriver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: matrix.browser == &amp;#39;firefox&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          GECKO_VERSION=0.36.0&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          wget -q https://github.com/mozilla/geckodriver/releases/download/v$GECKO_VERSION/geckodriver-v$GECKO_VERSION-linux64.tar.gz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          tar -xzf geckodriver-v$GECKO_VERSION-linux64.tar.gz&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          chmod +x geckodriver&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Setup Firefox&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: matrix.browser == &amp;#39;firefox&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: browser-actions/setup-firefox@fcf821c621167805dd63a29662bd7cb5676c81a8&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#6272a4&#34;&gt;# --- Chrome ---&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Install chromedriver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: matrix.browser == &amp;#39;chrome&amp;#39;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          CHROME_VER=$(google-chrome-stable --version | grep -oP &amp;#39;\d+\.\d+\.\d+\.\d+&amp;#39;)&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          wget -q -O chromedriver.zip \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            &amp;#34;https://storage.googleapis.com/chrome-for-testing-public/${CHROME_VER}/linux64/chromedriver-linux64.zip&amp;#34;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          unzip -q chromedriver.zip&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          mv chromedriver-linux64/chromedriver ./chromedriver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          chmod +x ./chromedriver&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#6272a4&#34;&gt;# --- Warm-up ---&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Warm up Heroku dyno&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # CURA runs on a Heroku dyno that sleeps after inactivity.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # Tests hitting a cold dyno produce timeouts (false fails) and slow&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # confirmations (false passes on gap tests). Wake it explicitly&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # before any browser opens.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          curl -sf --retry 6 --retry-delay 5 --retry-all-errors \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            --max-time 30 https://katalon-demo-cura.herokuapp.com/ &amp;gt; /dev/null&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          echo &amp;#34;Dyno is warm&amp;#34;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#6272a4&#34;&gt;# --- Run ---&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Run e2e cycle&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # ChromeDriver release builds ship without debug symbols. On errors it&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # dumps raw C++ stack frames (&amp;#34;#N 0x&amp;lt;addr&amp;gt; &amp;lt;unknown&amp;gt;&amp;#34;) into the WebDriver&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # response, which Selenium surfaces as the exception message — polluting&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # both the live log and the pretty-print summary with unreadable hex noise.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # No Python-side fix exists. Strip the frames with sed before they land.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          # pipefail preserves Python&amp;#39;s exit code through the pipe.&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          set -o pipefail&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          .venv/bin/python -u src/main.py \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            --driver-path ./${{ matrix.driver }} \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            --browser ${{ matrix.browser }} \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            --workers 3 \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            --wait-timeout ${{ env.WAIT_TIMEOUT }} \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            --logger terminal+file \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;            2&amp;gt;&amp;amp;1 | sed -u \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;              -e &amp;#39;/Stacktrace:/{N;/\n#[0-9][0-9]* 0x[0-9a-f][0-9a-f]* &amp;lt;unknown&amp;gt;/{s/Stacktrace:\n[^\n]*/HIDDEN/;b};P;D}&amp;#39; \&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;              -e &amp;#39;/^#[0-9][0-9]* 0x[0-9a-f][0-9a-f]* &amp;lt;unknown&amp;gt;/d&amp;#39;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Enumerate traces&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: always()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;run&lt;/span&gt;: |&lt;span style=&#34;color:#f1fa8c&#34;&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          sync&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          find $GITHUB_WORKSPACE/.screenshots -type f 2&amp;gt;/dev/null || true&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          find $GITHUB_WORKSPACE/.ocarina_logs -type f 2&amp;gt;/dev/null || true&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#f1fa8c&#34;&gt;          find $GITHUB_WORKSPACE/.reports -type f 2&amp;gt;/dev/null || true&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: Upload screenshots&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;if&lt;/span&gt;: always()&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;uses&lt;/span&gt;: actions/upload-artifact@v4&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#ff79c6&#34;&gt;with&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;name&lt;/span&gt;: screenshots-${{ matrix.browser }}&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;path&lt;/span&gt;: .screenshots/&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#ff79c6&#34;&gt;retention-days&lt;/span&gt;: &lt;span style=&#34;color:#bd93f9&#34;&gt;7&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#6272a4&#34;&gt;# … upload logs, reports similarly ...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id=&#34;analysis&#34;&gt;Analysis&lt;a class=&#34;anchor&#34; href=&#34;#analysis&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h2&gt;&#xA;&lt;h3 id=&#34;fail-fast-false&#34;&gt;&lt;code&gt;fail-fast: false&lt;/code&gt;&lt;a class=&#34;anchor&#34; href=&#34;#fail-fast-false&#34; data-pagefind-ignore=&#34;all&#34;&gt;#&lt;/a&gt;&lt;/h3&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-yaml&#34; data-lang=&#34;yaml&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#ff79c6&#34;&gt;strategy&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;fail-fast&lt;/span&gt;: &lt;span style=&#34;color:#ff79c6&#34;&gt;false&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#ff79c6&#34;&gt;matrix&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#ff79c6&#34;&gt;include&lt;/span&gt;:&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;browser&lt;/span&gt;: firefox&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      - &lt;span style=&#34;color:#ff79c6&#34;&gt;browser&lt;/span&gt;: chrome&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;fail-fast: false&lt;/code&gt; — if Firefox fails, Chrome continues. Essential: the point of the run is seeing &lt;strong&gt;both&lt;/strong&gt; browsers, even when one fails.&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
